@astralform/js 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/utils.ts","../src/rate-limit.ts","../src/streaming.ts","../src/client.ts","../src/storage.ts","../src/tools.ts","../src/protocol-registry.ts","../src/translate.ts","../src/session.ts","../src/types.ts","../src/stream-manager.ts","../src/replay.ts","../src/embedded-resource.ts"],"sourcesContent":["export interface RateLimitErrorDetails {\n retryAfterSec?: number;\n resetAt?: number;\n scope?: string;\n policyId?: string;\n limit?: number;\n remaining?: number;\n requestId?: string;\n}\n\nexport class AstralformError extends Error {\n constructor(\n message: string,\n public code: string,\n ) {\n super(message);\n this.name = \"AstralformError\";\n }\n}\n\nexport class AuthenticationError extends AstralformError {\n constructor(message = \"Invalid or missing API key\") {\n super(message, \"authentication_error\");\n this.name = \"AuthenticationError\";\n }\n}\n\nexport class RateLimitError extends AstralformError {\n declare readonly retryAfterSec?: number;\n declare readonly resetAt?: number;\n declare readonly scope?: string;\n declare readonly policyId?: string;\n declare readonly limit?: number;\n declare readonly remaining?: number;\n declare readonly requestId?: string;\n\n constructor(\n message = \"Rate limit exceeded\",\n details: RateLimitErrorDetails = {},\n ) {\n super(message, \"rate_limit_error\");\n this.name = \"RateLimitError\";\n Object.assign(this, details);\n }\n}\n\nexport class LLMNotConfiguredError extends AstralformError {\n constructor(message = \"LLM provider not configured for this project\") {\n super(message, \"llm_not_configured\");\n this.name = \"LLMNotConfiguredError\";\n }\n}\n\nexport class ServerError extends AstralformError {\n constructor(message = \"Internal server error\") {\n super(message, \"server_error\");\n this.name = \"ServerError\";\n }\n}\n\nexport class ConnectionError extends AstralformError {\n constructor(message = \"Failed to connect to server\") {\n super(message, \"connection_error\");\n this.name = \"ConnectionError\";\n }\n}\n\nexport class StreamAbortedError extends AstralformError {\n constructor(message = \"Stream was aborted\") {\n super(message, \"stream_aborted\");\n this.name = \"StreamAbortedError\";\n }\n}\n","export function sanitizeErrorText(text: string): string {\n return text.slice(0, 500).replace(/Bearer\\s+\\S+/gi, \"Bearer [REDACTED]\");\n}\n\nexport function generateId(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n","import { RateLimitError, type RateLimitErrorDetails } from \"./errors.js\";\nimport { sanitizeErrorText } from \"./utils.js\";\n\nconst DEFAULT_MESSAGE = \"Rate limit exceeded\";\n\nfunction parseNumber(value: unknown): number | undefined {\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return value;\n }\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (!trimmed) {\n return undefined;\n }\n const parsed = Number(trimmed);\n if (Number.isFinite(parsed)) {\n return parsed;\n }\n }\n return undefined;\n}\n\nfunction parseString(value: unknown): string | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed ? trimmed : undefined;\n}\n\nfunction parseJsonObject(rawText: string): Record<string, unknown> | undefined {\n if (!rawText) {\n return undefined;\n }\n try {\n const parsed: unknown = JSON.parse(rawText);\n if (parsed && typeof parsed === \"object\") {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // Ignore parse errors and fall back to text-only handling.\n }\n return undefined;\n}\n\nfunction parseRetryAfterHeader(value: string | null): number | undefined {\n if (!value) {\n return undefined;\n }\n\n const numeric = parseNumber(value);\n if (numeric !== undefined) {\n return Math.max(0, Math.ceil(numeric));\n }\n\n const asDate = Date.parse(value);\n if (Number.isFinite(asDate)) {\n const diffMs = asDate - Date.now();\n return Math.max(0, Math.ceil(diffMs / 1000));\n }\n\n return undefined;\n}\n\nfunction parseResetTimestamp(value: unknown): number | undefined {\n const numeric = parseNumber(value);\n if (numeric !== undefined) {\n if (numeric > 1_000_000_000_000) {\n return Math.floor(numeric);\n }\n return Math.floor(numeric * 1000);\n }\n\n const asString = parseString(value);\n if (!asString) {\n return undefined;\n }\n\n const asDate = Date.parse(asString);\n if (Number.isFinite(asDate)) {\n return asDate;\n }\n return undefined;\n}\n\nfunction pickFirst<T>(\n payload: Record<string, unknown>,\n keys: string[],\n parser: (value: unknown) => T | undefined,\n): T | undefined {\n for (const key of keys) {\n const parsed = parser(payload[key]);\n if (parsed !== undefined) {\n return parsed;\n }\n }\n return undefined;\n}\n\nfunction buildRateLimitDetails(\n payload: Record<string, unknown>,\n headers?: Headers,\n): RateLimitErrorDetails {\n const headerRetryAfter = headers\n ? parseRetryAfterHeader(headers.get(\"retry-after\"))\n : undefined;\n const bodyRetryAfter = pickFirst(\n payload,\n [\"retry_after\", \"retryAfter\", \"retry_after_sec\", \"retryAfterSec\"],\n parseNumber,\n );\n\n const retryAfterSec = bodyRetryAfter ?? headerRetryAfter;\n\n const headerReset = headers\n ? parseResetTimestamp(\n headers.get(\"x-ratelimit-reset\") ?? headers.get(\"x-ratelimit-reset-at\"),\n )\n : undefined;\n const bodyReset = parseResetTimestamp(\n payload.reset_at ?? payload.resetAt ?? payload.reset,\n );\n\n const resetAt =\n bodyReset ??\n headerReset ??\n (retryAfterSec !== undefined\n ? Date.now() + retryAfterSec * 1000\n : undefined);\n\n const limit =\n pickFirst(payload, [\"limit\", \"rate_limit\", \"max\"], parseNumber) ??\n (headers ? parseNumber(headers.get(\"x-ratelimit-limit\")) : undefined);\n\n const remaining =\n pickFirst(payload, [\"remaining\", \"rate_limit_remaining\"], parseNumber) ??\n (headers ? parseNumber(headers.get(\"x-ratelimit-remaining\")) : undefined);\n\n const scope =\n pickFirst(payload, [\"scope\", \"limit_scope\"], parseString) ??\n (headers ? parseString(headers.get(\"x-ratelimit-scope\")) : undefined);\n\n const policyId =\n pickFirst(payload, [\"policy_id\", \"policyId\", \"policy\"], parseString) ??\n (headers\n ? parseString(\n headers.get(\"x-ratelimit-policy\") ??\n headers.get(\"x-ratelimit-policy-id\"),\n )\n : undefined);\n\n const requestId =\n pickFirst(payload, [\"request_id\", \"requestId\"], parseString) ??\n (headers\n ? parseString(\n headers.get(\"x-request-id\") ?? headers.get(\"x-correlation-id\"),\n )\n : undefined);\n\n return {\n retryAfterSec,\n resetAt,\n scope,\n policyId,\n limit,\n remaining,\n requestId,\n };\n}\n\nexport function createRateLimitErrorFromPayload(\n payload: Record<string, unknown>,\n fallbackMessage = DEFAULT_MESSAGE,\n): RateLimitError {\n const message = pickFirst(\n payload,\n [\"message\", \"error_description\"],\n parseString,\n );\n return new RateLimitError(\n message ?? fallbackMessage,\n buildRateLimitDetails(payload),\n );\n}\n\nexport function createRateLimitErrorFromHttp(\n response: Response,\n rawText: string,\n): RateLimitError {\n const payload = parseJsonObject(rawText) ?? {};\n const details = buildRateLimitDetails(payload, response.headers);\n\n const sanitizedText = sanitizeErrorText(rawText);\n const message =\n pickFirst(payload, [\"message\", \"error_description\"], parseString) ??\n (sanitizedText || DEFAULT_MESSAGE);\n\n return new RateLimitError(message, details);\n}\n","import {\n AuthenticationError,\n ConnectionError,\n ServerError,\n StreamAbortedError,\n} from \"./errors.js\";\nimport { createRateLimitErrorFromHttp } from \"./rate-limit.js\";\nimport type { ChatStreamEvent, StreamJobSSEOptions } from \"./types.js\";\nimport { sanitizeErrorText } from \"./utils.js\";\n\n/**\n * GET-based SSE stream for job events.\n */\nexport async function* streamJobSSE(\n options: StreamJobSSEOptions,\n): AsyncGenerator<ChatStreamEvent> {\n const { url, headers, signal, fetchFn } = options;\n\n let response: Response;\n try {\n response = await fetchFn(url, {\n method: \"GET\",\n headers,\n signal,\n });\n } catch (err) {\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw new StreamAbortedError();\n }\n throw new ConnectionError(\n err instanceof Error ? err.message : \"Failed to connect\",\n );\n }\n\n if (!response.ok) {\n const rawText = await response.text().catch(() => \"\");\n const text = rawText ? sanitizeErrorText(rawText) : \"\";\n switch (response.status) {\n case 401:\n throw new AuthenticationError();\n case 429:\n throw createRateLimitErrorFromHttp(response, rawText);\n default:\n throw new ServerError(text || `HTTP ${response.status}`);\n }\n }\n\n if (!response.body) {\n throw new ConnectionError(\"Response body is null\");\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let currentEvent = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (line.startsWith(\"event: \")) {\n currentEvent = line.slice(7).trim();\n } else if (line.startsWith(\"data: \")) {\n const data = line.slice(6);\n if (data === \"[DONE]\") {\n return;\n }\n yield { event: currentEvent || \"message\", data };\n }\n if (line === \"\") {\n currentEvent = \"\";\n }\n }\n }\n } catch (err) {\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw new StreamAbortedError();\n }\n throw err;\n } finally {\n reader.releaseLock();\n }\n}\n","import { AuthenticationError, ConnectionError, ServerError } from \"./errors.js\";\nimport { createRateLimitErrorFromHttp } from \"./rate-limit.js\";\nimport { streamJobSSE } from \"./streaming.js\";\nimport { sanitizeErrorText } from \"./utils.js\";\nimport type {\n ActiveJob,\n AgentInfo,\n AstralformApiKeyConfig,\n AstralformConfig,\n ChatStreamEvent,\n ChatStreamRequest,\n ConversationAsset,\n ConversationEvent,\n Conversation,\n FeedbackRequest,\n FeedbackResponse,\n JobCreateResponse,\n JobStatus,\n JobSummary,\n Message,\n ProjectStatus,\n ProjectSummary,\n SkillInfo,\n TeamSummary,\n ToolApprovalRequest,\n ToolResultRequest,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.astralform.ai\";\n\nfunction validateBaseURL(url: string): string {\n const cleaned = url.replace(/\\/+$/, \"\");\n try {\n const parsed = new URL(cleaned);\n if (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") {\n throw new Error(\n `Invalid baseURL protocol \"${parsed.protocol}\" - only http: and https: are allowed`,\n );\n }\n return parsed.origin + parsed.pathname.replace(/\\/+$/, \"\");\n } catch (err) {\n if (err instanceof Error && err.message.includes(\"Invalid baseURL\")) {\n throw err;\n }\n throw new Error(`Invalid baseURL: \"${cleaned}\" is not a valid URL`);\n }\n}\n\nfunction isApiKeyConfig(\n config: AstralformConfig,\n): config is AstralformApiKeyConfig {\n return \"apiKey\" in config;\n}\n\n/** Discriminates between the two auth modes the client supports. */\ntype AuthMode =\n | { kind: \"api_key\"; apiKey: string; userId: string }\n | {\n kind: \"user_token\";\n accessToken: string;\n /** Null until the user picks a project; account-scoped calls still work. */\n projectId: string | null;\n /** Optional end-user override. When present, sent as X-End-User-ID. */\n endUserId: string | null;\n };\n\nexport class AstralformClient {\n private readonly baseURL: string;\n private readonly fetchFn: typeof globalThis.fetch;\n /**\n * Auth state is mutable so callers can rotate access tokens or switch\n * project context without re-instantiating the client. API-key mode is\n * effectively immutable in practice but uses the same shape for uniformity.\n */\n private auth: AuthMode;\n\n constructor(config: AstralformConfig) {\n if (isApiKeyConfig(config)) {\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new Error(\"apiKey is required and must be a non-empty string\");\n }\n if (!config.userId || typeof config.userId !== \"string\") {\n throw new Error(\"userId is required in API-key mode\");\n }\n this.auth = {\n kind: \"api_key\",\n apiKey: config.apiKey,\n userId: config.userId,\n };\n } else {\n if (!config.accessToken || typeof config.accessToken !== \"string\") {\n throw new Error(\n \"accessToken is required and must be a non-empty string in user-token mode\",\n );\n }\n // projectId is optional — a pre-pick client (right after login) can\n // still hit account-scoped routes like listTeams(). Project-scoped\n // routes will 4xx until one is set via updateProjectId().\n const projectId =\n typeof config.projectId === \"string\" && config.projectId.length > 0\n ? config.projectId\n : null;\n this.auth = {\n kind: \"user_token\",\n accessToken: config.accessToken,\n projectId,\n endUserId:\n typeof config.endUserId === \"string\" && config.endUserId.length > 0\n ? config.endUserId\n : null,\n };\n }\n\n this.baseURL = validateBaseURL(config.baseURL ?? DEFAULT_BASE_URL);\n this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n /**\n * Replace the current OIDC access token without reconstructing the client.\n * Use after refreshing via the host's token manager (e.g., Supabase JS SDK).\n * Throws if the client was created in API-key mode.\n */\n updateAccessToken(accessToken: string): void {\n if (this.auth.kind !== \"user_token\") {\n throw new Error(\"updateAccessToken is only valid in user-token mode\");\n }\n if (!accessToken || typeof accessToken !== \"string\") {\n throw new Error(\"accessToken must be a non-empty string\");\n }\n this.auth = { ...this.auth, accessToken };\n }\n\n /**\n * Swap the active project for a user-token client. The backend verifies the\n * current developer has access to the new project; a 403 comes back if not.\n */\n updateProjectId(projectId: string): void {\n if (this.auth.kind !== \"user_token\") {\n throw new Error(\"updateProjectId is only valid in user-token mode\");\n }\n if (!projectId || typeof projectId !== \"string\") {\n throw new Error(\"projectId must be a non-empty string\");\n }\n this.auth = { ...this.auth, projectId };\n }\n\n /**\n * Set (or clear) the end-user override for user-token mode.\n *\n * Pass `null` or an empty string to clear — subsequent requests go\n * back to scoping against the developer's own identity. Throws if\n * called in API-key mode, where end-user context already travels via\n * the constructor's `userId` field.\n */\n updateEndUserId(endUserId: string | null): void {\n if (this.auth.kind !== \"user_token\") {\n throw new Error(\"updateEndUserId is only valid in user-token mode\");\n }\n const normalized =\n typeof endUserId === \"string\" && endUserId.length > 0 ? endUserId : null;\n this.auth = { ...this.auth, endUserId: normalized };\n }\n\n /** Current end-user override in user-token mode, or `null` if unset. */\n get endUserId(): string | null {\n return this.auth.kind === \"user_token\" ? this.auth.endUserId : null;\n }\n\n /**\n * Active project for user-token mode, or `null` if pre-pick (client\n * was constructed without one). For API-key mode the project is baked\n * into the key, so this getter returns `null` there too — use\n * `authMode` to disambiguate.\n */\n get projectId(): string | null {\n return this.auth.kind === \"user_token\" ? this.auth.projectId : null;\n }\n\n /** Which auth mode this client was constructed with. */\n get authMode(): \"api_key\" | \"user_token\" {\n return this.auth.kind;\n }\n\n /**\n * Authorization + identity headers for the current auth mode, without\n * `Content-Type`. Suitable for JSON requests (paired with the JSON header\n * in the `headers` getter) and for multipart uploads where the browser\n * must set its own `Content-Type` boundary.\n */\n private get authHeaders(): Record<string, string> {\n if (this.auth.kind === \"api_key\") {\n return {\n Authorization: `Bearer ${this.auth.apiKey}`,\n \"X-End-User-ID\": this.auth.userId,\n };\n }\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.auth.accessToken}`,\n };\n if (this.auth.projectId) {\n headers[\"X-Project-ID\"] = this.auth.projectId;\n }\n if (this.auth.endUserId) {\n headers[\"X-End-User-ID\"] = this.auth.endUserId;\n }\n return headers;\n }\n\n private get headers(): Record<string, string> {\n return {\n ...this.authHeaders,\n \"Content-Type\": \"application/json\",\n };\n }\n\n private async request(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<Response> {\n const response = await this.fetchFn(`${this.baseURL}${path}`, {\n method,\n headers: this.headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n }).catch((err) => {\n throw new ConnectionError(\n err instanceof Error ? err.message : \"Failed to connect\",\n );\n });\n await this.handleError(response);\n return response;\n }\n\n async get<T>(path: string): Promise<T> {\n const response = await this.request(\"GET\", path);\n return response.json() as Promise<T>;\n }\n\n async post<T>(path: string, body: unknown): Promise<T> {\n const response = await this.request(\"POST\", path, body);\n return response.json() as Promise<T>;\n }\n\n private async del(path: string): Promise<void> {\n await this.request(\"DELETE\", path);\n }\n\n private async handleError(response: Response): Promise<void> {\n if (response.ok) return;\n const text = await response.text().catch(() => \"\");\n switch (response.status) {\n case 401:\n throw new AuthenticationError();\n case 429:\n throw createRateLimitErrorFromHttp(response, text);\n default: {\n const safeText = text ? sanitizeErrorText(text) : \"\";\n throw new ServerError(safeText || `HTTP ${response.status}`);\n }\n }\n }\n\n // --- REST Methods ---\n\n async getHealth(): Promise<{\n status: string;\n version: string;\n ollama_connected: boolean;\n }> {\n return this.get(\"/v1/health\");\n }\n\n async getProjectStatus(): Promise<ProjectStatus> {\n const raw = await this.get<{\n is_ready: boolean;\n llm_configured: boolean;\n llm_provider?: string;\n llm_model?: string;\n message: string;\n ui_components?: {\n enabled?: boolean;\n protocol?: string | null;\n mime_type?: string | null;\n };\n }>(\"/v1/project/status\");\n const ui = raw.ui_components ?? {};\n return {\n isReady: raw.is_ready,\n llmConfigured: raw.llm_configured,\n llmProvider: raw.llm_provider,\n llmModel: raw.llm_model,\n message: raw.message,\n uiComponents: {\n enabled: Boolean(ui.enabled),\n protocol: ui.protocol ?? null,\n mimeType: ui.mime_type ?? null,\n },\n };\n }\n\n async getConversations(limit = 50, offset = 0): Promise<Conversation[]> {\n const safeLimit = Math.max(1, Math.min(200, Math.floor(Number(limit))));\n const safeOffset = Math.max(0, Math.floor(Number(offset)));\n const raw = await this.get<\n {\n id: string;\n title: string;\n message_count: number;\n created_at: string;\n updated_at: string;\n }[]\n >(`/v1/conversations?limit=${safeLimit}&offset=${safeOffset}`);\n return raw.map((c) => ({\n id: c.id,\n title: c.title,\n messageCount: c.message_count,\n createdAt: c.created_at,\n updatedAt: c.updated_at,\n }));\n }\n\n async getMessages(conversationId: string): Promise<Message[]> {\n const raw = await this.get<\n {\n id: string;\n conversation_id: string;\n role: \"user\" | \"assistant\" | \"system\";\n content: string;\n parent_id?: string;\n created_at: string;\n }[]\n >(`/v1/conversations/${encodeURIComponent(conversationId)}/messages`);\n return raw.map((m) => ({\n id: m.id,\n conversationId: m.conversation_id,\n role: m.role,\n content: m.content,\n parentId: m.parent_id,\n status: \"complete\" as const,\n createdAt: m.created_at,\n }));\n }\n\n async deleteConversation(id: string): Promise<void> {\n await this.del(`/v1/conversations/${encodeURIComponent(id)}`);\n }\n\n async getAgents(): Promise<AgentInfo[]> {\n const raw = await this.get<\n {\n name: string;\n display_name: string;\n description: string;\n is_orchestrator: boolean;\n is_enabled: boolean;\n avatar_url?: string;\n }[]\n >(\"/v1/agents\");\n return raw.map((a) => ({\n name: a.name,\n displayName: a.display_name,\n description: a.description,\n isOrchestrator: a.is_orchestrator,\n isEnabled: a.is_enabled,\n avatarUrl: a.avatar_url,\n }));\n }\n\n async getSkills(): Promise<SkillInfo[]> {\n const raw = await this.get<\n {\n name: string;\n display_name: string;\n description: string;\n is_enabled: boolean;\n }[]\n >(\"/v1/skills\");\n return raw.map((s) => ({\n name: s.name,\n displayName: s.display_name,\n description: s.description,\n isEnabled: s.is_enabled,\n }));\n }\n\n async getConversationEvents(\n conversationId: string,\n jobId?: string,\n ): Promise<ConversationEvent[]> {\n let url = `/v1/conversations/${encodeURIComponent(conversationId)}/events`;\n if (jobId) url += `?job_id=${encodeURIComponent(jobId)}`;\n return this.get(url);\n }\n\n async submitToolResult(request: ToolResultRequest): Promise<void> {\n await this.post(\"/v1/tool-result\", request);\n }\n\n async submitToolApproval(request: ToolApprovalRequest): Promise<void> {\n await this.post(\"/v1/tool-approval\", request);\n }\n\n // --- Conversation Assets ---\n\n private mapAsset(raw: Record<string, unknown>): ConversationAsset {\n return {\n id: raw.id as string,\n kind: raw.kind as \"upload\" | \"output\",\n originalName: raw.original_name as string,\n mediaType: raw.media_type as string,\n sizeBytes: raw.size_bytes as number,\n workspacePath: raw.workspace_path as string | undefined,\n sourceMessageId: raw.source_message_id as string | undefined,\n agentName: raw.agent_name as string | undefined,\n createdAt: raw.created_at as string,\n };\n }\n\n async uploadFile(\n conversationId: string,\n file: Blob,\n filename?: string,\n ): Promise<ConversationAsset> {\n const formData = new FormData();\n formData.append(\"file\", file, filename);\n\n const response = await this.fetchFn(\n `${this.baseURL}/v1/conversations/${encodeURIComponent(conversationId)}/uploads`,\n {\n method: \"POST\",\n headers: this.authHeaders,\n body: formData,\n },\n ).catch((err) => {\n throw new ConnectionError(\n err instanceof Error ? err.message : \"Failed to connect\",\n );\n });\n await this.handleError(response);\n const raw = await response.json();\n return this.mapAsset(raw as Record<string, unknown>);\n }\n\n async listUploads(conversationId: string): Promise<ConversationAsset[]> {\n const raw = await this.get<Record<string, unknown>[]>(\n `/v1/conversations/${encodeURIComponent(conversationId)}/uploads`,\n );\n return raw.map((r) => this.mapAsset(r));\n }\n\n async listOutputs(conversationId: string): Promise<ConversationAsset[]> {\n const raw = await this.get<Record<string, unknown>[]>(\n `/v1/conversations/${encodeURIComponent(conversationId)}/outputs`,\n );\n return raw.map((r) => this.mapAsset(r));\n }\n\n // --- Account-scoped discovery (user-token mode) ---\n //\n // Lets a signed-in user pick which team/project they want to act on.\n // Backend gates these on OIDC user context (no X-Project-ID required) —\n // sending them in API-key mode yields 401.\n\n async listTeams(): Promise<TeamSummary[]> {\n const raw = await this.get<\n Array<{\n id: string;\n name: string;\n slug: string;\n is_default: boolean;\n role: string;\n }>\n >(\"/v1/teams\");\n return raw.map((t) => ({\n id: t.id,\n name: t.name,\n slug: t.slug,\n isDefault: t.is_default,\n role: t.role,\n }));\n }\n\n async listProjects(teamId: string): Promise<ProjectSummary[]> {\n const raw = await this.get<\n Array<{\n id: string;\n name: string;\n team_id: string;\n created_at: string;\n updated_at: string;\n }>\n >(`/v1/teams/${encodeURIComponent(teamId)}/projects`);\n return raw.map((p) => ({\n id: p.id,\n name: p.name,\n teamId: p.team_id,\n createdAt: p.created_at,\n updatedAt: p.updated_at,\n }));\n }\n\n // --- Jobs API ---\n\n async createJob(request: ChatStreamRequest): Promise<JobCreateResponse> {\n return this.post<JobCreateResponse>(\"/v1/jobs\", request);\n }\n\n async *streamJobEvents(\n jobId: string,\n afterSeq = -1,\n signal?: AbortSignal,\n ): AsyncGenerator<ChatStreamEvent> {\n const url = `${this.baseURL}/v1/jobs/${encodeURIComponent(jobId)}/events?after=${afterSeq}`;\n yield* streamJobSSE({\n url,\n headers: this.headers,\n signal,\n fetchFn: this.fetchFn,\n });\n }\n\n async cancelJob(jobId: string): Promise<void> {\n await this.post(`/v1/jobs/${encodeURIComponent(jobId)}/cancel`, {});\n }\n\n async getJob(jobId: string): Promise<JobStatus> {\n const raw = await this.get<{\n job_id: string;\n status: string;\n created_at?: string | null;\n started_at?: string | null;\n completed_at?: string | null;\n error_message?: string | null;\n input_tokens?: number;\n output_tokens?: number;\n }>(`/v1/jobs/${encodeURIComponent(jobId)}`);\n return {\n jobId: raw.job_id,\n status: raw.status,\n createdAt: raw.created_at ?? null,\n startedAt: raw.started_at ?? null,\n completedAt: raw.completed_at ?? null,\n errorMessage: raw.error_message ?? null,\n inputTokens: raw.input_tokens ?? 0,\n outputTokens: raw.output_tokens ?? 0,\n };\n }\n\n async submitFeedback(\n jobId: string,\n request: FeedbackRequest,\n ): Promise<FeedbackResponse> {\n const body: { rating: 1 | -1; comment?: string } = {\n rating: request.rating,\n };\n if (request.comment != null) body.comment = request.comment;\n const raw = await this.post<{\n id: string;\n job_id: string;\n rating: number;\n comment: string | null;\n created_at: string;\n }>(`/v1/jobs/${encodeURIComponent(jobId)}/feedback`, body);\n return {\n id: raw.id,\n jobId: raw.job_id,\n rating: raw.rating,\n comment: raw.comment,\n createdAt: raw.created_at,\n };\n }\n\n async getActiveJob(conversationId: string): Promise<ActiveJob> {\n const raw = await this.get<{\n job_id: string | null;\n status: string;\n }>(`/v1/conversations/${encodeURIComponent(conversationId)}/active-job`);\n return {\n jobId: raw.job_id ?? null,\n status: raw.status,\n };\n }\n\n async listJobs(conversationId: string): Promise<JobSummary[]> {\n const raw = await this.get<\n {\n job_id: string;\n status: string;\n replaces_job_id?: string | null;\n response_content?: Record<string, unknown> | null;\n metrics?: Record<string, unknown> | null;\n created_at?: string | null;\n }[]\n >(`/v1/conversations/${encodeURIComponent(conversationId)}/jobs`);\n return raw.map((j) => ({\n jobId: j.job_id,\n status: j.status,\n replacesJobId: j.replaces_job_id ?? null,\n responseContent: j.response_content ?? null,\n metrics: j.metrics ?? null,\n createdAt: j.created_at ?? null,\n }));\n }\n}\n","import type { Conversation, Message } from \"./types.js\";\n\nexport interface ChatStorage {\n fetchConversations(): Promise<Conversation[]>;\n fetchConversation(id: string): Promise<Conversation | null>;\n createConversation(id: string, title: string): Promise<Conversation>;\n updateConversationTitle(id: string, title: string): Promise<void>;\n deleteConversation(id: string): Promise<void>;\n fetchMessages(conversationId: string): Promise<Message[]>;\n addMessage(message: Message, conversationId: string): Promise<void>;\n updateMessageStatus(id: string, status: Message[\"status\"]): Promise<void>;\n deleteMessage(id: string): Promise<void>;\n}\n\nexport class InMemoryStorage implements ChatStorage {\n private conversations = new Map<string, Conversation>();\n private messages = new Map<string, Message[]>();\n\n async fetchConversations(): Promise<Conversation[]> {\n return Array.from(this.conversations.values()).sort(\n (a, b) =>\n new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(),\n );\n }\n\n async fetchConversation(id: string): Promise<Conversation | null> {\n return this.conversations.get(id) ?? null;\n }\n\n async createConversation(id: string, title: string): Promise<Conversation> {\n const now = new Date().toISOString();\n const conversation: Conversation = {\n id,\n title,\n messageCount: 0,\n createdAt: now,\n updatedAt: now,\n };\n this.conversations.set(id, conversation);\n this.messages.set(id, []);\n return conversation;\n }\n\n async updateConversationTitle(id: string, title: string): Promise<void> {\n const conv = this.conversations.get(id);\n if (conv) {\n conv.title = title;\n conv.updatedAt = new Date().toISOString();\n }\n }\n\n async deleteConversation(id: string): Promise<void> {\n this.conversations.delete(id);\n this.messages.delete(id);\n }\n\n async fetchMessages(conversationId: string): Promise<Message[]> {\n return this.messages.get(conversationId) ?? [];\n }\n\n async addMessage(message: Message, conversationId: string): Promise<void> {\n const msgs = this.messages.get(conversationId) ?? [];\n msgs.push(message);\n this.messages.set(conversationId, msgs);\n\n const conv = this.conversations.get(conversationId);\n if (conv) {\n conv.messageCount = msgs.length;\n conv.updatedAt = new Date().toISOString();\n }\n }\n\n async updateMessageStatus(\n id: string,\n status: Message[\"status\"],\n ): Promise<void> {\n for (const msgs of this.messages.values()) {\n const msg = msgs.find((m) => m.id === id);\n if (msg) {\n msg.status = status;\n return;\n }\n }\n }\n\n async deleteMessage(id: string): Promise<void> {\n for (const [convId, msgs] of this.messages.entries()) {\n const idx = msgs.findIndex((m) => m.id === id);\n if (idx !== -1) {\n msgs.splice(idx, 1);\n const conv = this.conversations.get(convId);\n if (conv) {\n conv.messageCount = msgs.length;\n }\n return;\n }\n }\n }\n}\n","import type { ToolCallRequest, ToolDefinition, ToolResult } from \"./types.js\";\n\nexport type ToolHandler = (args: Record<string, unknown>) => Promise<string>;\n\ninterface RegisteredTool {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n handler: ToolHandler;\n}\n\n/** Validates tool name: alphanumeric, hyphens, underscores, dots only */\nconst TOOL_NAME_PATTERN = /^[a-zA-Z0-9_.\\-]+$/;\n\n/** Strips prototype-polluting keys from an object */\nfunction sanitizeArgs(args: Record<string, unknown>): Record<string, unknown> {\n const clean: Record<string, unknown> = Object.create(null);\n for (const key of Object.keys(args)) {\n if (key === \"__proto__\" || key === \"constructor\" || key === \"prototype\") {\n continue;\n }\n clean[key] = args[key];\n }\n return clean;\n}\n\nexport class ToolRegistry {\n private tools = new Map<string, RegisteredTool>();\n\n registerTool(\n name: string,\n description: string,\n inputSchema: Record<string, unknown>,\n handler: ToolHandler,\n ): void {\n if (!name || !TOOL_NAME_PATTERN.test(name)) {\n throw new Error(\n `Invalid tool name \"${name}\" - must match ${TOOL_NAME_PATTERN}`,\n );\n }\n if (name.length > 256) {\n throw new Error(\"Tool name must be 256 characters or fewer\");\n }\n this.tools.set(name, { name, description, inputSchema, handler });\n }\n\n unregisterTool(name: string): boolean {\n return this.tools.delete(name);\n }\n\n hasTool(name: string): boolean {\n return this.tools.has(name);\n }\n\n async executeTool(request: ToolCallRequest): Promise<ToolResult> {\n const tool = this.tools.get(request.toolName);\n if (!tool) {\n return {\n call_id: request.callId,\n tool_name: request.toolName,\n result: `Tool \"${request.toolName}\" not found`,\n is_error: true,\n };\n }\n\n try {\n const result = await tool.handler(sanitizeArgs(request.arguments));\n return {\n call_id: request.callId,\n tool_name: request.toolName,\n result,\n is_error: false,\n };\n } catch (err) {\n return {\n call_id: request.callId,\n tool_name: request.toolName,\n result: err instanceof Error ? err.message : String(err),\n is_error: true,\n };\n }\n }\n\n getManifest(): ToolDefinition[] {\n return Array.from(this.tools.values()).map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.inputSchema,\n }));\n }\n\n getToolNames(): string[] {\n return Array.from(this.tools.keys());\n }\n\n clear(): void {\n this.tools.clear();\n }\n}\n","// =============================================================================\n// Protocol adapter registry — pluggable UI protocol layer\n// =============================================================================\n//\n// The SDK is framework-agnostic: it never renders. But when the backend\n// emits an MCP-style embedded resource (A2UI today, other protocols\n// tomorrow) the consumer needs to hand the payload to a renderer. This\n// file defines the contract between those two sides:\n//\n// • `ProtocolAdapter` — an opaque, framework-specific handle that\n// claims a MIME type. The SDK stores it; the consumer narrows the\n// type when reading it back out.\n//\n// • `ProtocolRegistry` — a MIME-keyed map of adapters. One lives on\n// each `ChatSession` so its lifecycle matches the session: clearing\n// on disconnect, swapping on reconnect with a different project.\n//\n// The consumer decides _whether_ to register an adapter — typically by\n// consulting `session.projectStatus.uiComponents` after `connect()`.\n// The SDK never auto-registers anything; it just stores what it's told.\n// =============================================================================\n\n/**\n * Minimal adapter contract. Frontends extend this with a `render()`\n * method (or equivalent) returning their framework's view type.\n */\nexport interface ProtocolAdapter {\n /** IANA-style MIME type this adapter handles (e.g. ``application/json+a2ui``). */\n readonly mimeType: string;\n}\n\n/**\n * MIME-keyed adapter map, generic on the adapter subtype so consumers\n * can register richer shapes without casting on every read.\n */\nexport class ProtocolRegistry<T extends ProtocolAdapter = ProtocolAdapter> {\n private adapters = new Map<string, T>();\n\n /** Register or replace the adapter for a MIME type. */\n register(adapter: T): void {\n this.adapters.set(adapter.mimeType, adapter);\n }\n\n /** Remove the adapter for a MIME type. No-op if not registered. */\n unregister(mimeType: string): void {\n this.adapters.delete(mimeType);\n }\n\n /** Returns the adapter for a MIME type, or ``null`` if none is registered. */\n get(mimeType: string): T | null {\n return this.adapters.get(mimeType) ?? null;\n }\n\n has(mimeType: string): boolean {\n return this.adapters.has(mimeType);\n }\n\n /** Drop every adapter. Called when a session disconnects. */\n clear(): void {\n this.adapters.clear();\n }\n\n listMimeTypes(): string[] {\n return Array.from(this.adapters.keys());\n }\n}\n","// =============================================================================\n// Wire → ChatEvent translation\n//\n// Single source of truth for the pure translation from backend wire types\n// (snake_case, mirroring Pydantic) to consumer-facing ChatEvents (camelCase).\n// Shared between the live session loop and the persisted-event replay path,\n// so adding a new event type only requires updating one place.\n// =============================================================================\n\nimport type {\n BlockDeltaPayload,\n ChatEvent,\n WireBlockDeltaPayload,\n WireEvent,\n} from \"./types.js\";\nimport type { MemoryRecord, TodoItem } from \"./custom-events.js\";\n\n// --- Delta channels ---\n\n/**\n * Translate a WireBlockDeltaPayload into the consumer-facing shape. Returns\n * ``null`` for unknown channels so forward-compatible backends can add new\n * ones without breaking old clients.\n */\nexport function translateDelta(\n wire: WireBlockDeltaPayload,\n): BlockDeltaPayload | null {\n switch (wire.channel) {\n case \"text\":\n return { channel: \"text\", text: wire.text };\n case \"thinking\":\n return { channel: \"thinking\", text: wire.text };\n case \"signature\":\n return { channel: \"signature\", signature: wire.signature };\n case \"input\":\n return { channel: \"input\", partialJson: wire.partial_json };\n case \"input_arg\":\n return {\n channel: \"inputArg\",\n argName: wire.arg_name,\n text: wire.text,\n };\n case \"output\":\n return { channel: \"output\", stream: wire.stream, chunk: wire.chunk };\n case \"status\":\n return {\n channel: \"status\",\n status: wire.status,\n note: wire.note,\n };\n default:\n return null;\n }\n}\n\n// --- Custom events ---\n\nfunction translateAgentIdentity(raw: Record<string, unknown>) {\n return {\n name: (raw.name as string) ?? \"\",\n displayName: (raw.display_name as string | null) ?? null,\n avatarUrl: (raw.avatar_url as string | null) ?? null,\n description: (raw.description as string | null) ?? null,\n };\n}\n\n/**\n * Translate a wire custom event (``{type: \"custom\", name, data}``) into a\n * typed ChatEvent. Unknown names fall through to the generic ``custom``\n * passthrough so consumers can still observe future backends.\n */\nexport function translateCustomEvent(\n name: string,\n data: Record<string, unknown>,\n): ChatEvent {\n switch (name) {\n case \"user_message\":\n return {\n type: \"user_message\",\n content: (data.content as string) ?? \"\",\n createdAt: data.created_at as number | undefined,\n };\n case \"title_generated\":\n return {\n type: \"title_generated\",\n title: (data.title as string) ?? \"\",\n };\n case \"todo_update\":\n return {\n type: \"todo_update\",\n todos: (data.todos as TodoItem[]) ?? [],\n };\n case \"context_update\":\n return {\n type: \"context_update\",\n context: (data.context as Record<string, unknown>) ?? {},\n phase: (data.phase as string | null) ?? null,\n updatedAt: (data.updated_at as number | null) ?? null,\n };\n case \"subagent_start\":\n return {\n type: \"subagent_start\",\n agent: translateAgentIdentity(\n (data.agent as Record<string, unknown>) ?? {},\n ),\n taskCallId: (data.task_call_id as string | null) ?? null,\n };\n case \"subagent_stop\":\n return {\n type: \"subagent_stop\",\n agent: translateAgentIdentity(\n (data.agent as Record<string, unknown>) ?? {},\n ),\n taskCallId: (data.task_call_id as string | null) ?? null,\n };\n case \"context_warning\":\n return {\n type: \"context_warning\",\n severity: (data.severity as string) ?? \"warning\",\n utilizationPct: (data.utilization_pct as number) ?? 0,\n remainingTokens: (data.remaining_tokens as number) ?? 0,\n windowTokens: (data.window_tokens as number) ?? 0,\n inputTokens: (data.input_tokens as number) ?? 0,\n message: (data.message as string) ?? \"\",\n };\n case \"memory_recall\":\n return {\n type: \"memory_recall\",\n memories: (data.memories as MemoryRecord[] | undefined) ?? [],\n };\n case \"memory_update\":\n return {\n type: \"memory_update\",\n action: (data.action as string) ?? \"\",\n memoryId: (data.memory_id as string | null) ?? null,\n key: (data.key as string | null) ?? null,\n namespace: (data.namespace as string | null) ?? null,\n };\n case \"desktop_stream\":\n return {\n type: \"desktop_stream\",\n url: (data.url as string) ?? \"\",\n sandboxId: (data.sandbox_id as string | null) ?? null,\n };\n case \"attachment_staged\":\n return {\n type: \"attachment_staged\",\n attachmentId: (data.attachment_id as string) ?? \"\",\n filename: (data.filename as string) ?? \"\",\n contentType: (data.content_type as string | null) ?? null,\n sizeBytes: (data.size_bytes as number | null) ?? null,\n };\n case \"workspace_ready\":\n return {\n type: \"workspace_ready\",\n sandboxId: (data.sandbox_id as string) ?? \"\",\n workspacePath: (data.workspace_path as string | null) ?? null,\n };\n case \"asset_created\":\n return {\n type: \"asset_created\",\n assetId: (data.asset_id as string) ?? \"\",\n filename: (data.filename as string) ?? \"\",\n url: (data.url as string | null) ?? null,\n contentType: (data.content_type as string | null) ?? null,\n };\n case \"tool_approval_requested\":\n return {\n type: \"tool_approval_requested\",\n toolName: (data.tool_name as string) ?? \"\",\n callId: (data.call_id as string) ?? \"\",\n arguments: (data.arguments as Record<string, unknown>) ?? {},\n riskLevel: (data.risk_level as string | null) ?? null,\n reason: (data.reason as string | null) ?? null,\n };\n case \"tool_approval_granted\":\n return {\n type: \"tool_approval_granted\",\n toolName: (data.tool_name as string) ?? \"\",\n callId: (data.call_id as string) ?? \"\",\n };\n case \"tool_permission_denied\":\n return {\n type: \"tool_permission_denied\",\n toolName: (data.tool_name as string) ?? \"\",\n callId: (data.call_id as string) ?? \"\",\n reason: (data.reason as string | null) ?? null,\n deniedBy: (data.denied_by as string | null) ?? null,\n };\n case \"tool_harness_warning\":\n return {\n type: \"tool_harness_warning\",\n toolName: (data.tool_name as string) ?? \"\",\n callId: (data.call_id as string) ?? \"\",\n message: (data.message as string | null) ?? null,\n details: (data.details as Record<string, unknown> | null) ?? null,\n };\n case \"user_unavailable\":\n return {\n type: \"user_unavailable\",\n consecutiveTimeouts: (data.consecutive_timeouts as number) ?? 0,\n toolName: (data.tool_name as string | null) ?? null,\n };\n case \"prompt_suggestion\":\n return {\n type: \"prompt_suggestion\",\n suggestions: (data.suggestions as string[]) ?? [],\n };\n case \"state_changed\":\n return {\n type: \"state_changed\",\n state: (data.state as string) ?? \"\",\n };\n default:\n return { type: \"custom\", name, data };\n }\n}\n\n// --- Top-level wire events ---\n\n/**\n * Legacy-transport payload extractor for events that aren't wrapped in a\n * ``CustomEvent`` envelope. The backend's ``writer.emit(name, data)`` path\n * spreads the payload fields at the top level of the wire object (see\n * ``backend/src/jobs/suggestion_hook.py`` for ``prompt_suggestion``), so\n * the wire object itself *is* the ``data`` dict for ``translateCustomEvent``.\n * This helper isolates the unsafe cast to one place.\n */\nfunction legacyCustomEventData(wire: WireEvent): Record<string, unknown> {\n return wire as unknown as Record<string, unknown>;\n}\n\n/**\n * Translate a full WireEvent into its typed ChatEvent counterpart. Returns\n * ``null`` when the wire payload is malformed (e.g. unknown delta channel)\n * so the caller can skip it without crashing the stream.\n */\nexport function translateWireEvent(wire: WireEvent): ChatEvent | null {\n // Legacy transport: ``prompt_suggestion`` is emitted via the raw\n // ``writer.emit()`` path rather than wrapped in a CustomEvent envelope,\n // so its ``type`` field is the event name itself. Route it through the\n // custom-event translator to keep the mapping in one place.\n if ((wire as { type: string }).type === \"prompt_suggestion\") {\n return translateCustomEvent(\n \"prompt_suggestion\",\n legacyCustomEventData(wire),\n );\n }\n switch (wire.type) {\n case \"message_start\":\n return {\n type: \"message_start\",\n turnId: wire.turn_id,\n model: wire.model,\n agentName: wire.agent_name,\n agentDisplayName: wire.agent_display_name,\n agentAvatarUrl: wire.agent_avatar_url,\n };\n case \"block_start\":\n return {\n type: \"block_start\",\n turnId: wire.turn_id,\n path: wire.path,\n parentPath: wire.parent_path ?? null,\n kind: wire.kind,\n metadata: wire.metadata,\n };\n case \"block_delta\": {\n const delta = translateDelta(wire.delta);\n if (!delta) return null;\n return {\n type: \"block_delta\",\n turnId: wire.turn_id,\n path: wire.path,\n delta,\n };\n }\n case \"block_stop\":\n return {\n type: \"block_stop\",\n turnId: wire.turn_id,\n path: wire.path,\n status: wire.status,\n final: wire.final,\n };\n case \"message_stop\":\n return {\n type: \"message_stop\",\n turnId: wire.turn_id,\n jobId: wire.job_id,\n stopReason: wire.stop_reason,\n usage: {\n inputTokens: wire.usage.input_tokens ?? 0,\n outputTokens: wire.usage.output_tokens ?? 0,\n cachedTokens: wire.usage.cached_tokens ?? 0,\n },\n ttfbMs: wire.ttfb_ms,\n totalMs: wire.total_ms,\n stallCount: wire.stall_count,\n };\n case \"stall\":\n return {\n type: \"stall\",\n sinceLastEventMs: wire.since_last_event_ms,\n stallCount: wire.stall_count,\n };\n case \"retry\":\n return {\n type: \"retry\",\n attempt: wire.attempt,\n reason: wire.reason,\n backoffMs: wire.backoff_ms,\n strategy: wire.strategy ?? null,\n maxAttempts: wire.max_attempts ?? null,\n contextRecovery: wire.context_recovery ?? null,\n };\n case \"error\":\n return {\n type: \"error\",\n code: wire.code,\n message: wire.message,\n blockPath: wire.block_path ?? null,\n };\n case \"keepalive\":\n return {\n type: \"keepalive\",\n sinceLastEventMs: wire.since_last_event_ms,\n };\n case \"custom\":\n return translateCustomEvent(wire.name, wire.data);\n default: {\n // Exhaustive guard — a new WireEvent variant should force this switch\n // to be updated at compile time.\n const _exhaustive: never = wire;\n void _exhaustive;\n return null;\n }\n }\n}\n","import { AstralformClient } from \"./client.js\";\nimport { InMemoryStorage, type ChatStorage } from \"./storage.js\";\nimport { ToolRegistry } from \"./tools.js\";\nimport { ProtocolRegistry } from \"./protocol-registry.js\";\nimport { translateWireEvent } from \"./translate.js\";\nimport type {\n AgentInfo,\n AstralformConfig,\n ChatEvent,\n ChatStreamRequest,\n Conversation,\n Message,\n ProjectStatus,\n SendOptions,\n SkillInfo,\n ToolCallRequest,\n ToolResult,\n WireEvent,\n} from \"./types.js\";\nimport { generateId } from \"./utils.js\";\n\ntype ChatEventHandler = (event: ChatEvent) => void;\n\nfunction pathEquals(a: number[], b: number[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * ChatSession — translates the backend wire protocol into typed ChatEvents\n * for consumers. Owns HTTP + SSE plumbing, conversation state, and the\n * client-tool round-trip. Does NOT own block construction — consumers\n * build their own block state from the typed events.\n */\nexport class ChatSession {\n readonly client: AstralformClient;\n readonly toolRegistry: ToolRegistry;\n readonly storage: ChatStorage;\n /**\n * Pluggable UI protocol adapters. Consumers register a framework-\n * specific adapter (e.g. React) for each MIME type they can render,\n * typically gated on ``session.projectStatus.uiComponents.protocol``.\n * ``ToolBlock``-style consumers look up the adapter for an incoming\n * embedded resource and hand off rendering.\n */\n readonly protocols = new ProtocolRegistry();\n\n // State\n conversationId: string | null = null;\n conversations: Conversation[] = [];\n messages: Message[] = [];\n isStreaming = false;\n projectStatus: ProjectStatus | null = null;\n agents: AgentInfo[] = [];\n skills: SkillInfo[] = [];\n enabledClientTools = new Set<string>();\n modelDisplayName: string | null = null;\n\n // Minimal in-session accumulation for the assistant message record.\n // Only top-level ``text`` blocks contribute; subagent / tool output\n // is tracked by the consumer's own block store.\n private accumulatedText = \"\";\n private currentTextPath: number[] | null = null;\n\n private handlers: Set<ChatEventHandler> = new Set();\n private abortController: AbortController | null = null;\n\n constructor(config: AstralformConfig, storage?: ChatStorage) {\n this.client = new AstralformClient(config);\n this.toolRegistry = new ToolRegistry();\n this.storage = storage ?? new InMemoryStorage();\n }\n\n on(handler: ChatEventHandler): () => void {\n this.handlers.add(handler);\n return () => {\n this.handlers.delete(handler);\n };\n }\n\n private emit(event: ChatEvent): void {\n for (const handler of this.handlers) {\n try {\n handler(event);\n } catch {\n // Don't let handler errors crash the session\n }\n }\n }\n\n async connect(): Promise<void> {\n const [status, conversations, agents, skills] = await Promise.allSettled([\n this.client.getProjectStatus(),\n this.client.getConversations(),\n this.client.getAgents().catch(() => [] as AgentInfo[]),\n this.client.getSkills().catch(() => [] as SkillInfo[]),\n ]);\n\n if (status.status === \"fulfilled\") {\n this.projectStatus = status.value;\n }\n if (conversations.status === \"fulfilled\") {\n this.conversations = conversations.value;\n }\n if (agents.status === \"fulfilled\") {\n this.agents = agents.value;\n }\n if (skills.status === \"fulfilled\") {\n this.skills = skills.value;\n }\n\n this.emit({ type: \"connected\" });\n }\n\n async send(content: string, options?: SendOptions): Promise<void> {\n if (this.isStreaming) return;\n\n const conversationId =\n options?.conversationId ?? this.conversationId ?? undefined;\n\n const userMessage: Message = {\n id: generateId(),\n conversationId: conversationId ?? \"\",\n role: \"user\",\n content,\n status: \"complete\",\n createdAt: new Date().toISOString(),\n };\n if (conversationId) {\n await this.storage.addMessage(userMessage, conversationId);\n }\n this.messages.push(userMessage);\n\n const request: ChatStreamRequest = {\n message: content,\n conversation_id: conversationId,\n mcp_manifest: this.toolRegistry.getManifest(),\n enabled_mcp: Array.from(\n options?.enabledClientTools ?? this.enabledClientTools,\n ),\n upload_ids: options?.uploadIds,\n agent_name: options?.agentName,\n enable_search: options?.enableSearch,\n plan_mode: options?.planMode,\n };\n\n await this.processStream(request);\n }\n\n async resendFromCheckpoint(\n messageId: string,\n newContent: string,\n options?: { enableSearch?: boolean },\n ): Promise<void> {\n if (this.isStreaming) return;\n\n const request: ChatStreamRequest = {\n message: newContent,\n conversation_id: this.conversationId ?? undefined,\n resend_from: messageId,\n mcp_manifest: this.toolRegistry.getManifest(),\n enabled_mcp: Array.from(this.enabledClientTools),\n enable_search: options?.enableSearch,\n };\n\n await this.processStream(request);\n }\n\n private resetStreamingState(): void {\n this.accumulatedText = \"\";\n this.currentTextPath = null;\n }\n\n private async processStream(request: ChatStreamRequest): Promise<void> {\n this.isStreaming = true;\n this.resetStreamingState();\n this.abortController = new AbortController();\n\n try {\n await this.consumeJobStream(request);\n } catch (err) {\n if (!(err instanceof DOMException && err.name === \"AbortError\")) {\n this.emit({\n type: \"error\",\n code: \"connection_error\",\n message: err instanceof Error ? err.message : String(err),\n blockPath: null,\n });\n }\n } finally {\n this.isStreaming = false;\n this.abortController = null;\n }\n }\n\n /** Last received sequence number for resumable reconnection */\n private lastSeq = -1;\n\n /** Current job ID for cancellation */\n currentJobId: string | null = null;\n\n private async consumeJobStream(request: ChatStreamRequest): Promise<void> {\n const job = await this.client.createJob(request);\n this.currentJobId = job.job_id;\n\n const conversationId = job.conversation_id;\n if (!this.conversationId) {\n this.conversationId = conversationId;\n }\n // Ensure the conversation exists in both the local array and\n // ChatStorage so title_generated, completeStream, and fallback\n // reload all work for backend-created conversations.\n if (!this.conversations.some((c) => c.id === conversationId)) {\n const now = new Date().toISOString();\n const conv = {\n id: conversationId,\n title: \"\",\n messageCount: 0,\n createdAt: now,\n updatedAt: now,\n };\n this.conversations.unshift(conv);\n await this.storage.createConversation(conversationId, \"\").catch(() => {});\n }\n // Backfill the just-sent user message if send() ran before we knew the\n // conversation id (first turn of an auto-created conversation).\n const lastMsg = this.messages[this.messages.length - 1];\n if (lastMsg?.role === \"user\" && !lastMsg.conversationId) {\n lastMsg.conversationId = conversationId;\n await this.storage.addMessage(lastMsg, conversationId).catch(() => {});\n }\n const messageId = job.message_id;\n this.lastSeq = -1;\n\n const stream = this.client.streamJobEvents(\n job.job_id,\n this.lastSeq,\n this.abortController?.signal,\n );\n\n await this.consumeEventStream(\n stream,\n conversationId,\n messageId,\n true, // executeClientTools\n );\n }\n\n /**\n * Shared event consumption loop. Parses each wire event, updates\n * minimal session state, and emits typed ChatEvents to consumers.\n */\n private async consumeEventStream(\n stream: AsyncGenerator<{ data: string }>,\n conversationId: string,\n messageId: string,\n executeClientTools: boolean,\n ): Promise<void> {\n for await (const raw of stream) {\n let parsed: WireEvent;\n try {\n const data = JSON.parse(raw.data);\n if (\n typeof data !== \"object\" ||\n data === null ||\n typeof data.type !== \"string\"\n ) {\n // Legacy \"done\" sentinel — backend still emits it for subscribers.\n // Silently consume; the new protocol uses message_stop for turn end.\n if (typeof (data as { seq?: unknown })?.seq === \"number\") {\n this.lastSeq = (data as { seq: number }).seq;\n }\n continue;\n }\n parsed = data as WireEvent;\n if (typeof (data as Record<string, unknown>).seq === \"number\") {\n this.lastSeq = (data as Record<string, unknown>).seq as number;\n }\n } catch {\n continue;\n }\n\n await this.dispatchWireEvent(\n parsed,\n conversationId,\n messageId,\n executeClientTools,\n );\n }\n }\n\n private async dispatchWireEvent(\n wire: WireEvent,\n conversationId: string,\n messageId: string,\n executeClientTools: boolean,\n ): Promise<void> {\n // Side effects that depend on mutable session state must run before the\n // ChatEvent is emitted so consumers see a consistent view.\n this.applyWireSideEffects(wire, conversationId, messageId);\n\n const event = translateWireEvent(wire);\n if (event) {\n this.emit(event);\n }\n\n // Client tool round-trip — deferred to block_stop with\n // status=awaiting_client_result, where the parsed input is in final.input.\n if (\n executeClientTools &&\n wire.type === \"block_stop\" &&\n wire.status === \"awaiting_client_result\" &&\n wire.final?.call_id\n ) {\n const f = wire.final;\n const request: ToolCallRequest = {\n callId: (f.call_id as string) ?? \"\",\n toolName: (f.tool_name as string) ?? \"\",\n arguments: (f.input as Record<string, unknown>) ?? {},\n isClientTool: true,\n };\n const results = await this.executeClientTools([request]);\n await this.client.submitToolResult({\n conversation_id: conversationId,\n message_id: messageId,\n tool_results: results,\n });\n }\n }\n\n /**\n * State mutations driven by wire events. Kept separate from translation so\n * the pure wire → ChatEvent mapping can live in translate.ts and be reused\n * by the replay path.\n *\n * ``messageId`` is the server-assigned assistant message id for the current\n * turn; empty in the reconnect and conversation-switch replay paths where\n * messages have already been loaded from REST and shouldn't be re-pushed.\n */\n private applyWireSideEffects(\n wire: WireEvent,\n conversationId: string,\n messageId: string,\n ): void {\n switch (wire.type) {\n case \"message_start\":\n // Reset per-turn accumulator so multi-turn replay doesn't concatenate\n // text from prior turns into the next assistant message.\n this.resetStreamingState();\n if (wire.model) {\n this.modelDisplayName = wire.model;\n }\n return;\n\n case \"block_start\":\n // Track the currently open top-level text block so we can accumulate\n // its content for the assistant Message record.\n if (\n wire.kind === \"text\" &&\n (!wire.parent_path || wire.parent_path.length === 0)\n ) {\n this.currentTextPath = wire.path;\n }\n return;\n\n case \"block_delta\":\n if (\n wire.delta.channel === \"text\" &&\n this.currentTextPath !== null &&\n pathEquals(this.currentTextPath, wire.path)\n ) {\n this.accumulatedText += wire.delta.text;\n }\n return;\n\n case \"block_stop\":\n if (\n this.currentTextPath !== null &&\n pathEquals(this.currentTextPath, wire.path)\n ) {\n this.currentTextPath = null;\n }\n return;\n\n case \"message_stop\":\n // Only record the assistant message when we have the server's\n // message id. Reconnect/replay paths load messages via REST instead.\n if (messageId) {\n const assistantMessage: Message = {\n id: messageId,\n conversationId,\n role: \"assistant\",\n content: this.accumulatedText,\n status: \"complete\",\n createdAt: new Date().toISOString(),\n };\n this.messages.push(assistantMessage);\n this.storage\n .addMessage(assistantMessage, conversationId)\n .catch(() => {});\n }\n this.isStreaming = false;\n this.currentJobId = null;\n return;\n\n case \"custom\":\n if (wire.name === \"title_generated\") {\n const title = (wire.data.title as string) ?? \"\";\n if (this.conversationId && title) {\n const conv = this.conversations.find(\n (c) => c.id === this.conversationId,\n );\n if (conv) {\n conv.title = title;\n }\n this.storage\n .updateConversationTitle(this.conversationId, title)\n .catch(() => {});\n }\n }\n return;\n\n default:\n return;\n }\n }\n\n private async executeClientTools(\n toolCalls: ToolCallRequest[],\n ): Promise<ToolResult[]> {\n const results: ToolResult[] = [];\n for (const call of toolCalls) {\n const result = await this.toolRegistry.executeTool(call);\n results.push(result);\n }\n return results;\n }\n\n /**\n * Load conversation context (messages) without replaying events.\n * Used before reconnectToJob — SSE replay handles event replay.\n */\n async loadConversation(id: string): Promise<void> {\n this.conversationId = id;\n this.resetStreamingState();\n this.messages = await this.client\n .getMessages(id)\n .catch(() => this.storage.fetchMessages(id));\n }\n\n /**\n * Reconnect to a running job's SSE stream (e.g. after page reload).\n * Replays all events from the beginning and continues live.\n */\n async reconnectToJob(jobId: string): Promise<void> {\n if (this.isStreaming) return;\n\n this.isStreaming = true;\n this.currentJobId = jobId;\n this.lastSeq = -1;\n this.resetStreamingState();\n this.abortController = new AbortController();\n\n try {\n const stream = this.client.streamJobEvents(\n jobId,\n this.lastSeq,\n this.abortController?.signal,\n );\n await this.consumeEventStream(\n stream,\n this.conversationId ?? \"\",\n \"\",\n false, // don't execute client tools on reconnect\n );\n } catch (err) {\n this.emit({\n type: \"error\",\n code: \"connection_error\",\n message: err instanceof Error ? err.message : String(err),\n blockPath: null,\n });\n } finally {\n this.isStreaming = false;\n this.abortController = null;\n }\n }\n\n /** Detach from the SSE stream without cancelling the job. */\n detach(): void {\n this.abortController?.abort();\n this.abortController = null;\n this.isStreaming = false;\n this.resetStreamingState();\n this.emit({ type: \"disconnected\" });\n }\n\n /** Stop the job and disconnect (explicit user action). */\n disconnect(): void {\n if (this.currentJobId) {\n this.client.cancelJob(this.currentJobId).catch(() => {});\n }\n this.detach();\n this.currentJobId = null;\n // Drop all protocol adapters — lifecycle tied to the session.\n this.protocols.clear();\n }\n\n async createNewConversation(): Promise<string> {\n const id = generateId();\n const conversation = await this.storage.createConversation(\n id,\n \"New Conversation\",\n );\n this.conversations.unshift(conversation);\n this.conversationId = id;\n this.messages = [];\n return id;\n }\n\n async switchConversation(\n id: string,\n jobId?: string,\n /**\n * User prompt that triggered this job, if known. Emitted as a\n * synthetic ``user_message`` ChatEvent right before the first\n * ``message_start`` of the replay. User messages aren't persisted\n * in ``job_events``, so without this the restored conversation\n * would show the agent response with no visible prompt above it.\n */\n userMessageContent?: string,\n ): Promise<void> {\n this.conversationId = id;\n this.resetStreamingState();\n\n const [messagesResult, eventsResult] = await Promise.allSettled([\n this.client.getMessages(id).catch(() => this.storage.fetchMessages(id)),\n this.client.getConversationEvents(id, jobId),\n ]);\n\n this.messages =\n messagesResult.status === \"fulfilled\" ? messagesResult.value : [];\n\n // Replay stored events via the same dispatch path. The consumer\n // rebuilds its block state from the replayed events. The data payload\n // is authoritative for `type` (matches how replay.ts#mapSseToChat reads\n // it) with the SSE event name as a fallback for pre-v2 rows.\n if (eventsResult.status === \"fulfilled\") {\n let userMessageEmitted = !userMessageContent;\n for (const ev of eventsResult.value) {\n const type = (ev.data.type as string) || ev.event;\n if (!type || type === \"done\") continue;\n\n // Inject the user prompt at the turn boundary — right before\n // the first ``message_start`` — so the consumer can insert a\n // user block above the agent response.\n if (!userMessageEmitted && type === \"message_start\") {\n this.emit({\n type: \"user_message\",\n content: userMessageContent!,\n });\n userMessageEmitted = true;\n }\n\n const wire = { ...ev.data, type } as unknown as WireEvent;\n try {\n await this.dispatchWireEvent(\n wire,\n id,\n \"\",\n false, // don't execute client tools on replay\n );\n } catch {\n // Skip malformed replay events\n }\n }\n }\n }\n\n async deleteConversation(id: string): Promise<void> {\n try {\n await this.client.deleteConversation(id);\n } catch {\n // May already be deleted on backend\n }\n await this.storage.deleteConversation(id);\n this.conversations = this.conversations.filter((c) => c.id !== id);\n if (this.conversationId === id) {\n this.conversationId = null;\n this.messages = [];\n }\n }\n\n toggleClientTool(name: string): boolean {\n if (this.enabledClientTools.has(name)) {\n this.enabledClientTools.delete(name);\n return false;\n }\n this.enabledClientTools.add(name);\n return true;\n }\n}\n","// =============================================================================\n// Astralform SDK v2 type definitions\n// =============================================================================\n//\n// Wire protocol types mirror the Pydantic models in\n// `backend/src/stream/protocol.py` 1:1. The SDK forwards typed events to\n// the consumer; block construction happens on the consumer side.\n//\n// Custom event payloads live in `custom-events.ts`.\n\nimport type { AgentIdentity, MemoryRecord, TodoItem } from \"./custom-events.js\";\n\n// Re-export so consumers can import from types.ts or custom-events.ts\nexport type { AgentIdentity, MemoryRecord, TodoItem };\n\n// --- Configuration ---\n//\n// The SDK supports two authentication modes. TypeScript narrows the union by\n// property presence, so consumers can write:\n//\n// new AstralformClient({ apiKey, userId }) // API-key mode\n// new AstralformClient({ accessToken, projectId }) // user-token mode\n//\n// Pick based on who the caller represents:\n//\n// * API-key mode — A customer's backend (B2B2C). Project scoping is baked\n// into the key; the end user is named per request via `X-End-User-ID`.\n//\n// * User-token mode — An app acting on behalf of an Astralform account\n// holder (AstralChat, future 3rd-party integrations). The OIDC access\n// token is issued by the Astralform Identity Provider (Supabase OAuth 2.1\n// Server); project scoping comes from the `X-Project-ID` header.\n\ninterface AstralformBaseConfig {\n /** Override the default API origin. Defaults to https://api.astralform.ai. */\n baseURL?: string;\n /** Supply a custom fetch (SSR, testing, custom interceptors). */\n fetch?: typeof globalThis.fetch;\n}\n\nexport interface AstralformApiKeyConfig extends AstralformBaseConfig {\n /** Project API key (`sk_live_...` or `sk_test_...`). */\n apiKey: string;\n /**\n * The customer's own identifier for the end user making this call.\n * Sent as the `X-End-User-ID` header. Required in API-key mode.\n */\n userId: string;\n}\n\nexport interface AstralformUserTokenConfig extends AstralformBaseConfig {\n /**\n * OIDC access token issued by the Astralform Identity Provider.\n * Expect this to be short-lived; use `client.updateAccessToken()` after\n * refreshing to hot-swap without re-instantiating the client.\n */\n accessToken: string;\n /**\n * Active project context. Sent as the `X-Project-ID` header. The backend\n * verifies the token's developer has access to this project on every\n * request; switching projects is a local `updateProjectId()` call.\n *\n * Optional so a pre-pick client (right after login, before the user has\n * chosen a team/project) can still call account-scoped discovery routes\n * like `listTeams()` / `listProjects(teamId)`. Project-scoped calls\n * (conversations, messages, chat) will error out until one is set.\n */\n projectId?: string;\n /**\n * Optional end-user override — lets a developer acting under a user\n * token impersonate a downstream end-user identity for testing\n * purposes. When set, sent alongside `X-Project-ID` as `X-End-User-ID`\n * so memory, rate limits, and conversations scope to the specified\n * end-user rather than the developer themselves.\n *\n * Use `client.updateEndUserId()` to rotate at runtime.\n */\n endUserId?: string;\n}\n\nexport type AstralformConfig =\n | AstralformApiKeyConfig\n | AstralformUserTokenConfig;\n\n// --- Event type constants (SDK public) ---\n//\n// These are the high-level ChatEvent kinds the SDK emits to its\n// consumer. The raw wire events below are translated into these\n// kinds at the session boundary.\n\nexport const ChatEventType = {\n // Connection lifecycle (SDK-local, not wire)\n Connected: \"connected\",\n Disconnected: \"disconnected\",\n\n // Turn lifecycle\n MessageStart: \"message_start\",\n MessageStop: \"message_stop\",\n\n // Block lifecycle\n BlockStart: \"block_start\",\n BlockDelta: \"block_delta\",\n BlockStop: \"block_stop\",\n\n // Reliability\n Stall: \"stall\",\n Retry: \"retry\",\n Error: \"error\",\n Keepalive: \"keepalive\",\n\n // Conversation-level (typed custom events)\n UserMessage: \"user_message\",\n TitleGenerated: \"title_generated\",\n TodoUpdate: \"todo_update\",\n ContextUpdate: \"context_update\",\n SubagentStart: \"subagent_start\",\n SubagentStop: \"subagent_stop\",\n ContextWarning: \"context_warning\",\n MemoryRecall: \"memory_recall\",\n MemoryUpdate: \"memory_update\",\n DesktopStream: \"desktop_stream\",\n AttachmentStaged: \"attachment_staged\",\n WorkspaceReady: \"workspace_ready\",\n AssetCreated: \"asset_created\",\n ToolApprovalRequested: \"tool_approval_requested\",\n ToolApprovalGranted: \"tool_approval_granted\",\n ToolPermissionDenied: \"tool_permission_denied\",\n ToolHarnessWarning: \"tool_harness_warning\",\n UserUnavailable: \"user_unavailable\",\n PromptSuggestion: \"prompt_suggestion\",\n StateChanged: \"state_changed\",\n\n // Generic fallthrough for unknown custom events\n Custom: \"custom\",\n} as const;\n\nexport type ChatEventTypeValue =\n (typeof ChatEventType)[keyof typeof ChatEventType];\n\n// =============================================================================\n// Wire protocol (matches backend Pydantic models 1:1, snake_case)\n// =============================================================================\n\nexport type WireBlockKind = \"text\" | \"thinking\" | \"tool_use\";\n\nexport type WireBlockStatus =\n | \"streaming\"\n | \"awaiting_client_result\"\n | \"ok\"\n | \"error\"\n | \"denied\"\n | \"cancelled\";\n\nexport type WireStopReason =\n | \"end_turn\"\n | \"tool_use\"\n | \"max_tokens\"\n | \"context_overflow\"\n | \"error\";\n\n// --- BlockDelta payloads (discriminated on `channel`) ---\n\nexport interface WireTextDelta {\n channel: \"text\";\n text: string;\n}\n\nexport interface WireThinkingDelta {\n channel: \"thinking\";\n text: string;\n}\n\nexport interface WireSignatureDelta {\n channel: \"signature\";\n signature: string;\n}\n\nexport interface WireInputDelta {\n channel: \"input\";\n partial_json: string;\n}\n\nexport interface WireInputArgDelta {\n channel: \"input_arg\";\n arg_name: string;\n text: string;\n}\n\nexport interface WireOutputDelta {\n channel: \"output\";\n stream: \"stdout\" | \"stderr\" | \"progress\";\n chunk: string;\n}\n\nexport interface WireStatusDelta {\n channel: \"status\";\n status:\n | \"executing\"\n | \"awaiting_client_result\"\n | \"awaiting_approval\"\n | \"denied\";\n note?: string;\n}\n\nexport type WireBlockDeltaPayload =\n | WireTextDelta\n | WireThinkingDelta\n | WireSignatureDelta\n | WireInputDelta\n | WireInputArgDelta\n | WireOutputDelta\n | WireStatusDelta;\n\n// --- Top-level wire events ---\n\ninterface WireEnvelope {\n seq: number;\n ts: number;\n job_id: string;\n}\n\nexport interface WireMessageStart extends WireEnvelope {\n type: \"message_start\";\n turn_id: string;\n model: string;\n agent_name?: string | null;\n agent_display_name?: string | null;\n agent_avatar_url?: string | null;\n}\n\nexport interface WireBlockStart extends WireEnvelope {\n type: \"block_start\";\n turn_id: string;\n path: number[];\n parent_path?: number[] | null;\n kind: WireBlockKind;\n metadata: Record<string, unknown>;\n}\n\nexport interface WireBlockDelta extends WireEnvelope {\n type: \"block_delta\";\n turn_id: string;\n path: number[];\n delta: WireBlockDeltaPayload;\n}\n\nexport interface WireBlockStop extends WireEnvelope {\n type: \"block_stop\";\n turn_id: string;\n path: number[];\n status: WireBlockStatus;\n final: Record<string, unknown>;\n}\n\nexport interface WireMessageStop extends WireEnvelope {\n type: \"message_stop\";\n turn_id: string;\n stop_reason: WireStopReason;\n usage: {\n input_tokens?: number;\n output_tokens?: number;\n cached_tokens?: number;\n };\n ttfb_ms?: number | null;\n total_ms: number;\n stall_count: number;\n}\n\nexport interface WireStallWarning extends WireEnvelope {\n type: \"stall\";\n since_last_event_ms: number;\n stall_count: number;\n}\n\nexport interface WireRetryEvent extends WireEnvelope {\n type: \"retry\";\n attempt: number;\n reason: string;\n backoff_ms: number;\n strategy?: string | null;\n max_attempts?: number | null;\n context_recovery?: Record<string, unknown> | null;\n}\n\nexport interface WireErrorEvent extends WireEnvelope {\n type: \"error\";\n code: string;\n message: string;\n block_path?: number[] | null;\n // Rate limit fields (carried when code == \"rate_limit_exceeded\")\n retry_after?: number;\n retry_after_sec?: number;\n reset_at?: number | string;\n scope?: string;\n policy_id?: string;\n limit?: number;\n remaining?: number;\n request_id?: string;\n}\n\nexport interface WireKeepalive extends WireEnvelope {\n type: \"keepalive\";\n since_last_event_ms: number;\n}\n\nexport interface WireCustomEvent extends WireEnvelope {\n type: \"custom\";\n name: string;\n data: Record<string, unknown>;\n}\n\nexport type WireEvent =\n | WireMessageStart\n | WireBlockStart\n | WireBlockDelta\n | WireBlockStop\n | WireMessageStop\n | WireStallWarning\n | WireRetryEvent\n | WireErrorEvent\n | WireKeepalive\n | WireCustomEvent;\n\n// =============================================================================\n// ChatEvent — high-level SDK events (camelCase, emitted to consumers)\n// =============================================================================\n\nexport interface TurnUsage {\n inputTokens: number;\n outputTokens: number;\n cachedTokens: number;\n}\n\nexport type BlockDeltaPayload =\n | { channel: \"text\"; text: string }\n | { channel: \"thinking\"; text: string }\n | { channel: \"signature\"; signature: string }\n | { channel: \"input\"; partialJson: string }\n | { channel: \"inputArg\"; argName: string; text: string }\n | {\n channel: \"output\";\n stream: \"stdout\" | \"stderr\" | \"progress\";\n chunk: string;\n }\n | {\n channel: \"status\";\n status:\n | \"executing\"\n | \"awaiting_client_result\"\n | \"awaiting_approval\"\n | \"denied\";\n note?: string;\n };\n\nexport type ChatEvent =\n // Connection lifecycle\n | { type: \"connected\" }\n | { type: \"disconnected\" }\n\n // Turn lifecycle\n | {\n type: \"message_start\";\n turnId: string;\n model: string;\n agentName?: string | null;\n agentDisplayName?: string | null;\n agentAvatarUrl?: string | null;\n }\n | {\n type: \"message_stop\";\n turnId: string;\n jobId: string;\n stopReason: WireStopReason;\n usage: TurnUsage;\n ttfbMs?: number | null;\n totalMs: number;\n stallCount: number;\n }\n\n // Block lifecycle\n | {\n type: \"block_start\";\n turnId: string;\n path: number[];\n parentPath?: number[] | null;\n kind: WireBlockKind;\n metadata: Record<string, unknown>;\n }\n | {\n type: \"block_delta\";\n turnId: string;\n path: number[];\n delta: BlockDeltaPayload;\n }\n | {\n type: \"block_stop\";\n turnId: string;\n path: number[];\n status: WireBlockStatus;\n final: Record<string, unknown>;\n }\n\n // Reliability\n | {\n type: \"stall\";\n sinceLastEventMs: number;\n stallCount: number;\n }\n | {\n type: \"retry\";\n attempt: number;\n reason: string;\n backoffMs: number;\n strategy?: string | null;\n maxAttempts?: number | null;\n contextRecovery?: Record<string, unknown> | null;\n }\n | {\n type: \"error\";\n code: string;\n message: string;\n blockPath: number[] | null;\n }\n | {\n type: \"keepalive\";\n sinceLastEventMs: number;\n }\n\n // Conversation-level — typed custom events\n | { type: \"user_message\"; content: string; createdAt?: number }\n | { type: \"title_generated\"; title: string }\n | { type: \"todo_update\"; todos: TodoItem[] }\n | {\n type: \"context_update\";\n context: Record<string, unknown>;\n phase?: string | null;\n updatedAt?: number | null;\n }\n | {\n type: \"subagent_start\";\n agent: AgentIdentity;\n taskCallId?: string | null;\n }\n | {\n type: \"subagent_stop\";\n agent: AgentIdentity;\n taskCallId?: string | null;\n }\n | {\n type: \"context_warning\";\n /** Known values: \"info\" | \"warning\" | \"critical\". Typed as string for forward compat. */\n severity: string;\n utilizationPct: number;\n remainingTokens: number;\n windowTokens: number;\n inputTokens: number;\n message: string;\n }\n | { type: \"memory_recall\"; memories: MemoryRecord[] }\n | {\n type: \"memory_update\";\n /** Known values: \"created\" | \"updated\" | \"deleted\". */\n action: string;\n memoryId?: string | null;\n key?: string | null;\n namespace?: string | null;\n }\n | { type: \"desktop_stream\"; url: string; sandboxId?: string | null }\n | {\n type: \"attachment_staged\";\n attachmentId: string;\n filename: string;\n contentType?: string | null;\n sizeBytes?: number | null;\n }\n | {\n type: \"workspace_ready\";\n sandboxId: string;\n workspacePath?: string | null;\n }\n | {\n type: \"asset_created\";\n assetId: string;\n filename: string;\n url?: string | null;\n contentType?: string | null;\n }\n | {\n type: \"tool_approval_requested\";\n toolName: string;\n callId: string;\n arguments: Record<string, unknown>;\n riskLevel?: string | null;\n reason?: string | null;\n }\n | {\n type: \"tool_approval_granted\";\n toolName: string;\n callId: string;\n }\n | {\n type: \"tool_permission_denied\";\n toolName: string;\n callId: string;\n reason?: string | null;\n /** Known values: \"hook\" | \"rule\" | \"user\" | \"timeout\" | \"circuit_breaker\". */\n deniedBy?: string | null;\n }\n | {\n type: \"tool_harness_warning\";\n toolName: string;\n callId: string;\n message?: string | null;\n details?: Record<string, unknown> | null;\n }\n | {\n type: \"user_unavailable\";\n consecutiveTimeouts: number;\n toolName?: string | null;\n }\n | {\n type: \"prompt_suggestion\";\n suggestions: string[];\n }\n | {\n type: \"state_changed\";\n /**\n * Job lifecycle state. Known values from the backend today include\n * \"queued\" | \"running\" | \"waiting_for_tool\" | \"completed\" | \"failed\".\n * Typed as string for forward compat.\n */\n state: string;\n }\n\n // Generic fallthrough for unknown custom events\n | {\n type: \"custom\";\n name: string;\n data: Record<string, unknown>;\n };\n\n// =============================================================================\n// Domain models (unchanged from previous SDK version)\n// =============================================================================\n\nexport interface Conversation {\n id: string;\n title: string;\n messageCount: number;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface Message {\n id: string;\n conversationId: string;\n role: \"user\" | \"assistant\" | \"system\";\n content: string;\n parentId?: string;\n status: \"sending\" | \"streaming\" | \"complete\" | \"error\";\n createdAt: string;\n toolCalls?: ToolCallRequest[];\n}\n\nexport interface UIComponentsConfig {\n enabled: boolean;\n /** Protocol slug (e.g. \"a2ui\"). Null when disabled. */\n protocol: string | null;\n /** MIME type to match against embedded resources (e.g. \"application/json+a2ui\"). */\n mimeType: string | null;\n}\n\nexport interface ProjectStatus {\n isReady: boolean;\n llmConfigured: boolean;\n llmProvider?: string;\n llmModel?: string;\n message: string;\n uiComponents: UIComponentsConfig;\n}\n\nexport interface AgentInfo {\n name: string;\n displayName: string;\n description: string;\n isOrchestrator: boolean;\n isEnabled: boolean;\n avatarUrl?: string;\n}\n\n// --- Team / Project discovery (OIDC user-token surface) ---\n\nexport interface TeamSummary {\n id: string;\n name: string;\n slug: string;\n isDefault: boolean;\n /** Caller's role in this team (e.g. \"owner\", \"admin\", \"member\"). */\n role: string;\n}\n\nexport interface ProjectSummary {\n id: string;\n name: string;\n teamId: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface SkillInfo {\n name: string;\n displayName: string;\n description: string;\n isEnabled: boolean;\n}\n\n// TodoItem is imported from custom-events.ts and re-exported above.\n\n// =============================================================================\n// Job and request types\n// =============================================================================\n\nexport interface JobCreateResponse {\n job_id: string;\n conversation_id: string;\n message_id: string;\n status: string;\n}\n\nexport interface JobStatus {\n jobId: string;\n status: string;\n createdAt: string | null;\n startedAt: string | null;\n completedAt: string | null;\n errorMessage: string | null;\n inputTokens: number;\n outputTokens: number;\n}\n\nexport interface ActiveJob {\n jobId: string | null;\n status: string;\n}\n\nexport interface JobSummary {\n jobId: string;\n status: string;\n replacesJobId: string | null;\n responseContent: Record<string, unknown> | null;\n metrics: Record<string, unknown> | null;\n createdAt: string | null;\n}\n\nexport interface FeedbackRequest {\n /** 1 for thumbs up, -1 for thumbs down. */\n rating: 1 | -1;\n comment?: string | null;\n}\n\nexport interface FeedbackResponse {\n id: string;\n jobId: string;\n rating: number;\n comment: string | null;\n createdAt: string;\n}\n\nexport interface ChatStreamRequest {\n message?: string;\n conversation_id?: string;\n mcp_manifest?: ToolDefinition[];\n enabled_mcp?: string[];\n continue_from_message?: string;\n resend_from?: string;\n upload_ids?: string[];\n agent_name?: string;\n enable_search?: boolean;\n plan_mode?: boolean;\n}\n\nexport interface ToolResultRequest {\n conversation_id: string;\n message_id: string;\n tool_results: ToolResult[];\n}\n\nexport interface ToolResult {\n call_id: string;\n tool_name: string;\n result: string;\n is_error: boolean;\n}\n\nexport type ToolApprovalDecision = \"allow\" | \"deny\";\nexport type ToolApprovalScope = \"once\" | \"conversation\" | \"always\";\n\nexport interface ToolApprovalRequest {\n job_id: string;\n call_id: string;\n decision: ToolApprovalDecision;\n scope: ToolApprovalScope;\n}\n\nexport interface ToolDefinition {\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n}\n\nexport interface ToolCallRequest {\n callId: string;\n toolName: string;\n displayName?: string;\n description?: string;\n arguments: Record<string, unknown>;\n isClientTool: boolean;\n toolCategory?: string;\n iconUrl?: string;\n}\n\n// =============================================================================\n// SSE transport / raw parsing\n// =============================================================================\n\nexport interface StreamJobSSEOptions {\n url: string;\n headers: Record<string, string>;\n signal?: AbortSignal;\n fetchFn: typeof globalThis.fetch;\n}\n\nexport interface ChatStreamEvent {\n event: string;\n data: string;\n}\n\nexport interface ConversationEvent {\n seq: number;\n event: string;\n data: Record<string, unknown>;\n}\n\n// =============================================================================\n// Send / session options\n// =============================================================================\n\nexport interface SendOptions {\n conversationId?: string;\n enabledClientTools?: string[];\n uploadIds?: string[];\n agentName?: string;\n enableSearch?: boolean;\n planMode?: boolean;\n}\n\n// =============================================================================\n// Conversation assets (unchanged)\n// =============================================================================\n\nexport interface ConversationAsset {\n id: string;\n kind: \"upload\" | \"output\";\n originalName: string;\n mediaType: string;\n sizeBytes: number;\n workspacePath?: string;\n sourceMessageId?: string;\n agentName?: string;\n createdAt: string;\n}\n","/**\n * StreamManager — high-level conversation lifecycle coordinator.\n *\n * Sits on top of ChatSession and manages the state machine for\n * multi-conversation SSE streaming. Framework-agnostic: emits\n * typed events to registered handlers. Block construction is NOT\n * the SDK's concern — consumers build their own block tree from\n * the forwarded ``ChatEvent`` instances.\n *\n * import { ChatSession, StreamManager } from \"@astralform/js\";\n * const session = new ChatSession({ ... });\n * const manager = new StreamManager(session);\n * manager.on((event) => {\n * if (event.type === \"event\") {\n * // event.event is a typed ChatEvent — dispatch to your reducer\n * }\n * });\n * await manager.send(\"Hello\");\n */\n\nimport type { ChatEvent } from \"./types.js\";\nimport { ChatEventType } from \"./types.js\";\nimport type { ChatSession } from \"./session.js\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type StreamState = \"idle\" | \"streaming\" | \"restoring\" | \"detached\";\n\nexport interface SendOptions {\n enableSearch?: boolean;\n agentName?: string;\n uploadIds?: string[];\n planMode?: boolean;\n}\n\nexport type StreamManagerEvent =\n | { type: \"stateChange\"; state: StreamState; conversationId: string | null }\n | { type: \"conversationChanged\"; conversationId: string | null }\n | {\n type: \"backgroundJobsChanged\";\n jobs: ReadonlyMap<string, string>;\n }\n | { type: \"event\"; conversationId: string | null; event: ChatEvent }\n | { type: \"versionsReady\"; conversationId: string; count: number };\n\ntype EventHandler = (event: StreamManagerEvent) => void;\n\n// =============================================================================\n// StreamManager\n// =============================================================================\n\nexport class StreamManager {\n private session: ChatSession;\n private _state: StreamState = \"idle\";\n private _activeConversationId: string | null = null;\n private _backgroundJobs = new Map<string, string>();\n private handlers: EventHandler[] = [];\n private unsub: (() => void) | null = null;\n\n constructor(session: ChatSession) {\n this.session = session;\n this.attach();\n }\n\n // ── Public state ──────────────────────────────────────────────\n\n get state(): StreamState {\n return this._state;\n }\n\n get activeConversationId(): string | null {\n return this._activeConversationId;\n }\n\n get backgroundJobs(): ReadonlyMap<string, string> {\n return this._backgroundJobs;\n }\n\n // ── Event subscription ────────────────────────────────────────\n\n on(handler: EventHandler): () => void {\n this.handlers.push(handler);\n return () => {\n this.handlers = this.handlers.filter((h) => h !== handler);\n };\n }\n\n private emit(event: StreamManagerEvent): void {\n for (const handler of this.handlers) {\n try {\n handler(event);\n } catch {\n // Don't let handler errors crash the manager\n }\n }\n }\n\n private setState(state: StreamState): void {\n this._state = state;\n this.emit({\n type: \"stateChange\",\n state,\n conversationId: this._activeConversationId,\n });\n }\n\n // ── Session event wiring ──────────────────────────────────────\n\n private attach(): void {\n this.unsub = this.session.on((event: ChatEvent) => {\n this.onSessionEvent(event);\n });\n }\n\n private onSessionEvent(event: ChatEvent): void {\n const convId = this.session.conversationId;\n\n // Forward every event to subscribers as a typed envelope\n this.emit({\n type: \"event\",\n conversationId: convId,\n event,\n });\n\n // Handle completion — message_stop is the terminal turn event.\n if (event.type === ChatEventType.MessageStop) {\n if (this._state === \"streaming\") {\n this.setState(\"idle\");\n }\n }\n }\n\n // ── Send ──────────────────────────────────────────────────────\n\n async send(content: string, options?: SendOptions): Promise<void> {\n if (this._state === \"streaming\") return;\n\n // Auto-create conversation if none active\n if (!this._activeConversationId) {\n const id = await this.session.createNewConversation();\n this.setActiveConversation(id);\n }\n\n this.setState(\"streaming\");\n\n try {\n await this.session.send(content, {\n enableSearch: options?.enableSearch,\n agentName: options?.agentName,\n uploadIds: options?.uploadIds,\n planMode: options?.planMode,\n });\n } catch {\n // AbortError from detach is expected\n }\n\n this.finalizeStream();\n }\n\n // ── Regenerate ────────────────────────────────────────────────\n\n async regenerate(): Promise<void> {\n if (this._state === \"streaming\") return;\n\n const userMsgs = this.session.messages.filter(\n (m: { role: string }) => m.role === \"user\",\n );\n const lastUserMsg = userMsgs[userMsgs.length - 1];\n if (!lastUserMsg) return;\n\n this.setState(\"streaming\");\n\n try {\n await this.session.resendFromCheckpoint(\n lastUserMsg.id,\n lastUserMsg.content,\n );\n } catch {\n // AbortError from detach is expected\n }\n\n this.finalizeStream();\n }\n\n // ── Switch conversation ───────────────────────────────────────\n\n async switchTo(conversationId: string): Promise<void> {\n if (conversationId === this._activeConversationId) return;\n\n // If streaming, detach (job keeps running in background)\n if (this._state === \"streaming\") {\n const oldConvId = this._activeConversationId;\n const jobId = this.session.currentJobId;\n if (oldConvId && jobId) {\n this._backgroundJobs.set(oldConvId, jobId);\n this.emit({\n type: \"backgroundJobsChanged\",\n jobs: this._backgroundJobs,\n });\n }\n this.session.detach();\n }\n\n // Clear background job for target (we're viewing it now)\n if (this._backgroundJobs.has(conversationId)) {\n this._backgroundJobs.delete(conversationId);\n this.emit({\n type: \"backgroundJobsChanged\",\n jobs: this._backgroundJobs,\n });\n }\n\n this.setActiveConversation(conversationId);\n await this.restore(conversationId);\n }\n\n // ── Create / delete conversation ──────────────────────────────\n\n async createConversation(): Promise<string> {\n const id = await this.session.createNewConversation();\n this.setActiveConversation(id);\n return id;\n }\n\n async deleteConversation(id: string): Promise<void> {\n await this.session.deleteConversation(id);\n this._backgroundJobs.delete(id);\n if (this._activeConversationId === id) {\n this._activeConversationId = null;\n this.emit({ type: \"conversationChanged\", conversationId: null });\n }\n }\n\n // ── Stop (explicit cancel) ────────────────────────────────────\n\n stop(): void {\n this.session.disconnect();\n this.setState(\"idle\");\n }\n\n // ── Cleanup ───────────────────────────────────────────────────\n\n destroy(): void {\n if (this.unsub) {\n this.unsub();\n this.unsub = null;\n }\n this.handlers = [];\n }\n\n // ── Internal: helpers ──────────────────────────────────────────\n\n private finalizeStream(): void {\n if (this._state === \"streaming\") {\n this.setState(\"idle\");\n }\n }\n\n // ── Internal: restore ─────────────────────────────────────────\n\n private async restore(conversationId: string): Promise<void> {\n this.setState(\"restoring\");\n\n // Check for active job\n let activeJobId: string | null = null;\n try {\n const res = await this.session.client.getActiveJob(conversationId);\n activeJobId = res.jobId;\n } catch {\n // Network error — assume no active job\n }\n\n if (activeJobId) {\n // Active job: load messages, reconnect to live SSE\n await this.session.loadConversation(conversationId);\n this.setState(\"streaming\");\n try {\n await this.session.reconnectToJob(activeJobId);\n } catch {\n // Stream ended or aborted\n }\n if (this._state === \"streaming\") {\n this.setState(\"idle\");\n }\n } else {\n // Completed: load messages, replay version chain\n await this.session.loadConversation(conversationId);\n\n try {\n const jobs = await this.session.client.get<\n {\n job_id: string;\n status: string;\n metrics?: Record<string, unknown>;\n }[]\n >(`/v1/conversations/${encodeURIComponent(conversationId)}/jobs`);\n const completedJobs = jobs.filter(\n (j: { status: string }) => j.status === \"completed\",\n );\n\n // User prompts aren't persisted in job_events — they live in\n // the messages table. Pair each completed job with its user\n // prompt by chronological index: the N-th completed job was\n // triggered by the N-th user message. Feed the content into\n // ``switchConversation`` so it emits a ``user_message`` event\n // at the right boundary during replay.\n const userMessages = this.session.messages.filter(\n (m) => m.role === \"user\",\n );\n\n for (let i = 0; i < completedJobs.length; i++) {\n const job = completedJobs[i]!;\n const userContent = userMessages[i]?.content;\n await this.session.switchConversation(\n conversationId,\n job.job_id,\n userContent,\n );\n }\n\n if (completedJobs.length > 0) {\n this.emit({\n type: \"versionsReady\",\n conversationId,\n count: completedJobs.length,\n });\n }\n } catch {\n // Version chain loading failed — non-blocking\n }\n\n this.setState(\"idle\");\n }\n }\n\n // ── Internal: set active conversation ─────────────────────────\n\n private setActiveConversation(id: string): void {\n this._activeConversationId = id;\n this.emit({ type: \"conversationChanged\", conversationId: id });\n }\n}\n","// =============================================================================\n// Event replay — translate persisted wire events into ChatEvent sequences\n//\n// The backend persists wire events in the `job_events` table. When a\n// consumer needs to restore a conversation (page refresh, conversation\n// switch), it fetches these raw events and replays them through the same\n// ChatEvent pipeline used during live streaming.\n//\n// All wire → ChatEvent translation lives in `translate.ts`. This file just\n// adapts the persisted envelope shape (`{seq, event, data}`) and handles\n// user-message interleaving, which the backend doesn't record.\n// =============================================================================\n\nimport { translateWireEvent } from \"./translate.js\";\nimport type { ChatEvent, WireEvent } from \"./types.js\";\n\n/**\n * Raw SSE event shape returned by GET /v1/conversations/{id}/events.\n * Mirrors what JobEventWriter persists to the job_events table.\n */\nexport interface RawSseEvent {\n seq: number;\n event: string;\n data: Record<string, unknown>;\n /** Epoch ms when the event was persisted (from job_events.created_at). */\n created_at?: number;\n}\n\n/**\n * Build a ``WireEvent`` from the persisted envelope. The data payload is\n * authoritative (and always carries ``type`` in practice); the SSE event\n * name is a fallback for older rows that pre-date the v2 protocol.\n */\nfunction toWireEvent(raw: RawSseEvent): WireEvent | null {\n const type = (raw.data.type as string) || raw.event;\n if (!type || type === \"done\") return null;\n return { ...raw.data, type } as unknown as WireEvent;\n}\n\n/**\n * Map a raw SSE event (persisted in job_events) into the SDK ChatEvent\n * format. Returns an array because some rows (malformed / ``done`` sentinels)\n * map to zero events.\n */\nexport function mapSseToChat(raw: RawSseEvent): ChatEvent[] {\n const wire = toWireEvent(raw);\n if (!wire) return [];\n const event = translateWireEvent(wire);\n return event ? [event] : [];\n}\n\n/**\n * Replay persisted SSE events through the provided handler, interleaving\n * user messages from session.messages at the first message_start of each\n * turn (user messages aren't persisted in job_events).\n */\nexport function replayEvents(\n sseEvents: RawSseEvent[],\n userMessages: { role: string; content: string }[],\n handleEvent: (event: ChatEvent) => void,\n addBlock: (block: { type: \"user\"; id: string; content: string }) => void,\n): void {\n const userMsgs = userMessages.filter((m) => m.role === \"user\");\n let userIdx = 0;\n let expectingUserMessage = true;\n\n for (const raw of sseEvents) {\n const type = (raw.data.type as string) || raw.event;\n\n if (type === \"message_stop\") {\n expectingUserMessage = true;\n }\n\n if (\n type === \"message_start\" &&\n expectingUserMessage &&\n userIdx < userMsgs.length\n ) {\n const userMsg = userMsgs[userIdx]!;\n addBlock({\n type: \"user\",\n id: `replay_user_${userIdx}`,\n content: userMsg.content,\n });\n userIdx++;\n expectingUserMessage = false;\n }\n\n for (const ce of mapSseToChat(raw)) {\n handleEvent(ce);\n }\n }\n}\n","// =============================================================================\n// Embedded Resource detection — protocol-agnostic\n// =============================================================================\n//\n// The backend can emit rich UI surfaces (A2UI, and future protocols) by\n// wrapping a tool result in an MCP-style embedded resource:\n//\n// {\n// \"_embedded_resource\": true,\n// \"mime_type\": \"application/json+a2ui\",\n// \"uri\": \"a2ui://surface/<id>\",\n// \"payload\": { ...protocol-specific... }\n// }\n//\n// The SDK stays protocol-agnostic: it exposes a detector/parser so\n// frontends can route the payload to a registered renderer for the\n// matching MIME type. The SDK itself never imports a renderer.\n// =============================================================================\n\n/**\n * Parsed shape of an MCP-style embedded resource, as emitted by the\n * backend's UI component tools (``render_surface``, ``update_surface``).\n */\nexport interface EmbeddedResource {\n /** IANA-style MIME type, e.g. \"application/json+a2ui\". */\n mimeType: string;\n /** Opaque URI (e.g. \"a2ui://surface/my-form\"). */\n uri: string;\n /** Protocol-specific payload — shape depends on ``mimeType``. */\n payload: Record<string, unknown>;\n}\n\n/**\n * Detect whether a value is an embedded resource wrapper. The check is\n * purposely loose — any object with ``_embedded_resource: true`` is\n * accepted, which matches the MCP convention and keeps the SDK\n * forward-compatible with future protocols.\n */\nexport function isEmbeddedResource(value: unknown): value is {\n _embedded_resource: true;\n mime_type?: string;\n uri?: string;\n payload?: Record<string, unknown>;\n} {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (value as { _embedded_resource?: unknown })._embedded_resource === true\n );\n}\n\n/**\n * Parse an embedded resource from arbitrary tool output. Returns\n * ``null`` when the value isn't an embedded resource or is malformed.\n *\n * Accepts either an object (the preferred wire format) or a JSON\n * string containing one (defense in depth — some transport layers\n * historically serialized tool results before sending).\n */\nexport function parseEmbeddedResource(value: unknown): EmbeddedResource | null {\n let candidate: unknown = value;\n if (typeof candidate === \"string\") {\n // Only try JSON.parse if it plausibly looks like a JSON object\n // starting with `{`. Avoids pathological input.\n const trimmed = candidate.trim();\n if (!trimmed.startsWith(\"{\")) return null;\n try {\n candidate = JSON.parse(trimmed);\n } catch {\n return null;\n }\n }\n if (!isEmbeddedResource(candidate)) return null;\n const mimeType = candidate.mime_type;\n const uri = candidate.uri;\n const payload = candidate.payload;\n if (typeof mimeType !== \"string\" || !mimeType) return null;\n if (typeof uri !== \"string\" || !uri) return null;\n if (!payload || typeof payload !== \"object\") return null;\n return {\n mimeType,\n uri,\n payload: payload as Record<string, unknown>,\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/utils.ts","../src/rate-limit.ts","../src/streaming.ts","../src/client.ts","../src/storage.ts","../src/tools.ts","../src/protocol-registry.ts","../src/translate.ts","../src/session.ts","../src/types.ts","../src/stream-manager.ts","../src/replay.ts","../src/embedded-resource.ts"],"sourcesContent":["export interface RateLimitErrorDetails {\n retryAfterSec?: number;\n resetAt?: number;\n scope?: string;\n policyId?: string;\n limit?: number;\n remaining?: number;\n requestId?: string;\n}\n\nexport class AstralformError extends Error {\n constructor(\n message: string,\n public code: string,\n ) {\n super(message);\n this.name = \"AstralformError\";\n }\n}\n\nexport class AuthenticationError extends AstralformError {\n constructor(message = \"Invalid or missing API key\") {\n super(message, \"authentication_error\");\n this.name = \"AuthenticationError\";\n }\n}\n\nexport class RateLimitError extends AstralformError {\n declare readonly retryAfterSec?: number;\n declare readonly resetAt?: number;\n declare readonly scope?: string;\n declare readonly policyId?: string;\n declare readonly limit?: number;\n declare readonly remaining?: number;\n declare readonly requestId?: string;\n\n constructor(\n message = \"Rate limit exceeded\",\n details: RateLimitErrorDetails = {},\n ) {\n super(message, \"rate_limit_error\");\n this.name = \"RateLimitError\";\n Object.assign(this, details);\n }\n}\n\nexport class LLMNotConfiguredError extends AstralformError {\n constructor(message = \"LLM provider not configured for this project\") {\n super(message, \"llm_not_configured\");\n this.name = \"LLMNotConfiguredError\";\n }\n}\n\nexport class ServerError extends AstralformError {\n constructor(message = \"Internal server error\") {\n super(message, \"server_error\");\n this.name = \"ServerError\";\n }\n}\n\nexport class ConnectionError extends AstralformError {\n constructor(message = \"Failed to connect to server\") {\n super(message, \"connection_error\");\n this.name = \"ConnectionError\";\n }\n}\n\nexport class StreamAbortedError extends AstralformError {\n constructor(message = \"Stream was aborted\") {\n super(message, \"stream_aborted\");\n this.name = \"StreamAbortedError\";\n }\n}\n","export function sanitizeErrorText(text: string): string {\n return text.slice(0, 500).replace(/Bearer\\s+\\S+/gi, \"Bearer [REDACTED]\");\n}\n\nexport function generateId(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n","import { RateLimitError, type RateLimitErrorDetails } from \"./errors.js\";\nimport { sanitizeErrorText } from \"./utils.js\";\n\nconst DEFAULT_MESSAGE = \"Rate limit exceeded\";\n\nfunction parseNumber(value: unknown): number | undefined {\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return value;\n }\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (!trimmed) {\n return undefined;\n }\n const parsed = Number(trimmed);\n if (Number.isFinite(parsed)) {\n return parsed;\n }\n }\n return undefined;\n}\n\nfunction parseString(value: unknown): string | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed ? trimmed : undefined;\n}\n\nfunction parseJsonObject(rawText: string): Record<string, unknown> | undefined {\n if (!rawText) {\n return undefined;\n }\n try {\n const parsed: unknown = JSON.parse(rawText);\n if (parsed && typeof parsed === \"object\") {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // Ignore parse errors and fall back to text-only handling.\n }\n return undefined;\n}\n\nfunction parseRetryAfterHeader(value: string | null): number | undefined {\n if (!value) {\n return undefined;\n }\n\n const numeric = parseNumber(value);\n if (numeric !== undefined) {\n return Math.max(0, Math.ceil(numeric));\n }\n\n const asDate = Date.parse(value);\n if (Number.isFinite(asDate)) {\n const diffMs = asDate - Date.now();\n return Math.max(0, Math.ceil(diffMs / 1000));\n }\n\n return undefined;\n}\n\nfunction parseResetTimestamp(value: unknown): number | undefined {\n const numeric = parseNumber(value);\n if (numeric !== undefined) {\n if (numeric > 1_000_000_000_000) {\n return Math.floor(numeric);\n }\n return Math.floor(numeric * 1000);\n }\n\n const asString = parseString(value);\n if (!asString) {\n return undefined;\n }\n\n const asDate = Date.parse(asString);\n if (Number.isFinite(asDate)) {\n return asDate;\n }\n return undefined;\n}\n\nfunction pickFirst<T>(\n payload: Record<string, unknown>,\n keys: string[],\n parser: (value: unknown) => T | undefined,\n): T | undefined {\n for (const key of keys) {\n const parsed = parser(payload[key]);\n if (parsed !== undefined) {\n return parsed;\n }\n }\n return undefined;\n}\n\nfunction buildRateLimitDetails(\n payload: Record<string, unknown>,\n headers?: Headers,\n): RateLimitErrorDetails {\n const headerRetryAfter = headers\n ? parseRetryAfterHeader(headers.get(\"retry-after\"))\n : undefined;\n const bodyRetryAfter = pickFirst(\n payload,\n [\"retry_after\", \"retryAfter\", \"retry_after_sec\", \"retryAfterSec\"],\n parseNumber,\n );\n\n const retryAfterSec = bodyRetryAfter ?? headerRetryAfter;\n\n const headerReset = headers\n ? parseResetTimestamp(\n headers.get(\"x-ratelimit-reset\") ?? headers.get(\"x-ratelimit-reset-at\"),\n )\n : undefined;\n const bodyReset = parseResetTimestamp(\n payload.reset_at ?? payload.resetAt ?? payload.reset,\n );\n\n const resetAt =\n bodyReset ??\n headerReset ??\n (retryAfterSec !== undefined\n ? Date.now() + retryAfterSec * 1000\n : undefined);\n\n const limit =\n pickFirst(payload, [\"limit\", \"rate_limit\", \"max\"], parseNumber) ??\n (headers ? parseNumber(headers.get(\"x-ratelimit-limit\")) : undefined);\n\n const remaining =\n pickFirst(payload, [\"remaining\", \"rate_limit_remaining\"], parseNumber) ??\n (headers ? parseNumber(headers.get(\"x-ratelimit-remaining\")) : undefined);\n\n const scope =\n pickFirst(payload, [\"scope\", \"limit_scope\"], parseString) ??\n (headers ? parseString(headers.get(\"x-ratelimit-scope\")) : undefined);\n\n const policyId =\n pickFirst(payload, [\"policy_id\", \"policyId\", \"policy\"], parseString) ??\n (headers\n ? parseString(\n headers.get(\"x-ratelimit-policy\") ??\n headers.get(\"x-ratelimit-policy-id\"),\n )\n : undefined);\n\n const requestId =\n pickFirst(payload, [\"request_id\", \"requestId\"], parseString) ??\n (headers\n ? parseString(\n headers.get(\"x-request-id\") ?? headers.get(\"x-correlation-id\"),\n )\n : undefined);\n\n return {\n retryAfterSec,\n resetAt,\n scope,\n policyId,\n limit,\n remaining,\n requestId,\n };\n}\n\nexport function createRateLimitErrorFromPayload(\n payload: Record<string, unknown>,\n fallbackMessage = DEFAULT_MESSAGE,\n): RateLimitError {\n const message = pickFirst(\n payload,\n [\"message\", \"error_description\"],\n parseString,\n );\n return new RateLimitError(\n message ?? fallbackMessage,\n buildRateLimitDetails(payload),\n );\n}\n\nexport function createRateLimitErrorFromHttp(\n response: Response,\n rawText: string,\n): RateLimitError {\n const payload = parseJsonObject(rawText) ?? {};\n const details = buildRateLimitDetails(payload, response.headers);\n\n const sanitizedText = sanitizeErrorText(rawText);\n const message =\n pickFirst(payload, [\"message\", \"error_description\"], parseString) ??\n (sanitizedText || DEFAULT_MESSAGE);\n\n return new RateLimitError(message, details);\n}\n","import {\n AuthenticationError,\n ConnectionError,\n ServerError,\n StreamAbortedError,\n} from \"./errors.js\";\nimport { createRateLimitErrorFromHttp } from \"./rate-limit.js\";\nimport type { ChatStreamEvent, StreamJobSSEOptions } from \"./types.js\";\nimport { sanitizeErrorText } from \"./utils.js\";\n\n/**\n * GET-based SSE stream for job events.\n */\nexport async function* streamJobSSE(\n options: StreamJobSSEOptions,\n): AsyncGenerator<ChatStreamEvent> {\n const { url, headers, signal, fetchFn } = options;\n\n let response: Response;\n try {\n response = await fetchFn(url, {\n method: \"GET\",\n headers,\n signal,\n });\n } catch (err) {\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw new StreamAbortedError();\n }\n throw new ConnectionError(\n err instanceof Error ? err.message : \"Failed to connect\",\n );\n }\n\n if (!response.ok) {\n const rawText = await response.text().catch(() => \"\");\n const text = rawText ? sanitizeErrorText(rawText) : \"\";\n switch (response.status) {\n case 401:\n throw new AuthenticationError();\n case 429:\n throw createRateLimitErrorFromHttp(response, rawText);\n default:\n throw new ServerError(text || `HTTP ${response.status}`);\n }\n }\n\n if (!response.body) {\n throw new ConnectionError(\"Response body is null\");\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let currentEvent = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (line.startsWith(\"event: \")) {\n currentEvent = line.slice(7).trim();\n } else if (line.startsWith(\"data: \")) {\n const data = line.slice(6);\n if (data === \"[DONE]\") {\n return;\n }\n yield { event: currentEvent || \"message\", data };\n }\n if (line === \"\") {\n currentEvent = \"\";\n }\n }\n }\n } catch (err) {\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw new StreamAbortedError();\n }\n throw err;\n } finally {\n reader.releaseLock();\n }\n}\n","import { AuthenticationError, ConnectionError, ServerError } from \"./errors.js\";\nimport { createRateLimitErrorFromHttp } from \"./rate-limit.js\";\nimport { streamJobSSE } from \"./streaming.js\";\nimport { sanitizeErrorText } from \"./utils.js\";\nimport type {\n ActiveJob,\n AgentInfo,\n AstralformApiKeyConfig,\n AstralformConfig,\n ChatStreamEvent,\n ChatStreamRequest,\n ConversationAsset,\n ConversationEvent,\n Conversation,\n FeedbackRequest,\n FeedbackResponse,\n JobCreateResponse,\n JobStatus,\n JobSummary,\n Message,\n MyToolGrantsPage,\n ProjectStatus,\n ProjectSummary,\n SkillInfo,\n TeamSummary,\n ToolApprovalRequest,\n ToolResultRequest,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.astralform.ai\";\n\nfunction validateBaseURL(url: string): string {\n const cleaned = url.replace(/\\/+$/, \"\");\n try {\n const parsed = new URL(cleaned);\n if (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") {\n throw new Error(\n `Invalid baseURL protocol \"${parsed.protocol}\" - only http: and https: are allowed`,\n );\n }\n return parsed.origin + parsed.pathname.replace(/\\/+$/, \"\");\n } catch (err) {\n if (err instanceof Error && err.message.includes(\"Invalid baseURL\")) {\n throw err;\n }\n throw new Error(`Invalid baseURL: \"${cleaned}\" is not a valid URL`);\n }\n}\n\nfunction isApiKeyConfig(\n config: AstralformConfig,\n): config is AstralformApiKeyConfig {\n return \"apiKey\" in config;\n}\n\n/** Discriminates between the two auth modes the client supports. */\ntype AuthMode =\n | { kind: \"api_key\"; apiKey: string; userId: string }\n | {\n kind: \"user_token\";\n accessToken: string;\n /** Null until the user picks a project; account-scoped calls still work. */\n projectId: string | null;\n /** Optional end-user override. When present, sent as X-End-User-ID. */\n endUserId: string | null;\n };\n\nexport class AstralformClient {\n private readonly baseURL: string;\n private readonly fetchFn: typeof globalThis.fetch;\n /**\n * Auth state is mutable so callers can rotate access tokens or switch\n * project context without re-instantiating the client. API-key mode is\n * effectively immutable in practice but uses the same shape for uniformity.\n */\n private auth: AuthMode;\n\n constructor(config: AstralformConfig) {\n if (isApiKeyConfig(config)) {\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new Error(\"apiKey is required and must be a non-empty string\");\n }\n if (!config.userId || typeof config.userId !== \"string\") {\n throw new Error(\"userId is required in API-key mode\");\n }\n this.auth = {\n kind: \"api_key\",\n apiKey: config.apiKey,\n userId: config.userId,\n };\n } else {\n if (!config.accessToken || typeof config.accessToken !== \"string\") {\n throw new Error(\n \"accessToken is required and must be a non-empty string in user-token mode\",\n );\n }\n // projectId is optional — a pre-pick client (right after login) can\n // still hit account-scoped routes like listTeams(). Project-scoped\n // routes will 4xx until one is set via updateProjectId().\n const projectId =\n typeof config.projectId === \"string\" && config.projectId.length > 0\n ? config.projectId\n : null;\n this.auth = {\n kind: \"user_token\",\n accessToken: config.accessToken,\n projectId,\n endUserId:\n typeof config.endUserId === \"string\" && config.endUserId.length > 0\n ? config.endUserId\n : null,\n };\n }\n\n this.baseURL = validateBaseURL(config.baseURL ?? DEFAULT_BASE_URL);\n this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n /**\n * Replace the current OIDC access token without reconstructing the client.\n * Use after refreshing via the host's token manager (e.g., Supabase JS SDK).\n * Throws if the client was created in API-key mode.\n */\n updateAccessToken(accessToken: string): void {\n if (this.auth.kind !== \"user_token\") {\n throw new Error(\"updateAccessToken is only valid in user-token mode\");\n }\n if (!accessToken || typeof accessToken !== \"string\") {\n throw new Error(\"accessToken must be a non-empty string\");\n }\n this.auth = { ...this.auth, accessToken };\n }\n\n /**\n * Swap the active project for a user-token client. The backend verifies the\n * current developer has access to the new project; a 403 comes back if not.\n */\n updateProjectId(projectId: string): void {\n if (this.auth.kind !== \"user_token\") {\n throw new Error(\"updateProjectId is only valid in user-token mode\");\n }\n if (!projectId || typeof projectId !== \"string\") {\n throw new Error(\"projectId must be a non-empty string\");\n }\n this.auth = { ...this.auth, projectId };\n }\n\n /**\n * Set (or clear) the end-user override for user-token mode.\n *\n * Pass `null` or an empty string to clear — subsequent requests go\n * back to scoping against the developer's own identity. Throws if\n * called in API-key mode, where end-user context already travels via\n * the constructor's `userId` field.\n */\n updateEndUserId(endUserId: string | null): void {\n if (this.auth.kind !== \"user_token\") {\n throw new Error(\"updateEndUserId is only valid in user-token mode\");\n }\n const normalized =\n typeof endUserId === \"string\" && endUserId.length > 0 ? endUserId : null;\n this.auth = { ...this.auth, endUserId: normalized };\n }\n\n /** Current end-user override in user-token mode, or `null` if unset. */\n get endUserId(): string | null {\n return this.auth.kind === \"user_token\" ? this.auth.endUserId : null;\n }\n\n /**\n * Active project for user-token mode, or `null` if pre-pick (client\n * was constructed without one). For API-key mode the project is baked\n * into the key, so this getter returns `null` there too — use\n * `authMode` to disambiguate.\n */\n get projectId(): string | null {\n return this.auth.kind === \"user_token\" ? this.auth.projectId : null;\n }\n\n /** Which auth mode this client was constructed with. */\n get authMode(): \"api_key\" | \"user_token\" {\n return this.auth.kind;\n }\n\n /**\n * Authorization + identity headers for the current auth mode, without\n * `Content-Type`. Suitable for JSON requests (paired with the JSON header\n * in the `headers` getter) and for multipart uploads where the browser\n * must set its own `Content-Type` boundary.\n */\n private get authHeaders(): Record<string, string> {\n if (this.auth.kind === \"api_key\") {\n return {\n Authorization: `Bearer ${this.auth.apiKey}`,\n \"X-End-User-ID\": this.auth.userId,\n };\n }\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.auth.accessToken}`,\n };\n if (this.auth.projectId) {\n headers[\"X-Project-ID\"] = this.auth.projectId;\n }\n if (this.auth.endUserId) {\n headers[\"X-End-User-ID\"] = this.auth.endUserId;\n }\n return headers;\n }\n\n private get headers(): Record<string, string> {\n return {\n ...this.authHeaders,\n \"Content-Type\": \"application/json\",\n };\n }\n\n private async request(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<Response> {\n const response = await this.fetchFn(`${this.baseURL}${path}`, {\n method,\n headers: this.headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n }).catch((err) => {\n throw new ConnectionError(\n err instanceof Error ? err.message : \"Failed to connect\",\n );\n });\n await this.handleError(response);\n return response;\n }\n\n async get<T>(path: string): Promise<T> {\n const response = await this.request(\"GET\", path);\n return response.json() as Promise<T>;\n }\n\n async post<T>(path: string, body: unknown): Promise<T> {\n const response = await this.request(\"POST\", path, body);\n return response.json() as Promise<T>;\n }\n\n private async del(path: string): Promise<void> {\n await this.request(\"DELETE\", path);\n }\n\n private async handleError(response: Response): Promise<void> {\n if (response.ok) return;\n const text = await response.text().catch(() => \"\");\n switch (response.status) {\n case 401:\n throw new AuthenticationError();\n case 429:\n throw createRateLimitErrorFromHttp(response, text);\n default: {\n const safeText = text ? sanitizeErrorText(text) : \"\";\n throw new ServerError(safeText || `HTTP ${response.status}`);\n }\n }\n }\n\n // --- REST Methods ---\n\n async getHealth(): Promise<{\n status: string;\n version: string;\n ollama_connected: boolean;\n }> {\n return this.get(\"/v1/health\");\n }\n\n async getProjectStatus(): Promise<ProjectStatus> {\n const raw = await this.get<{\n is_ready: boolean;\n llm_configured: boolean;\n llm_provider?: string;\n llm_model?: string;\n message: string;\n ui_components?: {\n enabled?: boolean;\n protocol?: string | null;\n mime_type?: string | null;\n };\n }>(\"/v1/project/status\");\n const ui = raw.ui_components ?? {};\n return {\n isReady: raw.is_ready,\n llmConfigured: raw.llm_configured,\n llmProvider: raw.llm_provider,\n llmModel: raw.llm_model,\n message: raw.message,\n uiComponents: {\n enabled: Boolean(ui.enabled),\n protocol: ui.protocol ?? null,\n mimeType: ui.mime_type ?? null,\n },\n };\n }\n\n async getConversations(limit = 50, offset = 0): Promise<Conversation[]> {\n const safeLimit = Math.max(1, Math.min(200, Math.floor(Number(limit))));\n const safeOffset = Math.max(0, Math.floor(Number(offset)));\n const raw = await this.get<\n {\n id: string;\n title: string;\n message_count: number;\n created_at: string;\n updated_at: string;\n }[]\n >(`/v1/conversations?limit=${safeLimit}&offset=${safeOffset}`);\n return raw.map((c) => ({\n id: c.id,\n title: c.title,\n messageCount: c.message_count,\n createdAt: c.created_at,\n updatedAt: c.updated_at,\n }));\n }\n\n async getMessages(conversationId: string): Promise<Message[]> {\n const raw = await this.get<\n {\n id: string;\n conversation_id: string;\n role: \"user\" | \"assistant\" | \"system\";\n content: string;\n parent_id?: string;\n created_at: string;\n }[]\n >(`/v1/conversations/${encodeURIComponent(conversationId)}/messages`);\n return raw.map((m) => ({\n id: m.id,\n conversationId: m.conversation_id,\n role: m.role,\n content: m.content,\n parentId: m.parent_id,\n status: \"complete\" as const,\n createdAt: m.created_at,\n }));\n }\n\n async deleteConversation(id: string): Promise<void> {\n await this.del(`/v1/conversations/${encodeURIComponent(id)}`);\n }\n\n async getAgents(): Promise<AgentInfo[]> {\n const raw = await this.get<\n {\n name: string;\n display_name: string;\n description: string;\n is_orchestrator: boolean;\n is_enabled: boolean;\n avatar_url?: string;\n }[]\n >(\"/v1/agents\");\n return raw.map((a) => ({\n name: a.name,\n displayName: a.display_name,\n description: a.description,\n isOrchestrator: a.is_orchestrator,\n isEnabled: a.is_enabled,\n avatarUrl: a.avatar_url,\n }));\n }\n\n async getSkills(): Promise<SkillInfo[]> {\n const raw = await this.get<\n {\n name: string;\n display_name: string;\n description: string;\n is_enabled: boolean;\n }[]\n >(\"/v1/skills\");\n return raw.map((s) => ({\n name: s.name,\n displayName: s.display_name,\n description: s.description,\n isEnabled: s.is_enabled,\n }));\n }\n\n async getConversationEvents(\n conversationId: string,\n jobId?: string,\n ): Promise<ConversationEvent[]> {\n let url = `/v1/conversations/${encodeURIComponent(conversationId)}/events`;\n if (jobId) url += `?job_id=${encodeURIComponent(jobId)}`;\n return this.get(url);\n }\n\n async submitToolResult(request: ToolResultRequest): Promise<void> {\n await this.post(\"/v1/tool-result\", request);\n }\n\n async submitToolApproval(request: ToolApprovalRequest): Promise<void> {\n await this.post(\"/v1/tool-approval\", request);\n }\n\n // --- End-user tool-permission self-service ---\n\n /**\n * List the current end user's own remembered tool-permission grants.\n * Only `conversation`/`always` grants exist (`once` is never persisted).\n * Paginated via `limit` (default 100, max 200) / `offset`; `total` lets you\n * page through all of them.\n */\n async getMyToolPermissions(options?: {\n limit?: number;\n offset?: number;\n }): Promise<MyToolGrantsPage> {\n const params = new URLSearchParams();\n if (options?.limit != null) {\n const safeLimit = Math.max(\n 1,\n Math.min(200, Math.floor(Number(options.limit))),\n );\n params.set(\"limit\", String(safeLimit));\n }\n if (options?.offset != null) {\n const safeOffset = Math.max(0, Math.floor(Number(options.offset)));\n params.set(\"offset\", String(safeOffset));\n }\n const qs = params.toString();\n const raw = await this.get<{\n grants: {\n id: string;\n tool_name: string;\n decision: \"allow\" | \"deny\";\n scope: \"conversation\" | \"always\";\n conversation_id: string | null;\n created_at: string;\n }[];\n total: number;\n limit: number;\n offset: number;\n }>(`/v1/me/tool-permissions${qs ? `?${qs}` : \"\"}`);\n return {\n grants: raw.grants.map((g) => ({\n id: g.id,\n toolName: g.tool_name,\n decision: g.decision,\n scope: g.scope,\n conversationId: g.conversation_id,\n createdAt: g.created_at,\n })),\n total: raw.total,\n limit: raw.limit,\n offset: raw.offset,\n };\n }\n\n /**\n * Revoke one of the current end user's remembered grants by id. The agent\n * will ask again the next time that tool is used.\n */\n async revokeToolPermission(id: string): Promise<void> {\n await this.del(`/v1/me/tool-permissions/${encodeURIComponent(id)}`);\n }\n\n // --- Conversation Assets ---\n\n private mapAsset(raw: Record<string, unknown>): ConversationAsset {\n return {\n id: raw.id as string,\n kind: raw.kind as \"upload\" | \"output\",\n originalName: raw.original_name as string,\n mediaType: raw.media_type as string,\n sizeBytes: raw.size_bytes as number,\n workspacePath: raw.workspace_path as string | undefined,\n sourceMessageId: raw.source_message_id as string | undefined,\n agentName: raw.agent_name as string | undefined,\n createdAt: raw.created_at as string,\n };\n }\n\n async uploadFile(\n conversationId: string,\n file: Blob,\n filename?: string,\n ): Promise<ConversationAsset> {\n const formData = new FormData();\n formData.append(\"file\", file, filename);\n\n const response = await this.fetchFn(\n `${this.baseURL}/v1/conversations/${encodeURIComponent(conversationId)}/uploads`,\n {\n method: \"POST\",\n headers: this.authHeaders,\n body: formData,\n },\n ).catch((err) => {\n throw new ConnectionError(\n err instanceof Error ? err.message : \"Failed to connect\",\n );\n });\n await this.handleError(response);\n const raw = await response.json();\n return this.mapAsset(raw as Record<string, unknown>);\n }\n\n async listUploads(conversationId: string): Promise<ConversationAsset[]> {\n const raw = await this.get<Record<string, unknown>[]>(\n `/v1/conversations/${encodeURIComponent(conversationId)}/uploads`,\n );\n return raw.map((r) => this.mapAsset(r));\n }\n\n async listOutputs(conversationId: string): Promise<ConversationAsset[]> {\n const raw = await this.get<Record<string, unknown>[]>(\n `/v1/conversations/${encodeURIComponent(conversationId)}/outputs`,\n );\n return raw.map((r) => this.mapAsset(r));\n }\n\n // --- Account-scoped discovery (user-token mode) ---\n //\n // Lets a signed-in user pick which team/project they want to act on.\n // Backend gates these on OIDC user context (no X-Project-ID required) —\n // sending them in API-key mode yields 401.\n\n async listTeams(): Promise<TeamSummary[]> {\n const raw = await this.get<\n Array<{\n id: string;\n name: string;\n slug: string;\n is_default: boolean;\n role: string;\n }>\n >(\"/v1/teams\");\n return raw.map((t) => ({\n id: t.id,\n name: t.name,\n slug: t.slug,\n isDefault: t.is_default,\n role: t.role,\n }));\n }\n\n async listProjects(teamId: string): Promise<ProjectSummary[]> {\n const raw = await this.get<\n Array<{\n id: string;\n name: string;\n team_id: string;\n created_at: string;\n updated_at: string;\n }>\n >(`/v1/teams/${encodeURIComponent(teamId)}/projects`);\n return raw.map((p) => ({\n id: p.id,\n name: p.name,\n teamId: p.team_id,\n createdAt: p.created_at,\n updatedAt: p.updated_at,\n }));\n }\n\n // --- Jobs API ---\n\n async createJob(request: ChatStreamRequest): Promise<JobCreateResponse> {\n return this.post<JobCreateResponse>(\"/v1/jobs\", request);\n }\n\n async *streamJobEvents(\n jobId: string,\n afterSeq = -1,\n signal?: AbortSignal,\n ): AsyncGenerator<ChatStreamEvent> {\n const url = `${this.baseURL}/v1/jobs/${encodeURIComponent(jobId)}/events?after=${afterSeq}`;\n yield* streamJobSSE({\n url,\n headers: this.headers,\n signal,\n fetchFn: this.fetchFn,\n });\n }\n\n async cancelJob(jobId: string): Promise<void> {\n await this.post(`/v1/jobs/${encodeURIComponent(jobId)}/cancel`, {});\n }\n\n async getJob(jobId: string): Promise<JobStatus> {\n const raw = await this.get<{\n job_id: string;\n status: string;\n created_at?: string | null;\n started_at?: string | null;\n completed_at?: string | null;\n error_message?: string | null;\n input_tokens?: number;\n output_tokens?: number;\n }>(`/v1/jobs/${encodeURIComponent(jobId)}`);\n return {\n jobId: raw.job_id,\n status: raw.status,\n createdAt: raw.created_at ?? null,\n startedAt: raw.started_at ?? null,\n completedAt: raw.completed_at ?? null,\n errorMessage: raw.error_message ?? null,\n inputTokens: raw.input_tokens ?? 0,\n outputTokens: raw.output_tokens ?? 0,\n };\n }\n\n async submitFeedback(\n jobId: string,\n request: FeedbackRequest,\n ): Promise<FeedbackResponse> {\n const body: { rating: 1 | -1; comment?: string } = {\n rating: request.rating,\n };\n if (request.comment != null) body.comment = request.comment;\n const raw = await this.post<{\n id: string;\n job_id: string;\n rating: number;\n comment: string | null;\n created_at: string;\n }>(`/v1/jobs/${encodeURIComponent(jobId)}/feedback`, body);\n return {\n id: raw.id,\n jobId: raw.job_id,\n rating: raw.rating,\n comment: raw.comment,\n createdAt: raw.created_at,\n };\n }\n\n async getActiveJob(conversationId: string): Promise<ActiveJob> {\n const raw = await this.get<{\n job_id: string | null;\n status: string;\n }>(`/v1/conversations/${encodeURIComponent(conversationId)}/active-job`);\n return {\n jobId: raw.job_id ?? null,\n status: raw.status,\n };\n }\n\n async listJobs(conversationId: string): Promise<JobSummary[]> {\n const raw = await this.get<\n {\n job_id: string;\n status: string;\n replaces_job_id?: string | null;\n response_content?: Record<string, unknown> | null;\n metrics?: Record<string, unknown> | null;\n created_at?: string | null;\n }[]\n >(`/v1/conversations/${encodeURIComponent(conversationId)}/jobs`);\n return raw.map((j) => ({\n jobId: j.job_id,\n status: j.status,\n replacesJobId: j.replaces_job_id ?? null,\n responseContent: j.response_content ?? null,\n metrics: j.metrics ?? null,\n createdAt: j.created_at ?? null,\n }));\n }\n}\n","import type { Conversation, Message } from \"./types.js\";\n\nexport interface ChatStorage {\n fetchConversations(): Promise<Conversation[]>;\n fetchConversation(id: string): Promise<Conversation | null>;\n createConversation(id: string, title: string): Promise<Conversation>;\n updateConversationTitle(id: string, title: string): Promise<void>;\n deleteConversation(id: string): Promise<void>;\n fetchMessages(conversationId: string): Promise<Message[]>;\n addMessage(message: Message, conversationId: string): Promise<void>;\n updateMessageStatus(id: string, status: Message[\"status\"]): Promise<void>;\n deleteMessage(id: string): Promise<void>;\n}\n\nexport class InMemoryStorage implements ChatStorage {\n private conversations = new Map<string, Conversation>();\n private messages = new Map<string, Message[]>();\n\n async fetchConversations(): Promise<Conversation[]> {\n return Array.from(this.conversations.values()).sort(\n (a, b) =>\n new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(),\n );\n }\n\n async fetchConversation(id: string): Promise<Conversation | null> {\n return this.conversations.get(id) ?? null;\n }\n\n async createConversation(id: string, title: string): Promise<Conversation> {\n const now = new Date().toISOString();\n const conversation: Conversation = {\n id,\n title,\n messageCount: 0,\n createdAt: now,\n updatedAt: now,\n };\n this.conversations.set(id, conversation);\n this.messages.set(id, []);\n return conversation;\n }\n\n async updateConversationTitle(id: string, title: string): Promise<void> {\n const conv = this.conversations.get(id);\n if (conv) {\n conv.title = title;\n conv.updatedAt = new Date().toISOString();\n }\n }\n\n async deleteConversation(id: string): Promise<void> {\n this.conversations.delete(id);\n this.messages.delete(id);\n }\n\n async fetchMessages(conversationId: string): Promise<Message[]> {\n return this.messages.get(conversationId) ?? [];\n }\n\n async addMessage(message: Message, conversationId: string): Promise<void> {\n const msgs = this.messages.get(conversationId) ?? [];\n msgs.push(message);\n this.messages.set(conversationId, msgs);\n\n const conv = this.conversations.get(conversationId);\n if (conv) {\n conv.messageCount = msgs.length;\n conv.updatedAt = new Date().toISOString();\n }\n }\n\n async updateMessageStatus(\n id: string,\n status: Message[\"status\"],\n ): Promise<void> {\n for (const msgs of this.messages.values()) {\n const msg = msgs.find((m) => m.id === id);\n if (msg) {\n msg.status = status;\n return;\n }\n }\n }\n\n async deleteMessage(id: string): Promise<void> {\n for (const [convId, msgs] of this.messages.entries()) {\n const idx = msgs.findIndex((m) => m.id === id);\n if (idx !== -1) {\n msgs.splice(idx, 1);\n const conv = this.conversations.get(convId);\n if (conv) {\n conv.messageCount = msgs.length;\n }\n return;\n }\n }\n }\n}\n","import type { ToolCallRequest, ToolDefinition, ToolResult } from \"./types.js\";\n\nexport type ToolHandler = (args: Record<string, unknown>) => Promise<string>;\n\ninterface RegisteredTool {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n handler: ToolHandler;\n}\n\n/** Validates tool name: alphanumeric, hyphens, underscores, dots only */\nconst TOOL_NAME_PATTERN = /^[a-zA-Z0-9_.\\-]+$/;\n\n/** Strips prototype-polluting keys from an object */\nfunction sanitizeArgs(args: Record<string, unknown>): Record<string, unknown> {\n const clean: Record<string, unknown> = Object.create(null);\n for (const key of Object.keys(args)) {\n if (key === \"__proto__\" || key === \"constructor\" || key === \"prototype\") {\n continue;\n }\n clean[key] = args[key];\n }\n return clean;\n}\n\nexport class ToolRegistry {\n private tools = new Map<string, RegisteredTool>();\n\n registerTool(\n name: string,\n description: string,\n inputSchema: Record<string, unknown>,\n handler: ToolHandler,\n ): void {\n if (!name || !TOOL_NAME_PATTERN.test(name)) {\n throw new Error(\n `Invalid tool name \"${name}\" - must match ${TOOL_NAME_PATTERN}`,\n );\n }\n if (name.length > 256) {\n throw new Error(\"Tool name must be 256 characters or fewer\");\n }\n this.tools.set(name, { name, description, inputSchema, handler });\n }\n\n unregisterTool(name: string): boolean {\n return this.tools.delete(name);\n }\n\n hasTool(name: string): boolean {\n return this.tools.has(name);\n }\n\n async executeTool(request: ToolCallRequest): Promise<ToolResult> {\n const tool = this.tools.get(request.toolName);\n if (!tool) {\n return {\n call_id: request.callId,\n tool_name: request.toolName,\n result: `Tool \"${request.toolName}\" not found`,\n is_error: true,\n };\n }\n\n try {\n const result = await tool.handler(sanitizeArgs(request.arguments));\n return {\n call_id: request.callId,\n tool_name: request.toolName,\n result,\n is_error: false,\n };\n } catch (err) {\n return {\n call_id: request.callId,\n tool_name: request.toolName,\n result: err instanceof Error ? err.message : String(err),\n is_error: true,\n };\n }\n }\n\n getManifest(): ToolDefinition[] {\n return Array.from(this.tools.values()).map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.inputSchema,\n }));\n }\n\n getToolNames(): string[] {\n return Array.from(this.tools.keys());\n }\n\n clear(): void {\n this.tools.clear();\n }\n}\n","// =============================================================================\n// Protocol adapter registry — pluggable UI protocol layer\n// =============================================================================\n//\n// The SDK is framework-agnostic: it never renders. But when the backend\n// emits an MCP-style embedded resource (A2UI today, other protocols\n// tomorrow) the consumer needs to hand the payload to a renderer. This\n// file defines the contract between those two sides:\n//\n// • `ProtocolAdapter` — an opaque, framework-specific handle that\n// claims a MIME type. The SDK stores it; the consumer narrows the\n// type when reading it back out.\n//\n// • `ProtocolRegistry` — a MIME-keyed map of adapters. One lives on\n// each `ChatSession` so its lifecycle matches the session: clearing\n// on disconnect, swapping on reconnect with a different project.\n//\n// The consumer decides _whether_ to register an adapter — typically by\n// consulting `session.projectStatus.uiComponents` after `connect()`.\n// The SDK never auto-registers anything; it just stores what it's told.\n// =============================================================================\n\n/**\n * Minimal adapter contract. Frontends extend this with a `render()`\n * method (or equivalent) returning their framework's view type.\n */\nexport interface ProtocolAdapter {\n /** IANA-style MIME type this adapter handles (e.g. ``application/json+a2ui``). */\n readonly mimeType: string;\n}\n\n/**\n * MIME-keyed adapter map, generic on the adapter subtype so consumers\n * can register richer shapes without casting on every read.\n */\nexport class ProtocolRegistry<T extends ProtocolAdapter = ProtocolAdapter> {\n private adapters = new Map<string, T>();\n\n /** Register or replace the adapter for a MIME type. */\n register(adapter: T): void {\n this.adapters.set(adapter.mimeType, adapter);\n }\n\n /** Remove the adapter for a MIME type. No-op if not registered. */\n unregister(mimeType: string): void {\n this.adapters.delete(mimeType);\n }\n\n /** Returns the adapter for a MIME type, or ``null`` if none is registered. */\n get(mimeType: string): T | null {\n return this.adapters.get(mimeType) ?? null;\n }\n\n has(mimeType: string): boolean {\n return this.adapters.has(mimeType);\n }\n\n /** Drop every adapter. Called when a session disconnects. */\n clear(): void {\n this.adapters.clear();\n }\n\n listMimeTypes(): string[] {\n return Array.from(this.adapters.keys());\n }\n}\n","// =============================================================================\n// Wire → ChatEvent translation\n//\n// Single source of truth for the pure translation from backend wire types\n// (snake_case, mirroring Pydantic) to consumer-facing ChatEvents (camelCase).\n// Shared between the live session loop and the persisted-event replay path,\n// so adding a new event type only requires updating one place.\n// =============================================================================\n\nimport type {\n BlockDeltaPayload,\n ChatEvent,\n WireBlockDeltaPayload,\n WireEvent,\n} from \"./types.js\";\nimport type { MemoryRecord, TodoItem } from \"./custom-events.js\";\n\n// --- Delta channels ---\n\n/**\n * Translate a WireBlockDeltaPayload into the consumer-facing shape. Returns\n * ``null`` for unknown channels so forward-compatible backends can add new\n * ones without breaking old clients.\n */\nexport function translateDelta(\n wire: WireBlockDeltaPayload,\n): BlockDeltaPayload | null {\n switch (wire.channel) {\n case \"text\":\n return { channel: \"text\", text: wire.text };\n case \"thinking\":\n return { channel: \"thinking\", text: wire.text };\n case \"signature\":\n return { channel: \"signature\", signature: wire.signature };\n case \"input\":\n return { channel: \"input\", partialJson: wire.partial_json };\n case \"input_arg\":\n return {\n channel: \"inputArg\",\n argName: wire.arg_name,\n text: wire.text,\n };\n case \"output\":\n return { channel: \"output\", stream: wire.stream, chunk: wire.chunk };\n case \"status\":\n return {\n channel: \"status\",\n status: wire.status,\n note: wire.note,\n };\n default:\n return null;\n }\n}\n\n// --- Custom events ---\n\nfunction translateAgentIdentity(raw: Record<string, unknown>) {\n return {\n name: (raw.name as string) ?? \"\",\n displayName: (raw.display_name as string | null) ?? null,\n avatarUrl: (raw.avatar_url as string | null) ?? null,\n description: (raw.description as string | null) ?? null,\n };\n}\n\n/**\n * Translate a wire custom event (``{type: \"custom\", name, data}``) into a\n * typed ChatEvent. Unknown names fall through to the generic ``custom``\n * passthrough so consumers can still observe future backends.\n */\nexport function translateCustomEvent(\n name: string,\n data: Record<string, unknown>,\n): ChatEvent {\n switch (name) {\n case \"user_message\":\n return {\n type: \"user_message\",\n content: (data.content as string) ?? \"\",\n createdAt: data.created_at as number | undefined,\n };\n case \"title_generated\":\n return {\n type: \"title_generated\",\n title: (data.title as string) ?? \"\",\n };\n case \"todo_update\":\n return {\n type: \"todo_update\",\n todos: (data.todos as TodoItem[]) ?? [],\n };\n case \"context_update\":\n return {\n type: \"context_update\",\n context: (data.context as Record<string, unknown>) ?? {},\n phase: (data.phase as string | null) ?? null,\n updatedAt: (data.updated_at as number | null) ?? null,\n };\n case \"subagent_start\":\n return {\n type: \"subagent_start\",\n agent: translateAgentIdentity(\n (data.agent as Record<string, unknown>) ?? {},\n ),\n taskCallId: (data.task_call_id as string | null) ?? null,\n };\n case \"subagent_stop\":\n return {\n type: \"subagent_stop\",\n agent: translateAgentIdentity(\n (data.agent as Record<string, unknown>) ?? {},\n ),\n taskCallId: (data.task_call_id as string | null) ?? null,\n };\n case \"context_warning\":\n return {\n type: \"context_warning\",\n severity: (data.severity as string) ?? \"warning\",\n utilizationPct: (data.utilization_pct as number) ?? 0,\n remainingTokens: (data.remaining_tokens as number) ?? 0,\n windowTokens: (data.window_tokens as number) ?? 0,\n inputTokens: (data.input_tokens as number) ?? 0,\n message: (data.message as string) ?? \"\",\n };\n case \"memory_recall\":\n return {\n type: \"memory_recall\",\n memories: (data.memories as MemoryRecord[] | undefined) ?? [],\n };\n case \"memory_update\":\n return {\n type: \"memory_update\",\n action: (data.action as string) ?? \"\",\n memoryId: (data.memory_id as string | null) ?? null,\n key: (data.key as string | null) ?? null,\n namespace: (data.namespace as string | null) ?? null,\n };\n case \"desktop_stream\":\n return {\n type: \"desktop_stream\",\n url: (data.url as string) ?? \"\",\n sandboxId: (data.sandbox_id as string | null) ?? null,\n };\n case \"attachment_staged\":\n return {\n type: \"attachment_staged\",\n attachmentId: (data.attachment_id as string) ?? \"\",\n filename: (data.filename as string) ?? \"\",\n contentType: (data.content_type as string | null) ?? null,\n sizeBytes: (data.size_bytes as number | null) ?? null,\n };\n case \"workspace_ready\":\n return {\n type: \"workspace_ready\",\n sandboxId: (data.sandbox_id as string) ?? \"\",\n workspacePath: (data.workspace_path as string | null) ?? null,\n };\n case \"asset_created\":\n return {\n type: \"asset_created\",\n assetId: (data.asset_id as string) ?? \"\",\n filename: (data.filename as string) ?? \"\",\n url: (data.url as string | null) ?? null,\n contentType: (data.content_type as string | null) ?? null,\n };\n case \"tool_approval_requested\":\n return {\n type: \"tool_approval_requested\",\n toolName: (data.tool_name as string) ?? \"\",\n callId: (data.call_id as string) ?? \"\",\n arguments: (data.arguments as Record<string, unknown>) ?? {},\n riskLevel: (data.risk_level as string | null) ?? null,\n reason: (data.reason as string | null) ?? null,\n };\n case \"tool_approval_granted\":\n return {\n type: \"tool_approval_granted\",\n toolName: (data.tool_name as string) ?? \"\",\n callId: (data.call_id as string) ?? \"\",\n };\n case \"tool_permission_denied\":\n return {\n type: \"tool_permission_denied\",\n toolName: (data.tool_name as string) ?? \"\",\n callId: (data.call_id as string) ?? \"\",\n reason: (data.reason as string | null) ?? null,\n deniedBy: (data.denied_by as string | null) ?? null,\n };\n case \"tool_harness_warning\":\n return {\n type: \"tool_harness_warning\",\n toolName: (data.tool_name as string) ?? \"\",\n callId: (data.call_id as string) ?? \"\",\n message: (data.message as string | null) ?? null,\n details: (data.details as Record<string, unknown> | null) ?? null,\n };\n case \"user_unavailable\":\n return {\n type: \"user_unavailable\",\n consecutiveTimeouts: (data.consecutive_timeouts as number) ?? 0,\n toolName: (data.tool_name as string | null) ?? null,\n };\n case \"prompt_suggestion\":\n return {\n type: \"prompt_suggestion\",\n suggestions: (data.suggestions as string[]) ?? [],\n };\n case \"state_changed\":\n return {\n type: \"state_changed\",\n state: (data.state as string) ?? \"\",\n };\n default:\n return { type: \"custom\", name, data };\n }\n}\n\n// --- Top-level wire events ---\n\n/**\n * Legacy-transport payload extractor for events that aren't wrapped in a\n * ``CustomEvent`` envelope. The backend's ``writer.emit(name, data)`` path\n * spreads the payload fields at the top level of the wire object (see\n * ``backend/src/jobs/suggestion_hook.py`` for ``prompt_suggestion``), so\n * the wire object itself *is* the ``data`` dict for ``translateCustomEvent``.\n * This helper isolates the unsafe cast to one place.\n */\nfunction legacyCustomEventData(wire: WireEvent): Record<string, unknown> {\n return wire as unknown as Record<string, unknown>;\n}\n\n/**\n * Translate a full WireEvent into its typed ChatEvent counterpart. Returns\n * ``null`` when the wire payload is malformed (e.g. unknown delta channel)\n * so the caller can skip it without crashing the stream.\n */\nexport function translateWireEvent(wire: WireEvent): ChatEvent | null {\n // Legacy transport: ``prompt_suggestion`` is emitted via the raw\n // ``writer.emit()`` path rather than wrapped in a CustomEvent envelope,\n // so its ``type`` field is the event name itself. Route it through the\n // custom-event translator to keep the mapping in one place.\n if ((wire as { type: string }).type === \"prompt_suggestion\") {\n return translateCustomEvent(\n \"prompt_suggestion\",\n legacyCustomEventData(wire),\n );\n }\n switch (wire.type) {\n case \"message_start\":\n return {\n type: \"message_start\",\n turnId: wire.turn_id,\n model: wire.model,\n agentName: wire.agent_name,\n agentDisplayName: wire.agent_display_name,\n agentAvatarUrl: wire.agent_avatar_url,\n };\n case \"block_start\":\n return {\n type: \"block_start\",\n turnId: wire.turn_id,\n path: wire.path,\n parentPath: wire.parent_path ?? null,\n kind: wire.kind,\n metadata: wire.metadata,\n };\n case \"block_delta\": {\n const delta = translateDelta(wire.delta);\n if (!delta) return null;\n return {\n type: \"block_delta\",\n turnId: wire.turn_id,\n path: wire.path,\n delta,\n };\n }\n case \"block_stop\":\n return {\n type: \"block_stop\",\n turnId: wire.turn_id,\n path: wire.path,\n status: wire.status,\n final: wire.final,\n };\n case \"message_stop\":\n return {\n type: \"message_stop\",\n turnId: wire.turn_id,\n jobId: wire.job_id,\n stopReason: wire.stop_reason,\n usage: {\n inputTokens: wire.usage.input_tokens ?? 0,\n outputTokens: wire.usage.output_tokens ?? 0,\n cachedTokens: wire.usage.cached_tokens ?? 0,\n },\n ttfbMs: wire.ttfb_ms,\n totalMs: wire.total_ms,\n stallCount: wire.stall_count,\n };\n case \"stall\":\n return {\n type: \"stall\",\n sinceLastEventMs: wire.since_last_event_ms,\n stallCount: wire.stall_count,\n };\n case \"retry\":\n return {\n type: \"retry\",\n attempt: wire.attempt,\n reason: wire.reason,\n backoffMs: wire.backoff_ms,\n strategy: wire.strategy ?? null,\n maxAttempts: wire.max_attempts ?? null,\n contextRecovery: wire.context_recovery ?? null,\n };\n case \"error\":\n return {\n type: \"error\",\n code: wire.code,\n message: wire.message,\n blockPath: wire.block_path ?? null,\n };\n case \"keepalive\":\n return {\n type: \"keepalive\",\n sinceLastEventMs: wire.since_last_event_ms,\n };\n case \"custom\":\n return translateCustomEvent(wire.name, wire.data);\n default: {\n // Exhaustive guard — a new WireEvent variant should force this switch\n // to be updated at compile time.\n const _exhaustive: never = wire;\n void _exhaustive;\n return null;\n }\n }\n}\n","import { AstralformClient } from \"./client.js\";\nimport { InMemoryStorage, type ChatStorage } from \"./storage.js\";\nimport { ToolRegistry } from \"./tools.js\";\nimport { ProtocolRegistry } from \"./protocol-registry.js\";\nimport { translateWireEvent } from \"./translate.js\";\nimport type {\n AgentInfo,\n AstralformConfig,\n ChatEvent,\n ChatStreamRequest,\n Conversation,\n Message,\n ProjectStatus,\n SendOptions,\n SkillInfo,\n ToolCallRequest,\n ToolResult,\n WireEvent,\n} from \"./types.js\";\nimport { generateId } from \"./utils.js\";\n\ntype ChatEventHandler = (event: ChatEvent) => void;\n\nfunction pathEquals(a: number[], b: number[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * ChatSession — translates the backend wire protocol into typed ChatEvents\n * for consumers. Owns HTTP + SSE plumbing, conversation state, and the\n * client-tool round-trip. Does NOT own block construction — consumers\n * build their own block state from the typed events.\n */\nexport class ChatSession {\n readonly client: AstralformClient;\n readonly toolRegistry: ToolRegistry;\n readonly storage: ChatStorage;\n /**\n * Pluggable UI protocol adapters. Consumers register a framework-\n * specific adapter (e.g. React) for each MIME type they can render,\n * typically gated on ``session.projectStatus.uiComponents.protocol``.\n * ``ToolBlock``-style consumers look up the adapter for an incoming\n * embedded resource and hand off rendering.\n */\n readonly protocols = new ProtocolRegistry();\n\n // State\n conversationId: string | null = null;\n conversations: Conversation[] = [];\n messages: Message[] = [];\n isStreaming = false;\n projectStatus: ProjectStatus | null = null;\n agents: AgentInfo[] = [];\n skills: SkillInfo[] = [];\n enabledClientTools = new Set<string>();\n modelDisplayName: string | null = null;\n\n // Minimal in-session accumulation for the assistant message record.\n // Only top-level ``text`` blocks contribute; subagent / tool output\n // is tracked by the consumer's own block store.\n private accumulatedText = \"\";\n private currentTextPath: number[] | null = null;\n\n private handlers: Set<ChatEventHandler> = new Set();\n private abortController: AbortController | null = null;\n\n constructor(config: AstralformConfig, storage?: ChatStorage) {\n this.client = new AstralformClient(config);\n this.toolRegistry = new ToolRegistry();\n this.storage = storage ?? new InMemoryStorage();\n }\n\n on(handler: ChatEventHandler): () => void {\n this.handlers.add(handler);\n return () => {\n this.handlers.delete(handler);\n };\n }\n\n private emit(event: ChatEvent): void {\n for (const handler of this.handlers) {\n try {\n handler(event);\n } catch {\n // Don't let handler errors crash the session\n }\n }\n }\n\n async connect(): Promise<void> {\n const [status, conversations, agents, skills] = await Promise.allSettled([\n this.client.getProjectStatus(),\n this.client.getConversations(),\n this.client.getAgents().catch(() => [] as AgentInfo[]),\n this.client.getSkills().catch(() => [] as SkillInfo[]),\n ]);\n\n if (status.status === \"fulfilled\") {\n this.projectStatus = status.value;\n }\n if (conversations.status === \"fulfilled\") {\n this.conversations = conversations.value;\n }\n if (agents.status === \"fulfilled\") {\n this.agents = agents.value;\n }\n if (skills.status === \"fulfilled\") {\n this.skills = skills.value;\n }\n\n this.emit({ type: \"connected\" });\n }\n\n async send(content: string, options?: SendOptions): Promise<void> {\n if (this.isStreaming) return;\n\n const conversationId =\n options?.conversationId ?? this.conversationId ?? undefined;\n\n const userMessage: Message = {\n id: generateId(),\n conversationId: conversationId ?? \"\",\n role: \"user\",\n content,\n status: \"complete\",\n createdAt: new Date().toISOString(),\n };\n if (conversationId) {\n await this.storage.addMessage(userMessage, conversationId);\n }\n this.messages.push(userMessage);\n\n const request: ChatStreamRequest = {\n message: content,\n conversation_id: conversationId,\n mcp_manifest: this.toolRegistry.getManifest(),\n enabled_mcp: Array.from(\n options?.enabledClientTools ?? this.enabledClientTools,\n ),\n upload_ids: options?.uploadIds,\n agent_name: options?.agentName,\n enable_search: options?.enableSearch,\n plan_mode: options?.planMode,\n };\n\n await this.processStream(request);\n }\n\n async resendFromCheckpoint(\n messageId: string,\n newContent: string,\n options?: { enableSearch?: boolean },\n ): Promise<void> {\n if (this.isStreaming) return;\n\n const request: ChatStreamRequest = {\n message: newContent,\n conversation_id: this.conversationId ?? undefined,\n resend_from: messageId,\n mcp_manifest: this.toolRegistry.getManifest(),\n enabled_mcp: Array.from(this.enabledClientTools),\n enable_search: options?.enableSearch,\n };\n\n await this.processStream(request);\n }\n\n private resetStreamingState(): void {\n this.accumulatedText = \"\";\n this.currentTextPath = null;\n }\n\n private async processStream(request: ChatStreamRequest): Promise<void> {\n this.isStreaming = true;\n this.resetStreamingState();\n this.abortController = new AbortController();\n\n try {\n await this.consumeJobStream(request);\n } catch (err) {\n if (!(err instanceof DOMException && err.name === \"AbortError\")) {\n this.emit({\n type: \"error\",\n code: \"connection_error\",\n message: err instanceof Error ? err.message : String(err),\n blockPath: null,\n });\n }\n } finally {\n this.isStreaming = false;\n this.abortController = null;\n }\n }\n\n /** Last received sequence number for resumable reconnection */\n private lastSeq = -1;\n\n /** Current job ID for cancellation */\n currentJobId: string | null = null;\n\n private async consumeJobStream(request: ChatStreamRequest): Promise<void> {\n const job = await this.client.createJob(request);\n this.currentJobId = job.job_id;\n\n const conversationId = job.conversation_id;\n if (!this.conversationId) {\n this.conversationId = conversationId;\n }\n // Ensure the conversation exists in both the local array and\n // ChatStorage so title_generated, completeStream, and fallback\n // reload all work for backend-created conversations.\n if (!this.conversations.some((c) => c.id === conversationId)) {\n const now = new Date().toISOString();\n const conv = {\n id: conversationId,\n title: \"\",\n messageCount: 0,\n createdAt: now,\n updatedAt: now,\n };\n this.conversations.unshift(conv);\n await this.storage.createConversation(conversationId, \"\").catch(() => {});\n }\n // Backfill the just-sent user message if send() ran before we knew the\n // conversation id (first turn of an auto-created conversation).\n const lastMsg = this.messages[this.messages.length - 1];\n if (lastMsg?.role === \"user\" && !lastMsg.conversationId) {\n lastMsg.conversationId = conversationId;\n await this.storage.addMessage(lastMsg, conversationId).catch(() => {});\n }\n const messageId = job.message_id;\n this.lastSeq = -1;\n\n const stream = this.client.streamJobEvents(\n job.job_id,\n this.lastSeq,\n this.abortController?.signal,\n );\n\n await this.consumeEventStream(\n stream,\n conversationId,\n messageId,\n true, // executeClientTools\n );\n }\n\n /**\n * Shared event consumption loop. Parses each wire event, updates\n * minimal session state, and emits typed ChatEvents to consumers.\n */\n private async consumeEventStream(\n stream: AsyncGenerator<{ data: string }>,\n conversationId: string,\n messageId: string,\n executeClientTools: boolean,\n ): Promise<void> {\n for await (const raw of stream) {\n let parsed: WireEvent;\n try {\n const data = JSON.parse(raw.data);\n if (\n typeof data !== \"object\" ||\n data === null ||\n typeof data.type !== \"string\"\n ) {\n // Legacy \"done\" sentinel — backend still emits it for subscribers.\n // Silently consume; the new protocol uses message_stop for turn end.\n if (typeof (data as { seq?: unknown })?.seq === \"number\") {\n this.lastSeq = (data as { seq: number }).seq;\n }\n continue;\n }\n parsed = data as WireEvent;\n if (typeof (data as Record<string, unknown>).seq === \"number\") {\n this.lastSeq = (data as Record<string, unknown>).seq as number;\n }\n } catch {\n continue;\n }\n\n await this.dispatchWireEvent(\n parsed,\n conversationId,\n messageId,\n executeClientTools,\n );\n }\n }\n\n private async dispatchWireEvent(\n wire: WireEvent,\n conversationId: string,\n messageId: string,\n executeClientTools: boolean,\n ): Promise<void> {\n // Side effects that depend on mutable session state must run before the\n // ChatEvent is emitted so consumers see a consistent view.\n this.applyWireSideEffects(wire, conversationId, messageId);\n\n const event = translateWireEvent(wire);\n if (event) {\n this.emit(event);\n }\n\n // Client tool round-trip — deferred to block_stop with\n // status=awaiting_client_result, where the parsed input is in final.input.\n if (\n executeClientTools &&\n wire.type === \"block_stop\" &&\n wire.status === \"awaiting_client_result\" &&\n wire.final?.call_id\n ) {\n const f = wire.final;\n const request: ToolCallRequest = {\n callId: (f.call_id as string) ?? \"\",\n toolName: (f.tool_name as string) ?? \"\",\n arguments: (f.input as Record<string, unknown>) ?? {},\n isClientTool: true,\n };\n const results = await this.executeClientTools([request]);\n await this.client.submitToolResult({\n conversation_id: conversationId,\n message_id: messageId,\n tool_results: results,\n });\n }\n }\n\n /**\n * State mutations driven by wire events. Kept separate from translation so\n * the pure wire → ChatEvent mapping can live in translate.ts and be reused\n * by the replay path.\n *\n * ``messageId`` is the server-assigned assistant message id for the current\n * turn; empty in the reconnect and conversation-switch replay paths where\n * messages have already been loaded from REST and shouldn't be re-pushed.\n */\n private applyWireSideEffects(\n wire: WireEvent,\n conversationId: string,\n messageId: string,\n ): void {\n switch (wire.type) {\n case \"message_start\":\n // Reset per-turn accumulator so multi-turn replay doesn't concatenate\n // text from prior turns into the next assistant message.\n this.resetStreamingState();\n if (wire.model) {\n this.modelDisplayName = wire.model;\n }\n return;\n\n case \"block_start\":\n // Track the currently open top-level text block so we can accumulate\n // its content for the assistant Message record.\n if (\n wire.kind === \"text\" &&\n (!wire.parent_path || wire.parent_path.length === 0)\n ) {\n this.currentTextPath = wire.path;\n }\n return;\n\n case \"block_delta\":\n if (\n wire.delta.channel === \"text\" &&\n this.currentTextPath !== null &&\n pathEquals(this.currentTextPath, wire.path)\n ) {\n this.accumulatedText += wire.delta.text;\n }\n return;\n\n case \"block_stop\":\n if (\n this.currentTextPath !== null &&\n pathEquals(this.currentTextPath, wire.path)\n ) {\n this.currentTextPath = null;\n }\n return;\n\n case \"message_stop\":\n // Only record the assistant message when we have the server's\n // message id. Reconnect/replay paths load messages via REST instead.\n if (messageId) {\n const assistantMessage: Message = {\n id: messageId,\n conversationId,\n role: \"assistant\",\n content: this.accumulatedText,\n status: \"complete\",\n createdAt: new Date().toISOString(),\n };\n this.messages.push(assistantMessage);\n this.storage\n .addMessage(assistantMessage, conversationId)\n .catch(() => {});\n }\n this.isStreaming = false;\n this.currentJobId = null;\n return;\n\n case \"custom\":\n if (wire.name === \"title_generated\") {\n const title = (wire.data.title as string) ?? \"\";\n if (this.conversationId && title) {\n const conv = this.conversations.find(\n (c) => c.id === this.conversationId,\n );\n if (conv) {\n conv.title = title;\n }\n this.storage\n .updateConversationTitle(this.conversationId, title)\n .catch(() => {});\n }\n }\n return;\n\n default:\n return;\n }\n }\n\n private async executeClientTools(\n toolCalls: ToolCallRequest[],\n ): Promise<ToolResult[]> {\n const results: ToolResult[] = [];\n for (const call of toolCalls) {\n const result = await this.toolRegistry.executeTool(call);\n results.push(result);\n }\n return results;\n }\n\n /**\n * Load conversation context (messages) without replaying events.\n * Used before reconnectToJob — SSE replay handles event replay.\n */\n async loadConversation(id: string): Promise<void> {\n this.conversationId = id;\n this.resetStreamingState();\n this.messages = await this.client\n .getMessages(id)\n .catch(() => this.storage.fetchMessages(id));\n }\n\n /**\n * Reconnect to a running job's SSE stream (e.g. after page reload).\n * Replays all events from the beginning and continues live.\n */\n async reconnectToJob(jobId: string): Promise<void> {\n if (this.isStreaming) return;\n\n this.isStreaming = true;\n this.currentJobId = jobId;\n this.lastSeq = -1;\n this.resetStreamingState();\n this.abortController = new AbortController();\n\n try {\n const stream = this.client.streamJobEvents(\n jobId,\n this.lastSeq,\n this.abortController?.signal,\n );\n await this.consumeEventStream(\n stream,\n this.conversationId ?? \"\",\n \"\",\n false, // don't execute client tools on reconnect\n );\n } catch (err) {\n this.emit({\n type: \"error\",\n code: \"connection_error\",\n message: err instanceof Error ? err.message : String(err),\n blockPath: null,\n });\n } finally {\n this.isStreaming = false;\n this.abortController = null;\n }\n }\n\n /** Detach from the SSE stream without cancelling the job. */\n detach(): void {\n this.abortController?.abort();\n this.abortController = null;\n this.isStreaming = false;\n this.resetStreamingState();\n this.emit({ type: \"disconnected\" });\n }\n\n /** Stop the job and disconnect (explicit user action). */\n disconnect(): void {\n if (this.currentJobId) {\n this.client.cancelJob(this.currentJobId).catch(() => {});\n }\n this.detach();\n this.currentJobId = null;\n // Drop all protocol adapters — lifecycle tied to the session.\n this.protocols.clear();\n }\n\n async createNewConversation(): Promise<string> {\n const id = generateId();\n const conversation = await this.storage.createConversation(\n id,\n \"New Conversation\",\n );\n this.conversations.unshift(conversation);\n this.conversationId = id;\n this.messages = [];\n return id;\n }\n\n async switchConversation(\n id: string,\n jobId?: string,\n /**\n * User prompt that triggered this job, if known. Emitted as a\n * synthetic ``user_message`` ChatEvent right before the first\n * ``message_start`` of the replay. User messages aren't persisted\n * in ``job_events``, so without this the restored conversation\n * would show the agent response with no visible prompt above it.\n */\n userMessageContent?: string,\n ): Promise<void> {\n this.conversationId = id;\n this.resetStreamingState();\n\n const [messagesResult, eventsResult] = await Promise.allSettled([\n this.client.getMessages(id).catch(() => this.storage.fetchMessages(id)),\n this.client.getConversationEvents(id, jobId),\n ]);\n\n this.messages =\n messagesResult.status === \"fulfilled\" ? messagesResult.value : [];\n\n // Replay stored events via the same dispatch path. The consumer\n // rebuilds its block state from the replayed events. The data payload\n // is authoritative for `type` (matches how replay.ts#mapSseToChat reads\n // it) with the SSE event name as a fallback for pre-v2 rows.\n if (eventsResult.status === \"fulfilled\") {\n let userMessageEmitted = !userMessageContent;\n for (const ev of eventsResult.value) {\n const type = (ev.data.type as string) || ev.event;\n if (!type || type === \"done\") continue;\n\n // Inject the user prompt at the turn boundary — right before\n // the first ``message_start`` — so the consumer can insert a\n // user block above the agent response.\n if (!userMessageEmitted && type === \"message_start\") {\n this.emit({\n type: \"user_message\",\n content: userMessageContent!,\n });\n userMessageEmitted = true;\n }\n\n const wire = { ...ev.data, type } as unknown as WireEvent;\n try {\n await this.dispatchWireEvent(\n wire,\n id,\n \"\",\n false, // don't execute client tools on replay\n );\n } catch {\n // Skip malformed replay events\n }\n }\n }\n }\n\n async deleteConversation(id: string): Promise<void> {\n try {\n await this.client.deleteConversation(id);\n } catch {\n // May already be deleted on backend\n }\n await this.storage.deleteConversation(id);\n this.conversations = this.conversations.filter((c) => c.id !== id);\n if (this.conversationId === id) {\n this.conversationId = null;\n this.messages = [];\n }\n }\n\n toggleClientTool(name: string): boolean {\n if (this.enabledClientTools.has(name)) {\n this.enabledClientTools.delete(name);\n return false;\n }\n this.enabledClientTools.add(name);\n return true;\n }\n}\n","// =============================================================================\n// Astralform SDK v2 type definitions\n// =============================================================================\n//\n// Wire protocol types mirror the Pydantic models in\n// `backend/src/stream/protocol.py` 1:1. The SDK forwards typed events to\n// the consumer; block construction happens on the consumer side.\n//\n// Custom event payloads live in `custom-events.ts`.\n\nimport type { AgentIdentity, MemoryRecord, TodoItem } from \"./custom-events.js\";\n\n// Re-export so consumers can import from types.ts or custom-events.ts\nexport type { AgentIdentity, MemoryRecord, TodoItem };\n\n// --- Configuration ---\n//\n// The SDK supports two authentication modes. TypeScript narrows the union by\n// property presence, so consumers can write:\n//\n// new AstralformClient({ apiKey, userId }) // API-key mode\n// new AstralformClient({ accessToken, projectId }) // user-token mode\n//\n// Pick based on who the caller represents:\n//\n// * API-key mode — A customer's backend (B2B2C). Project scoping is baked\n// into the key; the end user is named per request via `X-End-User-ID`.\n//\n// * User-token mode — An app acting on behalf of an Astralform account\n// holder (AstralChat, future 3rd-party integrations). The OIDC access\n// token is issued by the Astralform Identity Provider (Supabase OAuth 2.1\n// Server); project scoping comes from the `X-Project-ID` header.\n\ninterface AstralformBaseConfig {\n /** Override the default API origin. Defaults to https://api.astralform.ai. */\n baseURL?: string;\n /** Supply a custom fetch (SSR, testing, custom interceptors). */\n fetch?: typeof globalThis.fetch;\n}\n\nexport interface AstralformApiKeyConfig extends AstralformBaseConfig {\n /** Project API key (`sk_live_...` or `sk_test_...`). */\n apiKey: string;\n /**\n * The customer's own identifier for the end user making this call.\n * Sent as the `X-End-User-ID` header. Required in API-key mode.\n */\n userId: string;\n}\n\nexport interface AstralformUserTokenConfig extends AstralformBaseConfig {\n /**\n * OIDC access token issued by the Astralform Identity Provider.\n * Expect this to be short-lived; use `client.updateAccessToken()` after\n * refreshing to hot-swap without re-instantiating the client.\n */\n accessToken: string;\n /**\n * Active project context. Sent as the `X-Project-ID` header. The backend\n * verifies the token's developer has access to this project on every\n * request; switching projects is a local `updateProjectId()` call.\n *\n * Optional so a pre-pick client (right after login, before the user has\n * chosen a team/project) can still call account-scoped discovery routes\n * like `listTeams()` / `listProjects(teamId)`. Project-scoped calls\n * (conversations, messages, chat) will error out until one is set.\n */\n projectId?: string;\n /**\n * Optional end-user override — lets a developer acting under a user\n * token impersonate a downstream end-user identity for testing\n * purposes. When set, sent alongside `X-Project-ID` as `X-End-User-ID`\n * so memory, rate limits, and conversations scope to the specified\n * end-user rather than the developer themselves.\n *\n * Use `client.updateEndUserId()` to rotate at runtime.\n */\n endUserId?: string;\n}\n\nexport type AstralformConfig =\n | AstralformApiKeyConfig\n | AstralformUserTokenConfig;\n\n// --- Event type constants (SDK public) ---\n//\n// These are the high-level ChatEvent kinds the SDK emits to its\n// consumer. The raw wire events below are translated into these\n// kinds at the session boundary.\n\nexport const ChatEventType = {\n // Connection lifecycle (SDK-local, not wire)\n Connected: \"connected\",\n Disconnected: \"disconnected\",\n\n // Turn lifecycle\n MessageStart: \"message_start\",\n MessageStop: \"message_stop\",\n\n // Block lifecycle\n BlockStart: \"block_start\",\n BlockDelta: \"block_delta\",\n BlockStop: \"block_stop\",\n\n // Reliability\n Stall: \"stall\",\n Retry: \"retry\",\n Error: \"error\",\n Keepalive: \"keepalive\",\n\n // Conversation-level (typed custom events)\n UserMessage: \"user_message\",\n TitleGenerated: \"title_generated\",\n TodoUpdate: \"todo_update\",\n ContextUpdate: \"context_update\",\n SubagentStart: \"subagent_start\",\n SubagentStop: \"subagent_stop\",\n ContextWarning: \"context_warning\",\n MemoryRecall: \"memory_recall\",\n MemoryUpdate: \"memory_update\",\n DesktopStream: \"desktop_stream\",\n AttachmentStaged: \"attachment_staged\",\n WorkspaceReady: \"workspace_ready\",\n AssetCreated: \"asset_created\",\n ToolApprovalRequested: \"tool_approval_requested\",\n ToolApprovalGranted: \"tool_approval_granted\",\n ToolPermissionDenied: \"tool_permission_denied\",\n ToolHarnessWarning: \"tool_harness_warning\",\n UserUnavailable: \"user_unavailable\",\n PromptSuggestion: \"prompt_suggestion\",\n StateChanged: \"state_changed\",\n\n // Generic fallthrough for unknown custom events\n Custom: \"custom\",\n} as const;\n\nexport type ChatEventTypeValue =\n (typeof ChatEventType)[keyof typeof ChatEventType];\n\n// =============================================================================\n// Wire protocol (matches backend Pydantic models 1:1, snake_case)\n// =============================================================================\n\nexport type WireBlockKind = \"text\" | \"thinking\" | \"tool_use\";\n\nexport type WireBlockStatus =\n | \"streaming\"\n | \"awaiting_client_result\"\n | \"ok\"\n | \"error\"\n | \"denied\"\n | \"cancelled\";\n\nexport type WireStopReason =\n | \"end_turn\"\n | \"tool_use\"\n | \"max_tokens\"\n | \"context_overflow\"\n | \"error\";\n\n// --- BlockDelta payloads (discriminated on `channel`) ---\n\nexport interface WireTextDelta {\n channel: \"text\";\n text: string;\n}\n\nexport interface WireThinkingDelta {\n channel: \"thinking\";\n text: string;\n}\n\nexport interface WireSignatureDelta {\n channel: \"signature\";\n signature: string;\n}\n\nexport interface WireInputDelta {\n channel: \"input\";\n partial_json: string;\n}\n\nexport interface WireInputArgDelta {\n channel: \"input_arg\";\n arg_name: string;\n text: string;\n}\n\nexport interface WireOutputDelta {\n channel: \"output\";\n stream: \"stdout\" | \"stderr\" | \"progress\";\n chunk: string;\n}\n\nexport interface WireStatusDelta {\n channel: \"status\";\n status:\n | \"executing\"\n | \"awaiting_client_result\"\n | \"awaiting_approval\"\n | \"denied\";\n note?: string;\n}\n\nexport type WireBlockDeltaPayload =\n | WireTextDelta\n | WireThinkingDelta\n | WireSignatureDelta\n | WireInputDelta\n | WireInputArgDelta\n | WireOutputDelta\n | WireStatusDelta;\n\n// --- Top-level wire events ---\n\ninterface WireEnvelope {\n seq: number;\n ts: number;\n job_id: string;\n}\n\nexport interface WireMessageStart extends WireEnvelope {\n type: \"message_start\";\n turn_id: string;\n model: string;\n agent_name?: string | null;\n agent_display_name?: string | null;\n agent_avatar_url?: string | null;\n}\n\nexport interface WireBlockStart extends WireEnvelope {\n type: \"block_start\";\n turn_id: string;\n path: number[];\n parent_path?: number[] | null;\n kind: WireBlockKind;\n metadata: Record<string, unknown>;\n}\n\nexport interface WireBlockDelta extends WireEnvelope {\n type: \"block_delta\";\n turn_id: string;\n path: number[];\n delta: WireBlockDeltaPayload;\n}\n\nexport interface WireBlockStop extends WireEnvelope {\n type: \"block_stop\";\n turn_id: string;\n path: number[];\n status: WireBlockStatus;\n final: Record<string, unknown>;\n}\n\nexport interface WireMessageStop extends WireEnvelope {\n type: \"message_stop\";\n turn_id: string;\n stop_reason: WireStopReason;\n usage: {\n input_tokens?: number;\n output_tokens?: number;\n cached_tokens?: number;\n };\n ttfb_ms?: number | null;\n total_ms: number;\n stall_count: number;\n}\n\nexport interface WireStallWarning extends WireEnvelope {\n type: \"stall\";\n since_last_event_ms: number;\n stall_count: number;\n}\n\nexport interface WireRetryEvent extends WireEnvelope {\n type: \"retry\";\n attempt: number;\n reason: string;\n backoff_ms: number;\n strategy?: string | null;\n max_attempts?: number | null;\n context_recovery?: Record<string, unknown> | null;\n}\n\nexport interface WireErrorEvent extends WireEnvelope {\n type: \"error\";\n code: string;\n message: string;\n block_path?: number[] | null;\n // Rate limit fields (carried when code == \"rate_limit_exceeded\")\n retry_after?: number;\n retry_after_sec?: number;\n reset_at?: number | string;\n scope?: string;\n policy_id?: string;\n limit?: number;\n remaining?: number;\n request_id?: string;\n}\n\nexport interface WireKeepalive extends WireEnvelope {\n type: \"keepalive\";\n since_last_event_ms: number;\n}\n\nexport interface WireCustomEvent extends WireEnvelope {\n type: \"custom\";\n name: string;\n data: Record<string, unknown>;\n}\n\nexport type WireEvent =\n | WireMessageStart\n | WireBlockStart\n | WireBlockDelta\n | WireBlockStop\n | WireMessageStop\n | WireStallWarning\n | WireRetryEvent\n | WireErrorEvent\n | WireKeepalive\n | WireCustomEvent;\n\n// =============================================================================\n// ChatEvent — high-level SDK events (camelCase, emitted to consumers)\n// =============================================================================\n\nexport interface TurnUsage {\n inputTokens: number;\n outputTokens: number;\n cachedTokens: number;\n}\n\nexport type BlockDeltaPayload =\n | { channel: \"text\"; text: string }\n | { channel: \"thinking\"; text: string }\n | { channel: \"signature\"; signature: string }\n | { channel: \"input\"; partialJson: string }\n | { channel: \"inputArg\"; argName: string; text: string }\n | {\n channel: \"output\";\n stream: \"stdout\" | \"stderr\" | \"progress\";\n chunk: string;\n }\n | {\n channel: \"status\";\n status:\n | \"executing\"\n | \"awaiting_client_result\"\n | \"awaiting_approval\"\n | \"denied\";\n note?: string;\n };\n\nexport type ChatEvent =\n // Connection lifecycle\n | { type: \"connected\" }\n | { type: \"disconnected\" }\n\n // Turn lifecycle\n | {\n type: \"message_start\";\n turnId: string;\n model: string;\n agentName?: string | null;\n agentDisplayName?: string | null;\n agentAvatarUrl?: string | null;\n }\n | {\n type: \"message_stop\";\n turnId: string;\n jobId: string;\n stopReason: WireStopReason;\n usage: TurnUsage;\n ttfbMs?: number | null;\n totalMs: number;\n stallCount: number;\n }\n\n // Block lifecycle\n | {\n type: \"block_start\";\n turnId: string;\n path: number[];\n parentPath?: number[] | null;\n kind: WireBlockKind;\n metadata: Record<string, unknown>;\n }\n | {\n type: \"block_delta\";\n turnId: string;\n path: number[];\n delta: BlockDeltaPayload;\n }\n | {\n type: \"block_stop\";\n turnId: string;\n path: number[];\n status: WireBlockStatus;\n final: Record<string, unknown>;\n }\n\n // Reliability\n | {\n type: \"stall\";\n sinceLastEventMs: number;\n stallCount: number;\n }\n | {\n type: \"retry\";\n attempt: number;\n reason: string;\n backoffMs: number;\n strategy?: string | null;\n maxAttempts?: number | null;\n contextRecovery?: Record<string, unknown> | null;\n }\n | {\n type: \"error\";\n code: string;\n message: string;\n blockPath: number[] | null;\n }\n | {\n type: \"keepalive\";\n sinceLastEventMs: number;\n }\n\n // Conversation-level — typed custom events\n | { type: \"user_message\"; content: string; createdAt?: number }\n | { type: \"title_generated\"; title: string }\n | { type: \"todo_update\"; todos: TodoItem[] }\n | {\n type: \"context_update\";\n context: Record<string, unknown>;\n phase?: string | null;\n updatedAt?: number | null;\n }\n | {\n type: \"subagent_start\";\n agent: AgentIdentity;\n taskCallId?: string | null;\n }\n | {\n type: \"subagent_stop\";\n agent: AgentIdentity;\n taskCallId?: string | null;\n }\n | {\n type: \"context_warning\";\n /** Known values: \"info\" | \"warning\" | \"critical\". Typed as string for forward compat. */\n severity: string;\n utilizationPct: number;\n remainingTokens: number;\n windowTokens: number;\n inputTokens: number;\n message: string;\n }\n | { type: \"memory_recall\"; memories: MemoryRecord[] }\n | {\n type: \"memory_update\";\n /** Known values: \"created\" | \"updated\" | \"deleted\". */\n action: string;\n memoryId?: string | null;\n key?: string | null;\n namespace?: string | null;\n }\n | { type: \"desktop_stream\"; url: string; sandboxId?: string | null }\n | {\n type: \"attachment_staged\";\n attachmentId: string;\n filename: string;\n contentType?: string | null;\n sizeBytes?: number | null;\n }\n | {\n type: \"workspace_ready\";\n sandboxId: string;\n workspacePath?: string | null;\n }\n | {\n type: \"asset_created\";\n assetId: string;\n filename: string;\n url?: string | null;\n contentType?: string | null;\n }\n | {\n type: \"tool_approval_requested\";\n toolName: string;\n callId: string;\n arguments: Record<string, unknown>;\n riskLevel?: string | null;\n reason?: string | null;\n }\n | {\n type: \"tool_approval_granted\";\n toolName: string;\n callId: string;\n }\n | {\n type: \"tool_permission_denied\";\n toolName: string;\n callId: string;\n reason?: string | null;\n /** Known values: \"hook\" | \"rule\" | \"user\" | \"timeout\" | \"circuit_breaker\". */\n deniedBy?: string | null;\n }\n | {\n type: \"tool_harness_warning\";\n toolName: string;\n callId: string;\n message?: string | null;\n details?: Record<string, unknown> | null;\n }\n | {\n type: \"user_unavailable\";\n consecutiveTimeouts: number;\n toolName?: string | null;\n }\n | {\n type: \"prompt_suggestion\";\n suggestions: string[];\n }\n | {\n type: \"state_changed\";\n /**\n * Job lifecycle state. Known values from the backend today include\n * \"queued\" | \"running\" | \"waiting_for_tool\" | \"completed\" | \"failed\".\n * Typed as string for forward compat.\n */\n state: string;\n }\n\n // Generic fallthrough for unknown custom events\n | {\n type: \"custom\";\n name: string;\n data: Record<string, unknown>;\n };\n\n// =============================================================================\n// Domain models (unchanged from previous SDK version)\n// =============================================================================\n\nexport interface Conversation {\n id: string;\n title: string;\n messageCount: number;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface Message {\n id: string;\n conversationId: string;\n role: \"user\" | \"assistant\" | \"system\";\n content: string;\n parentId?: string;\n status: \"sending\" | \"streaming\" | \"complete\" | \"error\";\n createdAt: string;\n toolCalls?: ToolCallRequest[];\n}\n\nexport interface UIComponentsConfig {\n enabled: boolean;\n /** Protocol slug (e.g. \"a2ui\"). Null when disabled. */\n protocol: string | null;\n /** MIME type to match against embedded resources (e.g. \"application/json+a2ui\"). */\n mimeType: string | null;\n}\n\nexport interface ProjectStatus {\n isReady: boolean;\n llmConfigured: boolean;\n llmProvider?: string;\n llmModel?: string;\n message: string;\n uiComponents: UIComponentsConfig;\n}\n\nexport interface AgentInfo {\n name: string;\n displayName: string;\n description: string;\n isOrchestrator: boolean;\n isEnabled: boolean;\n avatarUrl?: string;\n}\n\n// --- Team / Project discovery (OIDC user-token surface) ---\n\nexport interface TeamSummary {\n id: string;\n name: string;\n slug: string;\n isDefault: boolean;\n /** Caller's role in this team (e.g. \"owner\", \"admin\", \"member\"). */\n role: string;\n}\n\nexport interface ProjectSummary {\n id: string;\n name: string;\n teamId: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface SkillInfo {\n name: string;\n displayName: string;\n description: string;\n isEnabled: boolean;\n}\n\n// TodoItem is imported from custom-events.ts and re-exported above.\n\n// =============================================================================\n// Job and request types\n// =============================================================================\n\nexport interface JobCreateResponse {\n job_id: string;\n conversation_id: string;\n message_id: string;\n status: string;\n}\n\nexport interface JobStatus {\n jobId: string;\n status: string;\n createdAt: string | null;\n startedAt: string | null;\n completedAt: string | null;\n errorMessage: string | null;\n inputTokens: number;\n outputTokens: number;\n}\n\nexport interface ActiveJob {\n jobId: string | null;\n status: string;\n}\n\nexport interface JobSummary {\n jobId: string;\n status: string;\n replacesJobId: string | null;\n responseContent: Record<string, unknown> | null;\n metrics: Record<string, unknown> | null;\n createdAt: string | null;\n}\n\nexport interface FeedbackRequest {\n /** 1 for thumbs up, -1 for thumbs down. */\n rating: 1 | -1;\n comment?: string | null;\n}\n\nexport interface FeedbackResponse {\n id: string;\n jobId: string;\n rating: number;\n comment: string | null;\n createdAt: string;\n}\n\nexport interface ChatStreamRequest {\n message?: string;\n conversation_id?: string;\n mcp_manifest?: ToolDefinition[];\n enabled_mcp?: string[];\n continue_from_message?: string;\n resend_from?: string;\n upload_ids?: string[];\n agent_name?: string;\n enable_search?: boolean;\n plan_mode?: boolean;\n}\n\nexport interface ToolResultRequest {\n conversation_id: string;\n message_id: string;\n tool_results: ToolResult[];\n}\n\nexport interface ToolResult {\n call_id: string;\n tool_name: string;\n result: string;\n is_error: boolean;\n}\n\nexport type ToolApprovalDecision = \"allow\" | \"deny\";\nexport type ToolApprovalScope = \"once\" | \"conversation\" | \"always\";\n\nexport interface ToolApprovalRequest {\n job_id: string;\n call_id: string;\n decision: ToolApprovalDecision;\n scope: ToolApprovalScope;\n}\n\n/**\n * A remembered tool-permission grant belonging to the current end user.\n * Only `conversation`/`always` grants are ever stored (`once` is consumed at\n * approval time and never persisted).\n */\nexport interface ToolGrant {\n id: string;\n toolName: string;\n decision: ToolApprovalDecision;\n scope: Exclude<ToolApprovalScope, \"once\">;\n /** Set for `conversation`-scoped grants; `null` for `always`. */\n conversationId: string | null;\n createdAt: string;\n}\n\n/** A page of the current end user's own tool grants. */\nexport interface MyToolGrantsPage {\n grants: ToolGrant[];\n total: number;\n limit: number;\n offset: number;\n}\n\nexport interface ToolDefinition {\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n}\n\nexport interface ToolCallRequest {\n callId: string;\n toolName: string;\n displayName?: string;\n description?: string;\n arguments: Record<string, unknown>;\n isClientTool: boolean;\n toolCategory?: string;\n iconUrl?: string;\n}\n\n// =============================================================================\n// SSE transport / raw parsing\n// =============================================================================\n\nexport interface StreamJobSSEOptions {\n url: string;\n headers: Record<string, string>;\n signal?: AbortSignal;\n fetchFn: typeof globalThis.fetch;\n}\n\nexport interface ChatStreamEvent {\n event: string;\n data: string;\n}\n\nexport interface ConversationEvent {\n seq: number;\n event: string;\n data: Record<string, unknown>;\n}\n\n// =============================================================================\n// Send / session options\n// =============================================================================\n\nexport interface SendOptions {\n conversationId?: string;\n enabledClientTools?: string[];\n uploadIds?: string[];\n agentName?: string;\n enableSearch?: boolean;\n planMode?: boolean;\n}\n\n// =============================================================================\n// Conversation assets (unchanged)\n// =============================================================================\n\nexport interface ConversationAsset {\n id: string;\n kind: \"upload\" | \"output\";\n originalName: string;\n mediaType: string;\n sizeBytes: number;\n workspacePath?: string;\n sourceMessageId?: string;\n agentName?: string;\n createdAt: string;\n}\n","/**\n * StreamManager — high-level conversation lifecycle coordinator.\n *\n * Sits on top of ChatSession and manages the state machine for\n * multi-conversation SSE streaming. Framework-agnostic: emits\n * typed events to registered handlers. Block construction is NOT\n * the SDK's concern — consumers build their own block tree from\n * the forwarded ``ChatEvent`` instances.\n *\n * import { ChatSession, StreamManager } from \"@astralform/js\";\n * const session = new ChatSession({ ... });\n * const manager = new StreamManager(session);\n * manager.on((event) => {\n * if (event.type === \"event\") {\n * // event.event is a typed ChatEvent — dispatch to your reducer\n * }\n * });\n * await manager.send(\"Hello\");\n */\n\nimport type { ChatEvent } from \"./types.js\";\nimport { ChatEventType } from \"./types.js\";\nimport type { ChatSession } from \"./session.js\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type StreamState = \"idle\" | \"streaming\" | \"restoring\" | \"detached\";\n\nexport interface SendOptions {\n enableSearch?: boolean;\n agentName?: string;\n uploadIds?: string[];\n planMode?: boolean;\n}\n\nexport type StreamManagerEvent =\n | { type: \"stateChange\"; state: StreamState; conversationId: string | null }\n | { type: \"conversationChanged\"; conversationId: string | null }\n | {\n type: \"backgroundJobsChanged\";\n jobs: ReadonlyMap<string, string>;\n }\n | { type: \"event\"; conversationId: string | null; event: ChatEvent }\n | { type: \"versionsReady\"; conversationId: string; count: number };\n\ntype EventHandler = (event: StreamManagerEvent) => void;\n\n// =============================================================================\n// StreamManager\n// =============================================================================\n\nexport class StreamManager {\n private session: ChatSession;\n private _state: StreamState = \"idle\";\n private _activeConversationId: string | null = null;\n private _backgroundJobs = new Map<string, string>();\n private handlers: EventHandler[] = [];\n private unsub: (() => void) | null = null;\n\n constructor(session: ChatSession) {\n this.session = session;\n this.attach();\n }\n\n // ── Public state ──────────────────────────────────────────────\n\n get state(): StreamState {\n return this._state;\n }\n\n get activeConversationId(): string | null {\n return this._activeConversationId;\n }\n\n get backgroundJobs(): ReadonlyMap<string, string> {\n return this._backgroundJobs;\n }\n\n // ── Event subscription ────────────────────────────────────────\n\n on(handler: EventHandler): () => void {\n this.handlers.push(handler);\n return () => {\n this.handlers = this.handlers.filter((h) => h !== handler);\n };\n }\n\n private emit(event: StreamManagerEvent): void {\n for (const handler of this.handlers) {\n try {\n handler(event);\n } catch {\n // Don't let handler errors crash the manager\n }\n }\n }\n\n private setState(state: StreamState): void {\n this._state = state;\n this.emit({\n type: \"stateChange\",\n state,\n conversationId: this._activeConversationId,\n });\n }\n\n // ── Session event wiring ──────────────────────────────────────\n\n private attach(): void {\n this.unsub = this.session.on((event: ChatEvent) => {\n this.onSessionEvent(event);\n });\n }\n\n private onSessionEvent(event: ChatEvent): void {\n const convId = this.session.conversationId;\n\n // Forward every event to subscribers as a typed envelope\n this.emit({\n type: \"event\",\n conversationId: convId,\n event,\n });\n\n // Handle completion — message_stop is the terminal turn event.\n if (event.type === ChatEventType.MessageStop) {\n if (this._state === \"streaming\") {\n this.setState(\"idle\");\n }\n }\n }\n\n // ── Send ──────────────────────────────────────────────────────\n\n async send(content: string, options?: SendOptions): Promise<void> {\n if (this._state === \"streaming\") return;\n\n // Auto-create conversation if none active\n if (!this._activeConversationId) {\n const id = await this.session.createNewConversation();\n this.setActiveConversation(id);\n }\n\n this.setState(\"streaming\");\n\n try {\n await this.session.send(content, {\n enableSearch: options?.enableSearch,\n agentName: options?.agentName,\n uploadIds: options?.uploadIds,\n planMode: options?.planMode,\n });\n } catch {\n // AbortError from detach is expected\n }\n\n this.finalizeStream();\n }\n\n // ── Regenerate ────────────────────────────────────────────────\n\n async regenerate(): Promise<void> {\n if (this._state === \"streaming\") return;\n\n const userMsgs = this.session.messages.filter(\n (m: { role: string }) => m.role === \"user\",\n );\n const lastUserMsg = userMsgs[userMsgs.length - 1];\n if (!lastUserMsg) return;\n\n this.setState(\"streaming\");\n\n try {\n await this.session.resendFromCheckpoint(\n lastUserMsg.id,\n lastUserMsg.content,\n );\n } catch {\n // AbortError from detach is expected\n }\n\n this.finalizeStream();\n }\n\n // ── Switch conversation ───────────────────────────────────────\n\n async switchTo(conversationId: string): Promise<void> {\n if (conversationId === this._activeConversationId) return;\n\n // If streaming, detach (job keeps running in background)\n if (this._state === \"streaming\") {\n const oldConvId = this._activeConversationId;\n const jobId = this.session.currentJobId;\n if (oldConvId && jobId) {\n this._backgroundJobs.set(oldConvId, jobId);\n this.emit({\n type: \"backgroundJobsChanged\",\n jobs: this._backgroundJobs,\n });\n }\n this.session.detach();\n }\n\n // Clear background job for target (we're viewing it now)\n if (this._backgroundJobs.has(conversationId)) {\n this._backgroundJobs.delete(conversationId);\n this.emit({\n type: \"backgroundJobsChanged\",\n jobs: this._backgroundJobs,\n });\n }\n\n this.setActiveConversation(conversationId);\n await this.restore(conversationId);\n }\n\n // ── Create / delete conversation ──────────────────────────────\n\n async createConversation(): Promise<string> {\n const id = await this.session.createNewConversation();\n this.setActiveConversation(id);\n return id;\n }\n\n async deleteConversation(id: string): Promise<void> {\n await this.session.deleteConversation(id);\n this._backgroundJobs.delete(id);\n if (this._activeConversationId === id) {\n this._activeConversationId = null;\n this.emit({ type: \"conversationChanged\", conversationId: null });\n }\n }\n\n // ── Stop (explicit cancel) ────────────────────────────────────\n\n stop(): void {\n this.session.disconnect();\n this.setState(\"idle\");\n }\n\n // ── Cleanup ───────────────────────────────────────────────────\n\n destroy(): void {\n if (this.unsub) {\n this.unsub();\n this.unsub = null;\n }\n this.handlers = [];\n }\n\n // ── Internal: helpers ──────────────────────────────────────────\n\n private finalizeStream(): void {\n if (this._state === \"streaming\") {\n this.setState(\"idle\");\n }\n }\n\n // ── Internal: restore ─────────────────────────────────────────\n\n private async restore(conversationId: string): Promise<void> {\n this.setState(\"restoring\");\n\n // Check for active job\n let activeJobId: string | null = null;\n try {\n const res = await this.session.client.getActiveJob(conversationId);\n activeJobId = res.jobId;\n } catch {\n // Network error — assume no active job\n }\n\n if (activeJobId) {\n // Active job: load messages, reconnect to live SSE\n await this.session.loadConversation(conversationId);\n this.setState(\"streaming\");\n try {\n await this.session.reconnectToJob(activeJobId);\n } catch {\n // Stream ended or aborted\n }\n if (this._state === \"streaming\") {\n this.setState(\"idle\");\n }\n } else {\n // Completed: load messages, replay version chain\n await this.session.loadConversation(conversationId);\n\n try {\n const jobs = await this.session.client.get<\n {\n job_id: string;\n status: string;\n metrics?: Record<string, unknown>;\n }[]\n >(`/v1/conversations/${encodeURIComponent(conversationId)}/jobs`);\n const completedJobs = jobs.filter(\n (j: { status: string }) => j.status === \"completed\",\n );\n\n // User prompts aren't persisted in job_events — they live in\n // the messages table. Pair each completed job with its user\n // prompt by chronological index: the N-th completed job was\n // triggered by the N-th user message. Feed the content into\n // ``switchConversation`` so it emits a ``user_message`` event\n // at the right boundary during replay.\n const userMessages = this.session.messages.filter(\n (m) => m.role === \"user\",\n );\n\n for (let i = 0; i < completedJobs.length; i++) {\n const job = completedJobs[i]!;\n const userContent = userMessages[i]?.content;\n await this.session.switchConversation(\n conversationId,\n job.job_id,\n userContent,\n );\n }\n\n if (completedJobs.length > 0) {\n this.emit({\n type: \"versionsReady\",\n conversationId,\n count: completedJobs.length,\n });\n }\n } catch {\n // Version chain loading failed — non-blocking\n }\n\n this.setState(\"idle\");\n }\n }\n\n // ── Internal: set active conversation ─────────────────────────\n\n private setActiveConversation(id: string): void {\n this._activeConversationId = id;\n this.emit({ type: \"conversationChanged\", conversationId: id });\n }\n}\n","// =============================================================================\n// Event replay — translate persisted wire events into ChatEvent sequences\n//\n// The backend persists wire events in the `job_events` table. When a\n// consumer needs to restore a conversation (page refresh, conversation\n// switch), it fetches these raw events and replays them through the same\n// ChatEvent pipeline used during live streaming.\n//\n// All wire → ChatEvent translation lives in `translate.ts`. This file just\n// adapts the persisted envelope shape (`{seq, event, data}`) and handles\n// user-message interleaving, which the backend doesn't record.\n// =============================================================================\n\nimport { translateWireEvent } from \"./translate.js\";\nimport type { ChatEvent, WireEvent } from \"./types.js\";\n\n/**\n * Raw SSE event shape returned by GET /v1/conversations/{id}/events.\n * Mirrors what JobEventWriter persists to the job_events table.\n */\nexport interface RawSseEvent {\n seq: number;\n event: string;\n data: Record<string, unknown>;\n /** Epoch ms when the event was persisted (from job_events.created_at). */\n created_at?: number;\n}\n\n/**\n * Build a ``WireEvent`` from the persisted envelope. The data payload is\n * authoritative (and always carries ``type`` in practice); the SSE event\n * name is a fallback for older rows that pre-date the v2 protocol.\n */\nfunction toWireEvent(raw: RawSseEvent): WireEvent | null {\n const type = (raw.data.type as string) || raw.event;\n if (!type || type === \"done\") return null;\n return { ...raw.data, type } as unknown as WireEvent;\n}\n\n/**\n * Map a raw SSE event (persisted in job_events) into the SDK ChatEvent\n * format. Returns an array because some rows (malformed / ``done`` sentinels)\n * map to zero events.\n */\nexport function mapSseToChat(raw: RawSseEvent): ChatEvent[] {\n const wire = toWireEvent(raw);\n if (!wire) return [];\n const event = translateWireEvent(wire);\n return event ? [event] : [];\n}\n\n/**\n * Replay persisted SSE events through the provided handler, interleaving\n * user messages from session.messages at the first message_start of each\n * turn (user messages aren't persisted in job_events).\n */\nexport function replayEvents(\n sseEvents: RawSseEvent[],\n userMessages: { role: string; content: string }[],\n handleEvent: (event: ChatEvent) => void,\n addBlock: (block: { type: \"user\"; id: string; content: string }) => void,\n): void {\n const userMsgs = userMessages.filter((m) => m.role === \"user\");\n let userIdx = 0;\n let expectingUserMessage = true;\n\n for (const raw of sseEvents) {\n const type = (raw.data.type as string) || raw.event;\n\n if (type === \"message_stop\") {\n expectingUserMessage = true;\n }\n\n if (\n type === \"message_start\" &&\n expectingUserMessage &&\n userIdx < userMsgs.length\n ) {\n const userMsg = userMsgs[userIdx]!;\n addBlock({\n type: \"user\",\n id: `replay_user_${userIdx}`,\n content: userMsg.content,\n });\n userIdx++;\n expectingUserMessage = false;\n }\n\n for (const ce of mapSseToChat(raw)) {\n handleEvent(ce);\n }\n }\n}\n","// =============================================================================\n// Embedded Resource detection — protocol-agnostic\n// =============================================================================\n//\n// The backend can emit rich UI surfaces (A2UI, and future protocols) by\n// wrapping a tool result in an MCP-style embedded resource:\n//\n// {\n// \"_embedded_resource\": true,\n// \"mime_type\": \"application/json+a2ui\",\n// \"uri\": \"a2ui://surface/<id>\",\n// \"payload\": { ...protocol-specific... }\n// }\n//\n// The SDK stays protocol-agnostic: it exposes a detector/parser so\n// frontends can route the payload to a registered renderer for the\n// matching MIME type. The SDK itself never imports a renderer.\n// =============================================================================\n\n/**\n * Parsed shape of an MCP-style embedded resource, as emitted by the\n * backend's UI component tools (``render_surface``, ``update_surface``).\n */\nexport interface EmbeddedResource {\n /** IANA-style MIME type, e.g. \"application/json+a2ui\". */\n mimeType: string;\n /** Opaque URI (e.g. \"a2ui://surface/my-form\"). */\n uri: string;\n /** Protocol-specific payload — shape depends on ``mimeType``. */\n payload: Record<string, unknown>;\n}\n\n/**\n * Detect whether a value is an embedded resource wrapper. The check is\n * purposely loose — any object with ``_embedded_resource: true`` is\n * accepted, which matches the MCP convention and keeps the SDK\n * forward-compatible with future protocols.\n */\nexport function isEmbeddedResource(value: unknown): value is {\n _embedded_resource: true;\n mime_type?: string;\n uri?: string;\n payload?: Record<string, unknown>;\n} {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (value as { _embedded_resource?: unknown })._embedded_resource === true\n );\n}\n\n/**\n * Parse an embedded resource from arbitrary tool output. Returns\n * ``null`` when the value isn't an embedded resource or is malformed.\n *\n * Accepts either an object (the preferred wire format) or a JSON\n * string containing one (defense in depth — some transport layers\n * historically serialized tool results before sending).\n */\nexport function parseEmbeddedResource(value: unknown): EmbeddedResource | null {\n let candidate: unknown = value;\n if (typeof candidate === \"string\") {\n // Only try JSON.parse if it plausibly looks like a JSON object\n // starting with `{`. Avoids pathological input.\n const trimmed = candidate.trim();\n if (!trimmed.startsWith(\"{\")) return null;\n try {\n candidate = JSON.parse(trimmed);\n } catch {\n return null;\n }\n }\n if (!isEmbeddedResource(candidate)) return null;\n const mimeType = candidate.mime_type;\n const uri = candidate.uri;\n const payload = candidate.payload;\n if (typeof mimeType !== \"string\" || !mimeType) return null;\n if (typeof uri !== \"string\" || !uri) return null;\n if (!payload || typeof payload !== \"object\") return null;\n return {\n mimeType,\n uri,\n payload: payload as Record<string, unknown>,\n };\n}\n"],"mappings":";AAUO,IAAM,kBAAN,cAA8B,MAAM;AAAA,EACzC,YACE,SACO,MACP;AACA,UAAM,OAAO;AAFN;AAGP,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,sBAAN,cAAkC,gBAAgB;AAAA,EACvD,YAAY,UAAU,8BAA8B;AAClD,UAAM,SAAS,sBAAsB;AACrC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,iBAAN,cAA6B,gBAAgB;AAAA,EASlD,YACE,UAAU,uBACV,UAAiC,CAAC,GAClC;AACA,UAAM,SAAS,kBAAkB;AACjC,SAAK,OAAO;AACZ,WAAO,OAAO,MAAM,OAAO;AAAA,EAC7B;AACF;AAEO,IAAM,wBAAN,cAAoC,gBAAgB;AAAA,EACzD,YAAY,UAAU,gDAAgD;AACpE,UAAM,SAAS,oBAAoB;AACnC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,cAAN,cAA0B,gBAAgB;AAAA,EAC/C,YAAY,UAAU,yBAAyB;AAC7C,UAAM,SAAS,cAAc;AAC7B,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,kBAAN,cAA8B,gBAAgB;AAAA,EACnD,YAAY,UAAU,+BAA+B;AACnD,UAAM,SAAS,kBAAkB;AACjC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,qBAAN,cAAiC,gBAAgB;AAAA,EACtD,YAAY,UAAU,sBAAsB;AAC1C,UAAM,SAAS,gBAAgB;AAC/B,SAAK,OAAO;AAAA,EACd;AACF;;;ACxEO,SAAS,kBAAkB,MAAsB;AACtD,SAAO,KAAK,MAAM,GAAG,GAAG,EAAE,QAAQ,kBAAkB,mBAAmB;AACzE;AAEO,SAAS,aAAqB;AACnC,MAAI,OAAO,WAAW,eAAe,OAAO,YAAY;AACtD,WAAO,OAAO,WAAW;AAAA,EAC3B;AAEA,SAAO,uCAAuC,QAAQ,SAAS,CAAC,MAAM;AACpE,UAAM,IAAK,KAAK,OAAO,IAAI,KAAM;AACjC,UAAM,IAAI,MAAM,MAAM,IAAK,IAAI,IAAO;AACtC,WAAO,EAAE,SAAS,EAAE;AAAA,EACtB,CAAC;AACH;;;ACXA,IAAM,kBAAkB;AAExB,SAAS,YAAY,OAAoC;AACvD,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,GAAG;AACvD,WAAO;AAAA,EACT;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,UAAU,MAAM,KAAK;AAC3B,QAAI,CAAC,SAAS;AACZ,aAAO;AAAA,IACT;AACA,UAAM,SAAS,OAAO,OAAO;AAC7B,QAAI,OAAO,SAAS,MAAM,GAAG;AAC3B,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,YAAY,OAAoC;AACvD,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AACA,QAAM,UAAU,MAAM,KAAK;AAC3B,SAAO,UAAU,UAAU;AAC7B;AAEA,SAAS,gBAAgB,SAAsD;AAC7E,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,SAAkB,KAAK,MAAM,OAAO;AAC1C,QAAI,UAAU,OAAO,WAAW,UAAU;AACxC,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEA,SAAS,sBAAsB,OAA0C;AACvE,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,YAAY,KAAK;AACjC,MAAI,YAAY,QAAW;AACzB,WAAO,KAAK,IAAI,GAAG,KAAK,KAAK,OAAO,CAAC;AAAA,EACvC;AAEA,QAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,MAAI,OAAO,SAAS,MAAM,GAAG;AAC3B,UAAM,SAAS,SAAS,KAAK,IAAI;AACjC,WAAO,KAAK,IAAI,GAAG,KAAK,KAAK,SAAS,GAAI,CAAC;AAAA,EAC7C;AAEA,SAAO;AACT;AAEA,SAAS,oBAAoB,OAAoC;AAC/D,QAAM,UAAU,YAAY,KAAK;AACjC,MAAI,YAAY,QAAW;AACzB,QAAI,UAAU,MAAmB;AAC/B,aAAO,KAAK,MAAM,OAAO;AAAA,IAC3B;AACA,WAAO,KAAK,MAAM,UAAU,GAAI;AAAA,EAClC;AAEA,QAAM,WAAW,YAAY,KAAK;AAClC,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,KAAK,MAAM,QAAQ;AAClC,MAAI,OAAO,SAAS,MAAM,GAAG;AAC3B,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,UACP,SACA,MACA,QACe;AACf,aAAW,OAAO,MAAM;AACtB,UAAM,SAAS,OAAO,QAAQ,GAAG,CAAC;AAClC,QAAI,WAAW,QAAW;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,sBACP,SACA,SACuB;AACvB,QAAM,mBAAmB,UACrB,sBAAsB,QAAQ,IAAI,aAAa,CAAC,IAChD;AACJ,QAAM,iBAAiB;AAAA,IACrB;AAAA,IACA,CAAC,eAAe,cAAc,mBAAmB,eAAe;AAAA,IAChE;AAAA,EACF;AAEA,QAAM,gBAAgB,kBAAkB;AAExC,QAAM,cAAc,UAChB;AAAA,IACE,QAAQ,IAAI,mBAAmB,KAAK,QAAQ,IAAI,sBAAsB;AAAA,EACxE,IACA;AACJ,QAAM,YAAY;AAAA,IAChB,QAAQ,YAAY,QAAQ,WAAW,QAAQ;AAAA,EACjD;AAEA,QAAM,UACJ,aACA,gBACC,kBAAkB,SACf,KAAK,IAAI,IAAI,gBAAgB,MAC7B;AAEN,QAAM,QACJ,UAAU,SAAS,CAAC,SAAS,cAAc,KAAK,GAAG,WAAW,MAC7D,UAAU,YAAY,QAAQ,IAAI,mBAAmB,CAAC,IAAI;AAE7D,QAAM,YACJ,UAAU,SAAS,CAAC,aAAa,sBAAsB,GAAG,WAAW,MACpE,UAAU,YAAY,QAAQ,IAAI,uBAAuB,CAAC,IAAI;AAEjE,QAAM,QACJ,UAAU,SAAS,CAAC,SAAS,aAAa,GAAG,WAAW,MACvD,UAAU,YAAY,QAAQ,IAAI,mBAAmB,CAAC,IAAI;AAE7D,QAAM,WACJ,UAAU,SAAS,CAAC,aAAa,YAAY,QAAQ,GAAG,WAAW,MAClE,UACG;AAAA,IACE,QAAQ,IAAI,oBAAoB,KAC9B,QAAQ,IAAI,uBAAuB;AAAA,EACvC,IACA;AAEN,QAAM,YACJ,UAAU,SAAS,CAAC,cAAc,WAAW,GAAG,WAAW,MAC1D,UACG;AAAA,IACE,QAAQ,IAAI,cAAc,KAAK,QAAQ,IAAI,kBAAkB;AAAA,EAC/D,IACA;AAEN,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAiBO,SAAS,6BACd,UACA,SACgB;AAChB,QAAM,UAAU,gBAAgB,OAAO,KAAK,CAAC;AAC7C,QAAM,UAAU,sBAAsB,SAAS,SAAS,OAAO;AAE/D,QAAM,gBAAgB,kBAAkB,OAAO;AAC/C,QAAM,UACJ,UAAU,SAAS,CAAC,WAAW,mBAAmB,GAAG,WAAW,MAC/D,iBAAiB;AAEpB,SAAO,IAAI,eAAe,SAAS,OAAO;AAC5C;;;ACzLA,gBAAuB,aACrB,SACiC;AACjC,QAAM,EAAE,KAAK,SAAS,QAAQ,QAAQ,IAAI;AAE1C,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,QAAQ,KAAK;AAAA,MAC5B,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,QAAI,eAAe,gBAAgB,IAAI,SAAS,cAAc;AAC5D,YAAM,IAAI,mBAAmB;AAAA,IAC/B;AACA,UAAM,IAAI;AAAA,MACR,eAAe,QAAQ,IAAI,UAAU;AAAA,IACvC;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,UAAU,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACpD,UAAM,OAAO,UAAU,kBAAkB,OAAO,IAAI;AACpD,YAAQ,SAAS,QAAQ;AAAA,MACvB,KAAK;AACH,cAAM,IAAI,oBAAoB;AAAA,MAChC,KAAK;AACH,cAAM,6BAA6B,UAAU,OAAO;AAAA,MACtD;AACE,cAAM,IAAI,YAAY,QAAQ,QAAQ,SAAS,MAAM,EAAE;AAAA,IAC3D;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,MAAM;AAClB,UAAM,IAAI,gBAAgB,uBAAuB;AAAA,EACnD;AAEA,QAAM,SAAS,SAAS,KAAK,UAAU;AACvC,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,SAAS;AACb,MAAI,eAAe;AAEnB,MAAI;AACF,WAAO,MAAM;AACX,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,KAAM;AAEV,gBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAChD,YAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,eAAS,MAAM,IAAI,KAAK;AAExB,iBAAW,QAAQ,OAAO;AACxB,YAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,yBAAe,KAAK,MAAM,CAAC,EAAE,KAAK;AAAA,QACpC,WAAW,KAAK,WAAW,QAAQ,GAAG;AACpC,gBAAM,OAAO,KAAK,MAAM,CAAC;AACzB,cAAI,SAAS,UAAU;AACrB;AAAA,UACF;AACA,gBAAM,EAAE,OAAO,gBAAgB,WAAW,KAAK;AAAA,QACjD;AACA,YAAI,SAAS,IAAI;AACf,yBAAe;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,eAAe,gBAAgB,IAAI,SAAS,cAAc;AAC5D,YAAM,IAAI,mBAAmB;AAAA,IAC/B;AACA,UAAM;AAAA,EACR,UAAE;AACA,WAAO,YAAY;AAAA,EACrB;AACF;;;AC3DA,IAAM,mBAAmB;AAEzB,SAAS,gBAAgB,KAAqB;AAC5C,QAAM,UAAU,IAAI,QAAQ,QAAQ,EAAE;AACtC,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,OAAO;AAC9B,QAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAS;AAC/D,YAAM,IAAI;AAAA,QACR,6BAA6B,OAAO,QAAQ;AAAA,MAC9C;AAAA,IACF;AACA,WAAO,OAAO,SAAS,OAAO,SAAS,QAAQ,QAAQ,EAAE;AAAA,EAC3D,SAAS,KAAK;AACZ,QAAI,eAAe,SAAS,IAAI,QAAQ,SAAS,iBAAiB,GAAG;AACnE,YAAM;AAAA,IACR;AACA,UAAM,IAAI,MAAM,qBAAqB,OAAO,sBAAsB;AAAA,EACpE;AACF;AAEA,SAAS,eACP,QACkC;AAClC,SAAO,YAAY;AACrB;AAcO,IAAM,mBAAN,MAAuB;AAAA,EAU5B,YAAY,QAA0B;AACpC,QAAI,eAAe,MAAM,GAAG;AAC1B,UAAI,CAAC,OAAO,UAAU,OAAO,OAAO,WAAW,UAAU;AACvD,cAAM,IAAI,MAAM,mDAAmD;AAAA,MACrE;AACA,UAAI,CAAC,OAAO,UAAU,OAAO,OAAO,WAAW,UAAU;AACvD,cAAM,IAAI,MAAM,oCAAoC;AAAA,MACtD;AACA,WAAK,OAAO;AAAA,QACV,MAAM;AAAA,QACN,QAAQ,OAAO;AAAA,QACf,QAAQ,OAAO;AAAA,MACjB;AAAA,IACF,OAAO;AACL,UAAI,CAAC,OAAO,eAAe,OAAO,OAAO,gBAAgB,UAAU;AACjE,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAIA,YAAM,YACJ,OAAO,OAAO,cAAc,YAAY,OAAO,UAAU,SAAS,IAC9D,OAAO,YACP;AACN,WAAK,OAAO;AAAA,QACV,MAAM;AAAA,QACN,aAAa,OAAO;AAAA,QACpB;AAAA,QACA,WACE,OAAO,OAAO,cAAc,YAAY,OAAO,UAAU,SAAS,IAC9D,OAAO,YACP;AAAA,MACR;AAAA,IACF;AAEA,SAAK,UAAU,gBAAgB,OAAO,WAAW,gBAAgB;AACjE,SAAK,UAAU,OAAO,SAAS,WAAW,MAAM,KAAK,UAAU;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,kBAAkB,aAA2B;AAC3C,QAAI,KAAK,KAAK,SAAS,cAAc;AACnC,YAAM,IAAI,MAAM,oDAAoD;AAAA,IACtE;AACA,QAAI,CAAC,eAAe,OAAO,gBAAgB,UAAU;AACnD,YAAM,IAAI,MAAM,wCAAwC;AAAA,IAC1D;AACA,SAAK,OAAO,EAAE,GAAG,KAAK,MAAM,YAAY;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAgB,WAAyB;AACvC,QAAI,KAAK,KAAK,SAAS,cAAc;AACnC,YAAM,IAAI,MAAM,kDAAkD;AAAA,IACpE;AACA,QAAI,CAAC,aAAa,OAAO,cAAc,UAAU;AAC/C,YAAM,IAAI,MAAM,sCAAsC;AAAA,IACxD;AACA,SAAK,OAAO,EAAE,GAAG,KAAK,MAAM,UAAU;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,gBAAgB,WAAgC;AAC9C,QAAI,KAAK,KAAK,SAAS,cAAc;AACnC,YAAM,IAAI,MAAM,kDAAkD;AAAA,IACpE;AACA,UAAM,aACJ,OAAO,cAAc,YAAY,UAAU,SAAS,IAAI,YAAY;AACtE,SAAK,OAAO,EAAE,GAAG,KAAK,MAAM,WAAW,WAAW;AAAA,EACpD;AAAA;AAAA,EAGA,IAAI,YAA2B;AAC7B,WAAO,KAAK,KAAK,SAAS,eAAe,KAAK,KAAK,YAAY;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAI,YAA2B;AAC7B,WAAO,KAAK,KAAK,SAAS,eAAe,KAAK,KAAK,YAAY;AAAA,EACjE;AAAA;AAAA,EAGA,IAAI,WAAqC;AACvC,WAAO,KAAK,KAAK;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAY,cAAsC;AAChD,QAAI,KAAK,KAAK,SAAS,WAAW;AAChC,aAAO;AAAA,QACL,eAAe,UAAU,KAAK,KAAK,MAAM;AAAA,QACzC,iBAAiB,KAAK,KAAK;AAAA,MAC7B;AAAA,IACF;AACA,UAAM,UAAkC;AAAA,MACtC,eAAe,UAAU,KAAK,KAAK,WAAW;AAAA,IAChD;AACA,QAAI,KAAK,KAAK,WAAW;AACvB,cAAQ,cAAc,IAAI,KAAK,KAAK;AAAA,IACtC;AACA,QAAI,KAAK,KAAK,WAAW;AACvB,cAAQ,eAAe,IAAI,KAAK,KAAK;AAAA,IACvC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,IAAY,UAAkC;AAC5C,WAAO;AAAA,MACL,GAAG,KAAK;AAAA,MACR,gBAAgB;AAAA,IAClB;AAAA,EACF;AAAA,EAEA,MAAc,QACZ,QACA,MACA,MACmB;AACnB,UAAM,WAAW,MAAM,KAAK,QAAQ,GAAG,KAAK,OAAO,GAAG,IAAI,IAAI;AAAA,MAC5D;AAAA,MACA,SAAS,KAAK;AAAA,MACd,MAAM,SAAS,SAAY,KAAK,UAAU,IAAI,IAAI;AAAA,IACpD,CAAC,EAAE,MAAM,CAAC,QAAQ;AAChB,YAAM,IAAI;AAAA,QACR,eAAe,QAAQ,IAAI,UAAU;AAAA,MACvC;AAAA,IACF,CAAC;AACD,UAAM,KAAK,YAAY,QAAQ;AAC/B,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,IAAO,MAA0B;AACrC,UAAM,WAAW,MAAM,KAAK,QAAQ,OAAO,IAAI;AAC/C,WAAO,SAAS,KAAK;AAAA,EACvB;AAAA,EAEA,MAAM,KAAQ,MAAc,MAA2B;AACrD,UAAM,WAAW,MAAM,KAAK,QAAQ,QAAQ,MAAM,IAAI;AACtD,WAAO,SAAS,KAAK;AAAA,EACvB;AAAA,EAEA,MAAc,IAAI,MAA6B;AAC7C,UAAM,KAAK,QAAQ,UAAU,IAAI;AAAA,EACnC;AAAA,EAEA,MAAc,YAAY,UAAmC;AAC3D,QAAI,SAAS,GAAI;AACjB,UAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,YAAQ,SAAS,QAAQ;AAAA,MACvB,KAAK;AACH,cAAM,IAAI,oBAAoB;AAAA,MAChC,KAAK;AACH,cAAM,6BAA6B,UAAU,IAAI;AAAA,MACnD,SAAS;AACP,cAAM,WAAW,OAAO,kBAAkB,IAAI,IAAI;AAClD,cAAM,IAAI,YAAY,YAAY,QAAQ,SAAS,MAAM,EAAE;AAAA,MAC7D;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,YAIH;AACD,WAAO,KAAK,IAAI,YAAY;AAAA,EAC9B;AAAA,EAEA,MAAM,mBAA2C;AAC/C,UAAM,MAAM,MAAM,KAAK,IAWpB,oBAAoB;AACvB,UAAM,KAAK,IAAI,iBAAiB,CAAC;AACjC,WAAO;AAAA,MACL,SAAS,IAAI;AAAA,MACb,eAAe,IAAI;AAAA,MACnB,aAAa,IAAI;AAAA,MACjB,UAAU,IAAI;AAAA,MACd,SAAS,IAAI;AAAA,MACb,cAAc;AAAA,QACZ,SAAS,QAAQ,GAAG,OAAO;AAAA,QAC3B,UAAU,GAAG,YAAY;AAAA,QACzB,UAAU,GAAG,aAAa;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,iBAAiB,QAAQ,IAAI,SAAS,GAA4B;AACtE,UAAM,YAAY,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,KAAK,MAAM,OAAO,KAAK,CAAC,CAAC,CAAC;AACtE,UAAM,aAAa,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,MAAM,CAAC,CAAC;AACzD,UAAM,MAAM,MAAM,KAAK,IAQrB,2BAA2B,SAAS,WAAW,UAAU,EAAE;AAC7D,WAAO,IAAI,IAAI,CAAC,OAAO;AAAA,MACrB,IAAI,EAAE;AAAA,MACN,OAAO,EAAE;AAAA,MACT,cAAc,EAAE;AAAA,MAChB,WAAW,EAAE;AAAA,MACb,WAAW,EAAE;AAAA,IACf,EAAE;AAAA,EACJ;AAAA,EAEA,MAAM,YAAY,gBAA4C;AAC5D,UAAM,MAAM,MAAM,KAAK,IASrB,qBAAqB,mBAAmB,cAAc,CAAC,WAAW;AACpE,WAAO,IAAI,IAAI,CAAC,OAAO;AAAA,MACrB,IAAI,EAAE;AAAA,MACN,gBAAgB,EAAE;AAAA,MAClB,MAAM,EAAE;AAAA,MACR,SAAS,EAAE;AAAA,MACX,UAAU,EAAE;AAAA,MACZ,QAAQ;AAAA,MACR,WAAW,EAAE;AAAA,IACf,EAAE;AAAA,EACJ;AAAA,EAEA,MAAM,mBAAmB,IAA2B;AAClD,UAAM,KAAK,IAAI,qBAAqB,mBAAmB,EAAE,CAAC,EAAE;AAAA,EAC9D;AAAA,EAEA,MAAM,YAAkC;AACtC,UAAM,MAAM,MAAM,KAAK,IASrB,YAAY;AACd,WAAO,IAAI,IAAI,CAAC,OAAO;AAAA,MACrB,MAAM,EAAE;AAAA,MACR,aAAa,EAAE;AAAA,MACf,aAAa,EAAE;AAAA,MACf,gBAAgB,EAAE;AAAA,MAClB,WAAW,EAAE;AAAA,MACb,WAAW,EAAE;AAAA,IACf,EAAE;AAAA,EACJ;AAAA,EAEA,MAAM,YAAkC;AACtC,UAAM,MAAM,MAAM,KAAK,IAOrB,YAAY;AACd,WAAO,IAAI,IAAI,CAAC,OAAO;AAAA,MACrB,MAAM,EAAE;AAAA,MACR,aAAa,EAAE;AAAA,MACf,aAAa,EAAE;AAAA,MACf,WAAW,EAAE;AAAA,IACf,EAAE;AAAA,EACJ;AAAA,EAEA,MAAM,sBACJ,gBACA,OAC8B;AAC9B,QAAI,MAAM,qBAAqB,mBAAmB,cAAc,CAAC;AACjE,QAAI,MAAO,QAAO,WAAW,mBAAmB,KAAK,CAAC;AACtD,WAAO,KAAK,IAAI,GAAG;AAAA,EACrB;AAAA,EAEA,MAAM,iBAAiB,SAA2C;AAChE,UAAM,KAAK,KAAK,mBAAmB,OAAO;AAAA,EAC5C;AAAA,EAEA,MAAM,mBAAmB,SAA6C;AACpE,UAAM,KAAK,KAAK,qBAAqB,OAAO;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,qBAAqB,SAGG;AAC5B,UAAM,SAAS,IAAI,gBAAgB;AACnC,QAAI,SAAS,SAAS,MAAM;AAC1B,YAAM,YAAY,KAAK;AAAA,QACrB;AAAA,QACA,KAAK,IAAI,KAAK,KAAK,MAAM,OAAO,QAAQ,KAAK,CAAC,CAAC;AAAA,MACjD;AACA,aAAO,IAAI,SAAS,OAAO,SAAS,CAAC;AAAA,IACvC;AACA,QAAI,SAAS,UAAU,MAAM;AAC3B,YAAM,aAAa,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,QAAQ,MAAM,CAAC,CAAC;AACjE,aAAO,IAAI,UAAU,OAAO,UAAU,CAAC;AAAA,IACzC;AACA,UAAM,KAAK,OAAO,SAAS;AAC3B,UAAM,MAAM,MAAM,KAAK,IAYpB,0BAA0B,KAAK,IAAI,EAAE,KAAK,EAAE,EAAE;AACjD,WAAO;AAAA,MACL,QAAQ,IAAI,OAAO,IAAI,CAAC,OAAO;AAAA,QAC7B,IAAI,EAAE;AAAA,QACN,UAAU,EAAE;AAAA,QACZ,UAAU,EAAE;AAAA,QACZ,OAAO,EAAE;AAAA,QACT,gBAAgB,EAAE;AAAA,QAClB,WAAW,EAAE;AAAA,MACf,EAAE;AAAA,MACF,OAAO,IAAI;AAAA,MACX,OAAO,IAAI;AAAA,MACX,QAAQ,IAAI;AAAA,IACd;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,qBAAqB,IAA2B;AACpD,UAAM,KAAK,IAAI,2BAA2B,mBAAmB,EAAE,CAAC,EAAE;AAAA,EACpE;AAAA;AAAA,EAIQ,SAAS,KAAiD;AAChE,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,cAAc,IAAI;AAAA,MAClB,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,MACf,eAAe,IAAI;AAAA,MACnB,iBAAiB,IAAI;AAAA,MACrB,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,MAAM,WACJ,gBACA,MACA,UAC4B;AAC5B,UAAM,WAAW,IAAI,SAAS;AAC9B,aAAS,OAAO,QAAQ,MAAM,QAAQ;AAEtC,UAAM,WAAW,MAAM,KAAK;AAAA,MAC1B,GAAG,KAAK,OAAO,qBAAqB,mBAAmB,cAAc,CAAC;AAAA,MACtE;AAAA,QACE,QAAQ;AAAA,QACR,SAAS,KAAK;AAAA,QACd,MAAM;AAAA,MACR;AAAA,IACF,EAAE,MAAM,CAAC,QAAQ;AACf,YAAM,IAAI;AAAA,QACR,eAAe,QAAQ,IAAI,UAAU;AAAA,MACvC;AAAA,IACF,CAAC;AACD,UAAM,KAAK,YAAY,QAAQ;AAC/B,UAAM,MAAM,MAAM,SAAS,KAAK;AAChC,WAAO,KAAK,SAAS,GAA8B;AAAA,EACrD;AAAA,EAEA,MAAM,YAAY,gBAAsD;AACtE,UAAM,MAAM,MAAM,KAAK;AAAA,MACrB,qBAAqB,mBAAmB,cAAc,CAAC;AAAA,IACzD;AACA,WAAO,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;AAAA,EACxC;AAAA,EAEA,MAAM,YAAY,gBAAsD;AACtE,UAAM,MAAM,MAAM,KAAK;AAAA,MACrB,qBAAqB,mBAAmB,cAAc,CAAC;AAAA,IACzD;AACA,WAAO,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,YAAoC;AACxC,UAAM,MAAM,MAAM,KAAK,IAQrB,WAAW;AACb,WAAO,IAAI,IAAI,CAAC,OAAO;AAAA,MACrB,IAAI,EAAE;AAAA,MACN,MAAM,EAAE;AAAA,MACR,MAAM,EAAE;AAAA,MACR,WAAW,EAAE;AAAA,MACb,MAAM,EAAE;AAAA,IACV,EAAE;AAAA,EACJ;AAAA,EAEA,MAAM,aAAa,QAA2C;AAC5D,UAAM,MAAM,MAAM,KAAK,IAQrB,aAAa,mBAAmB,MAAM,CAAC,WAAW;AACpD,WAAO,IAAI,IAAI,CAAC,OAAO;AAAA,MACrB,IAAI,EAAE;AAAA,MACN,MAAM,EAAE;AAAA,MACR,QAAQ,EAAE;AAAA,MACV,WAAW,EAAE;AAAA,MACb,WAAW,EAAE;AAAA,IACf,EAAE;AAAA,EACJ;AAAA;AAAA,EAIA,MAAM,UAAU,SAAwD;AACtE,WAAO,KAAK,KAAwB,YAAY,OAAO;AAAA,EACzD;AAAA,EAEA,OAAO,gBACL,OACA,WAAW,IACX,QACiC;AACjC,UAAM,MAAM,GAAG,KAAK,OAAO,YAAY,mBAAmB,KAAK,CAAC,iBAAiB,QAAQ;AACzF,WAAO,aAAa;AAAA,MAClB;AAAA,MACA,SAAS,KAAK;AAAA,MACd;AAAA,MACA,SAAS,KAAK;AAAA,IAChB,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,UAAU,OAA8B;AAC5C,UAAM,KAAK,KAAK,YAAY,mBAAmB,KAAK,CAAC,WAAW,CAAC,CAAC;AAAA,EACpE;AAAA,EAEA,MAAM,OAAO,OAAmC;AAC9C,UAAM,MAAM,MAAM,KAAK,IASpB,YAAY,mBAAmB,KAAK,CAAC,EAAE;AAC1C,WAAO;AAAA,MACL,OAAO,IAAI;AAAA,MACX,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI,cAAc;AAAA,MAC7B,WAAW,IAAI,cAAc;AAAA,MAC7B,aAAa,IAAI,gBAAgB;AAAA,MACjC,cAAc,IAAI,iBAAiB;AAAA,MACnC,aAAa,IAAI,gBAAgB;AAAA,MACjC,cAAc,IAAI,iBAAiB;AAAA,IACrC;AAAA,EACF;AAAA,EAEA,MAAM,eACJ,OACA,SAC2B;AAC3B,UAAM,OAA6C;AAAA,MACjD,QAAQ,QAAQ;AAAA,IAClB;AACA,QAAI,QAAQ,WAAW,KAAM,MAAK,UAAU,QAAQ;AACpD,UAAM,MAAM,MAAM,KAAK,KAMpB,YAAY,mBAAmB,KAAK,CAAC,aAAa,IAAI;AACzD,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,OAAO,IAAI;AAAA,MACX,QAAQ,IAAI;AAAA,MACZ,SAAS,IAAI;AAAA,MACb,WAAW,IAAI;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,gBAA4C;AAC7D,UAAM,MAAM,MAAM,KAAK,IAGpB,qBAAqB,mBAAmB,cAAc,CAAC,aAAa;AACvE,WAAO;AAAA,MACL,OAAO,IAAI,UAAU;AAAA,MACrB,QAAQ,IAAI;AAAA,IACd;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,gBAA+C;AAC5D,UAAM,MAAM,MAAM,KAAK,IASrB,qBAAqB,mBAAmB,cAAc,CAAC,OAAO;AAChE,WAAO,IAAI,IAAI,CAAC,OAAO;AAAA,MACrB,OAAO,EAAE;AAAA,MACT,QAAQ,EAAE;AAAA,MACV,eAAe,EAAE,mBAAmB;AAAA,MACpC,iBAAiB,EAAE,oBAAoB;AAAA,MACvC,SAAS,EAAE,WAAW;AAAA,MACtB,WAAW,EAAE,cAAc;AAAA,IAC7B,EAAE;AAAA,EACJ;AACF;;;AC3oBO,IAAM,kBAAN,MAA6C;AAAA,EAA7C;AACL,SAAQ,gBAAgB,oBAAI,IAA0B;AACtD,SAAQ,WAAW,oBAAI,IAAuB;AAAA;AAAA,EAE9C,MAAM,qBAA8C;AAClD,WAAO,MAAM,KAAK,KAAK,cAAc,OAAO,CAAC,EAAE;AAAA,MAC7C,CAAC,GAAG,MACF,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,IAAI,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ;AAAA,IACpE;AAAA,EACF;AAAA,EAEA,MAAM,kBAAkB,IAA0C;AAChE,WAAO,KAAK,cAAc,IAAI,EAAE,KAAK;AAAA,EACvC;AAAA,EAEA,MAAM,mBAAmB,IAAY,OAAsC;AACzE,UAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,UAAM,eAA6B;AAAA,MACjC;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AACA,SAAK,cAAc,IAAI,IAAI,YAAY;AACvC,SAAK,SAAS,IAAI,IAAI,CAAC,CAAC;AACxB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,wBAAwB,IAAY,OAA8B;AACtE,UAAM,OAAO,KAAK,cAAc,IAAI,EAAE;AACtC,QAAI,MAAM;AACR,WAAK,QAAQ;AACb,WAAK,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IAC1C;AAAA,EACF;AAAA,EAEA,MAAM,mBAAmB,IAA2B;AAClD,SAAK,cAAc,OAAO,EAAE;AAC5B,SAAK,SAAS,OAAO,EAAE;AAAA,EACzB;AAAA,EAEA,MAAM,cAAc,gBAA4C;AAC9D,WAAO,KAAK,SAAS,IAAI,cAAc,KAAK,CAAC;AAAA,EAC/C;AAAA,EAEA,MAAM,WAAW,SAAkB,gBAAuC;AACxE,UAAM,OAAO,KAAK,SAAS,IAAI,cAAc,KAAK,CAAC;AACnD,SAAK,KAAK,OAAO;AACjB,SAAK,SAAS,IAAI,gBAAgB,IAAI;AAEtC,UAAM,OAAO,KAAK,cAAc,IAAI,cAAc;AAClD,QAAI,MAAM;AACR,WAAK,eAAe,KAAK;AACzB,WAAK,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IAC1C;AAAA,EACF;AAAA,EAEA,MAAM,oBACJ,IACA,QACe;AACf,eAAW,QAAQ,KAAK,SAAS,OAAO,GAAG;AACzC,YAAM,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;AACxC,UAAI,KAAK;AACP,YAAI,SAAS;AACb;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,IAA2B;AAC7C,eAAW,CAAC,QAAQ,IAAI,KAAK,KAAK,SAAS,QAAQ,GAAG;AACpD,YAAM,MAAM,KAAK,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE;AAC7C,UAAI,QAAQ,IAAI;AACd,aAAK,OAAO,KAAK,CAAC;AAClB,cAAM,OAAO,KAAK,cAAc,IAAI,MAAM;AAC1C,YAAI,MAAM;AACR,eAAK,eAAe,KAAK;AAAA,QAC3B;AACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;ACtFA,IAAM,oBAAoB;AAG1B,SAAS,aAAa,MAAwD;AAC5E,QAAM,QAAiC,uBAAO,OAAO,IAAI;AACzD,aAAW,OAAO,OAAO,KAAK,IAAI,GAAG;AACnC,QAAI,QAAQ,eAAe,QAAQ,iBAAiB,QAAQ,aAAa;AACvE;AAAA,IACF;AACA,UAAM,GAAG,IAAI,KAAK,GAAG;AAAA,EACvB;AACA,SAAO;AACT;AAEO,IAAM,eAAN,MAAmB;AAAA,EAAnB;AACL,SAAQ,QAAQ,oBAAI,IAA4B;AAAA;AAAA,EAEhD,aACE,MACA,aACA,aACA,SACM;AACN,QAAI,CAAC,QAAQ,CAAC,kBAAkB,KAAK,IAAI,GAAG;AAC1C,YAAM,IAAI;AAAA,QACR,sBAAsB,IAAI,kBAAkB,iBAAiB;AAAA,MAC/D;AAAA,IACF;AACA,QAAI,KAAK,SAAS,KAAK;AACrB,YAAM,IAAI,MAAM,2CAA2C;AAAA,IAC7D;AACA,SAAK,MAAM,IAAI,MAAM,EAAE,MAAM,aAAa,aAAa,QAAQ,CAAC;AAAA,EAClE;AAAA,EAEA,eAAe,MAAuB;AACpC,WAAO,KAAK,MAAM,OAAO,IAAI;AAAA,EAC/B;AAAA,EAEA,QAAQ,MAAuB;AAC7B,WAAO,KAAK,MAAM,IAAI,IAAI;AAAA,EAC5B;AAAA,EAEA,MAAM,YAAY,SAA+C;AAC/D,UAAM,OAAO,KAAK,MAAM,IAAI,QAAQ,QAAQ;AAC5C,QAAI,CAAC,MAAM;AACT,aAAO;AAAA,QACL,SAAS,QAAQ;AAAA,QACjB,WAAW,QAAQ;AAAA,QACnB,QAAQ,SAAS,QAAQ,QAAQ;AAAA,QACjC,UAAU;AAAA,MACZ;AAAA,IACF;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,QAAQ,aAAa,QAAQ,SAAS,CAAC;AACjE,aAAO;AAAA,QACL,SAAS,QAAQ;AAAA,QACjB,WAAW,QAAQ;AAAA,QACnB;AAAA,QACA,UAAU;AAAA,MACZ;AAAA,IACF,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,SAAS,QAAQ;AAAA,QACjB,WAAW,QAAQ;AAAA,QACnB,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QACvD,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAAA,EAEA,cAAgC;AAC9B,WAAO,MAAM,KAAK,KAAK,MAAM,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO;AAAA,MACjD,MAAM,EAAE;AAAA,MACR,aAAa,EAAE;AAAA,MACf,YAAY,EAAE;AAAA,IAChB,EAAE;AAAA,EACJ;AAAA,EAEA,eAAyB;AACvB,WAAO,MAAM,KAAK,KAAK,MAAM,KAAK,CAAC;AAAA,EACrC;AAAA,EAEA,QAAc;AACZ,SAAK,MAAM,MAAM;AAAA,EACnB;AACF;;;AC/DO,IAAM,mBAAN,MAAoE;AAAA,EAApE;AACL,SAAQ,WAAW,oBAAI,IAAe;AAAA;AAAA;AAAA,EAGtC,SAAS,SAAkB;AACzB,SAAK,SAAS,IAAI,QAAQ,UAAU,OAAO;AAAA,EAC7C;AAAA;AAAA,EAGA,WAAW,UAAwB;AACjC,SAAK,SAAS,OAAO,QAAQ;AAAA,EAC/B;AAAA;AAAA,EAGA,IAAI,UAA4B;AAC9B,WAAO,KAAK,SAAS,IAAI,QAAQ,KAAK;AAAA,EACxC;AAAA,EAEA,IAAI,UAA2B;AAC7B,WAAO,KAAK,SAAS,IAAI,QAAQ;AAAA,EACnC;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,SAAS,MAAM;AAAA,EACtB;AAAA,EAEA,gBAA0B;AACxB,WAAO,MAAM,KAAK,KAAK,SAAS,KAAK,CAAC;AAAA,EACxC;AACF;;;ACzCO,SAAS,eACd,MAC0B;AAC1B,UAAQ,KAAK,SAAS;AAAA,IACpB,KAAK;AACH,aAAO,EAAE,SAAS,QAAQ,MAAM,KAAK,KAAK;AAAA,IAC5C,KAAK;AACH,aAAO,EAAE,SAAS,YAAY,MAAM,KAAK,KAAK;AAAA,IAChD,KAAK;AACH,aAAO,EAAE,SAAS,aAAa,WAAW,KAAK,UAAU;AAAA,IAC3D,KAAK;AACH,aAAO,EAAE,SAAS,SAAS,aAAa,KAAK,aAAa;AAAA,IAC5D,KAAK;AACH,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,KAAK;AAAA,QACd,MAAM,KAAK;AAAA,MACb;AAAA,IACF,KAAK;AACH,aAAO,EAAE,SAAS,UAAU,QAAQ,KAAK,QAAQ,OAAO,KAAK,MAAM;AAAA,IACrE,KAAK;AACH,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ,KAAK;AAAA,QACb,MAAM,KAAK;AAAA,MACb;AAAA,IACF;AACE,aAAO;AAAA,EACX;AACF;AAIA,SAAS,uBAAuB,KAA8B;AAC5D,SAAO;AAAA,IACL,MAAO,IAAI,QAAmB;AAAA,IAC9B,aAAc,IAAI,gBAAkC;AAAA,IACpD,WAAY,IAAI,cAAgC;AAAA,IAChD,aAAc,IAAI,eAAiC;AAAA,EACrD;AACF;AAOO,SAAS,qBACd,MACA,MACW;AACX,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAU,KAAK,WAAsB;AAAA,QACrC,WAAW,KAAK;AAAA,MAClB;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAQ,KAAK,SAAoB;AAAA,MACnC;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAQ,KAAK,SAAwB,CAAC;AAAA,MACxC;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAU,KAAK,WAAuC,CAAC;AAAA,QACvD,OAAQ,KAAK,SAA2B;AAAA,QACxC,WAAY,KAAK,cAAgC;AAAA,MACnD;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO;AAAA,UACJ,KAAK,SAAqC,CAAC;AAAA,QAC9C;AAAA,QACA,YAAa,KAAK,gBAAkC;AAAA,MACtD;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO;AAAA,UACJ,KAAK,SAAqC,CAAC;AAAA,QAC9C;AAAA,QACA,YAAa,KAAK,gBAAkC;AAAA,MACtD;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAW,KAAK,YAAuB;AAAA,QACvC,gBAAiB,KAAK,mBAA8B;AAAA,QACpD,iBAAkB,KAAK,oBAA+B;AAAA,QACtD,cAAe,KAAK,iBAA4B;AAAA,QAChD,aAAc,KAAK,gBAA2B;AAAA,QAC9C,SAAU,KAAK,WAAsB;AAAA,MACvC;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAW,KAAK,YAA2C,CAAC;AAAA,MAC9D;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAS,KAAK,UAAqB;AAAA,QACnC,UAAW,KAAK,aAA+B;AAAA,QAC/C,KAAM,KAAK,OAAyB;AAAA,QACpC,WAAY,KAAK,aAA+B;AAAA,MAClD;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,KAAM,KAAK,OAAkB;AAAA,QAC7B,WAAY,KAAK,cAAgC;AAAA,MACnD;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,cAAe,KAAK,iBAA4B;AAAA,QAChD,UAAW,KAAK,YAAuB;AAAA,QACvC,aAAc,KAAK,gBAAkC;AAAA,QACrD,WAAY,KAAK,cAAgC;AAAA,MACnD;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,WAAY,KAAK,cAAyB;AAAA,QAC1C,eAAgB,KAAK,kBAAoC;AAAA,MAC3D;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAU,KAAK,YAAuB;AAAA,QACtC,UAAW,KAAK,YAAuB;AAAA,QACvC,KAAM,KAAK,OAAyB;AAAA,QACpC,aAAc,KAAK,gBAAkC;AAAA,MACvD;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAW,KAAK,aAAwB;AAAA,QACxC,QAAS,KAAK,WAAsB;AAAA,QACpC,WAAY,KAAK,aAAyC,CAAC;AAAA,QAC3D,WAAY,KAAK,cAAgC;AAAA,QACjD,QAAS,KAAK,UAA4B;AAAA,MAC5C;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAW,KAAK,aAAwB;AAAA,QACxC,QAAS,KAAK,WAAsB;AAAA,MACtC;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAW,KAAK,aAAwB;AAAA,QACxC,QAAS,KAAK,WAAsB;AAAA,QACpC,QAAS,KAAK,UAA4B;AAAA,QAC1C,UAAW,KAAK,aAA+B;AAAA,MACjD;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAW,KAAK,aAAwB;AAAA,QACxC,QAAS,KAAK,WAAsB;AAAA,QACpC,SAAU,KAAK,WAA6B;AAAA,QAC5C,SAAU,KAAK,WAA8C;AAAA,MAC/D;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,qBAAsB,KAAK,wBAAmC;AAAA,QAC9D,UAAW,KAAK,aAA+B;AAAA,MACjD;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,aAAc,KAAK,eAA4B,CAAC;AAAA,MAClD;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAQ,KAAK,SAAoB;AAAA,MACnC;AAAA,IACF;AACE,aAAO,EAAE,MAAM,UAAU,MAAM,KAAK;AAAA,EACxC;AACF;AAYA,SAAS,sBAAsB,MAA0C;AACvE,SAAO;AACT;AAOO,SAAS,mBAAmB,MAAmC;AAKpE,MAAK,KAA0B,SAAS,qBAAqB;AAC3D,WAAO;AAAA,MACL;AAAA,MACA,sBAAsB,IAAI;AAAA,IAC5B;AAAA,EACF;AACA,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,KAAK;AAAA,QACb,OAAO,KAAK;AAAA,QACZ,WAAW,KAAK;AAAA,QAChB,kBAAkB,KAAK;AAAA,QACvB,gBAAgB,KAAK;AAAA,MACvB;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,KAAK;AAAA,QACb,MAAM,KAAK;AAAA,QACX,YAAY,KAAK,eAAe;AAAA,QAChC,MAAM,KAAK;AAAA,QACX,UAAU,KAAK;AAAA,MACjB;AAAA,IACF,KAAK,eAAe;AAClB,YAAM,QAAQ,eAAe,KAAK,KAAK;AACvC,UAAI,CAAC,MAAO,QAAO;AACnB,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,KAAK;AAAA,QACb,MAAM,KAAK;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,IACA,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,KAAK;AAAA,QACb,MAAM,KAAK;AAAA,QACX,QAAQ,KAAK;AAAA,QACb,OAAO,KAAK;AAAA,MACd;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ,KAAK;AAAA,QACb,OAAO,KAAK;AAAA,QACZ,YAAY,KAAK;AAAA,QACjB,OAAO;AAAA,UACL,aAAa,KAAK,MAAM,gBAAgB;AAAA,UACxC,cAAc,KAAK,MAAM,iBAAiB;AAAA,UAC1C,cAAc,KAAK,MAAM,iBAAiB;AAAA,QAC5C;AAAA,QACA,QAAQ,KAAK;AAAA,QACb,SAAS,KAAK;AAAA,QACd,YAAY,KAAK;AAAA,MACnB;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,kBAAkB,KAAK;AAAA,QACvB,YAAY,KAAK;AAAA,MACnB;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS,KAAK;AAAA,QACd,QAAQ,KAAK;AAAA,QACb,WAAW,KAAK;AAAA,QAChB,UAAU,KAAK,YAAY;AAAA,QAC3B,aAAa,KAAK,gBAAgB;AAAA,QAClC,iBAAiB,KAAK,oBAAoB;AAAA,MAC5C;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,MAAM,KAAK;AAAA,QACX,SAAS,KAAK;AAAA,QACd,WAAW,KAAK,cAAc;AAAA,MAChC;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,kBAAkB,KAAK;AAAA,MACzB;AAAA,IACF,KAAK;AACH,aAAO,qBAAqB,KAAK,MAAM,KAAK,IAAI;AAAA,IAClD,SAAS;AAGP,YAAM,cAAqB;AAC3B,WAAK;AACL,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AC3TA,SAAS,WAAW,GAAa,GAAsB;AACrD,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,QAAI,EAAE,CAAC,MAAM,EAAE,CAAC,EAAG,QAAO;AAAA,EAC5B;AACA,SAAO;AACT;AAQO,IAAM,cAAN,MAAkB;AAAA,EAiCvB,YAAY,QAA0B,SAAuB;AAtB7D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,YAAY,IAAI,iBAAiB;AAG1C;AAAA,0BAAgC;AAChC,yBAAgC,CAAC;AACjC,oBAAsB,CAAC;AACvB,uBAAc;AACd,yBAAsC;AACtC,kBAAsB,CAAC;AACvB,kBAAsB,CAAC;AACvB,8BAAqB,oBAAI,IAAY;AACrC,4BAAkC;AAKlC;AAAA;AAAA;AAAA,SAAQ,kBAAkB;AAC1B,SAAQ,kBAAmC;AAE3C,SAAQ,WAAkC,oBAAI,IAAI;AAClD,SAAQ,kBAA0C;AAmIlD;AAAA,SAAQ,UAAU;AAGlB;AAAA,wBAA8B;AAnI5B,SAAK,SAAS,IAAI,iBAAiB,MAAM;AACzC,SAAK,eAAe,IAAI,aAAa;AACrC,SAAK,UAAU,WAAW,IAAI,gBAAgB;AAAA,EAChD;AAAA,EAEA,GAAG,SAAuC;AACxC,SAAK,SAAS,IAAI,OAAO;AACzB,WAAO,MAAM;AACX,WAAK,SAAS,OAAO,OAAO;AAAA,IAC9B;AAAA,EACF;AAAA,EAEQ,KAAK,OAAwB;AACnC,eAAW,WAAW,KAAK,UAAU;AACnC,UAAI;AACF,gBAAQ,KAAK;AAAA,MACf,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,UAAyB;AAC7B,UAAM,CAAC,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,QAAQ,WAAW;AAAA,MACvE,KAAK,OAAO,iBAAiB;AAAA,MAC7B,KAAK,OAAO,iBAAiB;AAAA,MAC7B,KAAK,OAAO,UAAU,EAAE,MAAM,MAAM,CAAC,CAAgB;AAAA,MACrD,KAAK,OAAO,UAAU,EAAE,MAAM,MAAM,CAAC,CAAgB;AAAA,IACvD,CAAC;AAED,QAAI,OAAO,WAAW,aAAa;AACjC,WAAK,gBAAgB,OAAO;AAAA,IAC9B;AACA,QAAI,cAAc,WAAW,aAAa;AACxC,WAAK,gBAAgB,cAAc;AAAA,IACrC;AACA,QAAI,OAAO,WAAW,aAAa;AACjC,WAAK,SAAS,OAAO;AAAA,IACvB;AACA,QAAI,OAAO,WAAW,aAAa;AACjC,WAAK,SAAS,OAAO;AAAA,IACvB;AAEA,SAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAAA,EACjC;AAAA,EAEA,MAAM,KAAK,SAAiB,SAAsC;AAChE,QAAI,KAAK,YAAa;AAEtB,UAAM,iBACJ,SAAS,kBAAkB,KAAK,kBAAkB;AAEpD,UAAM,cAAuB;AAAA,MAC3B,IAAI,WAAW;AAAA,MACf,gBAAgB,kBAAkB;AAAA,MAClC,MAAM;AAAA,MACN;AAAA,MACA,QAAQ;AAAA,MACR,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC;AACA,QAAI,gBAAgB;AAClB,YAAM,KAAK,QAAQ,WAAW,aAAa,cAAc;AAAA,IAC3D;AACA,SAAK,SAAS,KAAK,WAAW;AAE9B,UAAM,UAA6B;AAAA,MACjC,SAAS;AAAA,MACT,iBAAiB;AAAA,MACjB,cAAc,KAAK,aAAa,YAAY;AAAA,MAC5C,aAAa,MAAM;AAAA,QACjB,SAAS,sBAAsB,KAAK;AAAA,MACtC;AAAA,MACA,YAAY,SAAS;AAAA,MACrB,YAAY,SAAS;AAAA,MACrB,eAAe,SAAS;AAAA,MACxB,WAAW,SAAS;AAAA,IACtB;AAEA,UAAM,KAAK,cAAc,OAAO;AAAA,EAClC;AAAA,EAEA,MAAM,qBACJ,WACA,YACA,SACe;AACf,QAAI,KAAK,YAAa;AAEtB,UAAM,UAA6B;AAAA,MACjC,SAAS;AAAA,MACT,iBAAiB,KAAK,kBAAkB;AAAA,MACxC,aAAa;AAAA,MACb,cAAc,KAAK,aAAa,YAAY;AAAA,MAC5C,aAAa,MAAM,KAAK,KAAK,kBAAkB;AAAA,MAC/C,eAAe,SAAS;AAAA,IAC1B;AAEA,UAAM,KAAK,cAAc,OAAO;AAAA,EAClC;AAAA,EAEQ,sBAA4B;AAClC,SAAK,kBAAkB;AACvB,SAAK,kBAAkB;AAAA,EACzB;AAAA,EAEA,MAAc,cAAc,SAA2C;AACrE,SAAK,cAAc;AACnB,SAAK,oBAAoB;AACzB,SAAK,kBAAkB,IAAI,gBAAgB;AAE3C,QAAI;AACF,YAAM,KAAK,iBAAiB,OAAO;AAAA,IACrC,SAAS,KAAK;AACZ,UAAI,EAAE,eAAe,gBAAgB,IAAI,SAAS,eAAe;AAC/D,aAAK,KAAK;AAAA,UACR,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,UACxD,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IACF,UAAE;AACA,WAAK,cAAc;AACnB,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AAAA,EAQA,MAAc,iBAAiB,SAA2C;AACxE,UAAM,MAAM,MAAM,KAAK,OAAO,UAAU,OAAO;AAC/C,SAAK,eAAe,IAAI;AAExB,UAAM,iBAAiB,IAAI;AAC3B,QAAI,CAAC,KAAK,gBAAgB;AACxB,WAAK,iBAAiB;AAAA,IACxB;AAIA,QAAI,CAAC,KAAK,cAAc,KAAK,CAAC,MAAM,EAAE,OAAO,cAAc,GAAG;AAC5D,YAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,YAAM,OAAO;AAAA,QACX,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,cAAc;AAAA,QACd,WAAW;AAAA,QACX,WAAW;AAAA,MACb;AACA,WAAK,cAAc,QAAQ,IAAI;AAC/B,YAAM,KAAK,QAAQ,mBAAmB,gBAAgB,EAAE,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAC1E;AAGA,UAAM,UAAU,KAAK,SAAS,KAAK,SAAS,SAAS,CAAC;AACtD,QAAI,SAAS,SAAS,UAAU,CAAC,QAAQ,gBAAgB;AACvD,cAAQ,iBAAiB;AACzB,YAAM,KAAK,QAAQ,WAAW,SAAS,cAAc,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IACvE;AACA,UAAM,YAAY,IAAI;AACtB,SAAK,UAAU;AAEf,UAAM,SAAS,KAAK,OAAO;AAAA,MACzB,IAAI;AAAA,MACJ,KAAK;AAAA,MACL,KAAK,iBAAiB;AAAA,IACxB;AAEA,UAAM,KAAK;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,mBACZ,QACA,gBACA,WACA,oBACe;AACf,qBAAiB,OAAO,QAAQ;AAC9B,UAAI;AACJ,UAAI;AACF,cAAM,OAAO,KAAK,MAAM,IAAI,IAAI;AAChC,YACE,OAAO,SAAS,YAChB,SAAS,QACT,OAAO,KAAK,SAAS,UACrB;AAGA,cAAI,OAAQ,MAA4B,QAAQ,UAAU;AACxD,iBAAK,UAAW,KAAyB;AAAA,UAC3C;AACA;AAAA,QACF;AACA,iBAAS;AACT,YAAI,OAAQ,KAAiC,QAAQ,UAAU;AAC7D,eAAK,UAAW,KAAiC;AAAA,QACnD;AAAA,MACF,QAAQ;AACN;AAAA,MACF;AAEA,YAAM,KAAK;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,kBACZ,MACA,gBACA,WACA,oBACe;AAGf,SAAK,qBAAqB,MAAM,gBAAgB,SAAS;AAEzD,UAAM,QAAQ,mBAAmB,IAAI;AACrC,QAAI,OAAO;AACT,WAAK,KAAK,KAAK;AAAA,IACjB;AAIA,QACE,sBACA,KAAK,SAAS,gBACd,KAAK,WAAW,4BAChB,KAAK,OAAO,SACZ;AACA,YAAM,IAAI,KAAK;AACf,YAAM,UAA2B;AAAA,QAC/B,QAAS,EAAE,WAAsB;AAAA,QACjC,UAAW,EAAE,aAAwB;AAAA,QACrC,WAAY,EAAE,SAAqC,CAAC;AAAA,QACpD,cAAc;AAAA,MAChB;AACA,YAAM,UAAU,MAAM,KAAK,mBAAmB,CAAC,OAAO,CAAC;AACvD,YAAM,KAAK,OAAO,iBAAiB;AAAA,QACjC,iBAAiB;AAAA,QACjB,YAAY;AAAA,QACZ,cAAc;AAAA,MAChB,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,qBACN,MACA,gBACA,WACM;AACN,YAAQ,KAAK,MAAM;AAAA,MACjB,KAAK;AAGH,aAAK,oBAAoB;AACzB,YAAI,KAAK,OAAO;AACd,eAAK,mBAAmB,KAAK;AAAA,QAC/B;AACA;AAAA,MAEF,KAAK;AAGH,YACE,KAAK,SAAS,WACb,CAAC,KAAK,eAAe,KAAK,YAAY,WAAW,IAClD;AACA,eAAK,kBAAkB,KAAK;AAAA,QAC9B;AACA;AAAA,MAEF,KAAK;AACH,YACE,KAAK,MAAM,YAAY,UACvB,KAAK,oBAAoB,QACzB,WAAW,KAAK,iBAAiB,KAAK,IAAI,GAC1C;AACA,eAAK,mBAAmB,KAAK,MAAM;AAAA,QACrC;AACA;AAAA,MAEF,KAAK;AACH,YACE,KAAK,oBAAoB,QACzB,WAAW,KAAK,iBAAiB,KAAK,IAAI,GAC1C;AACA,eAAK,kBAAkB;AAAA,QACzB;AACA;AAAA,MAEF,KAAK;AAGH,YAAI,WAAW;AACb,gBAAM,mBAA4B;AAAA,YAChC,IAAI;AAAA,YACJ;AAAA,YACA,MAAM;AAAA,YACN,SAAS,KAAK;AAAA,YACd,QAAQ;AAAA,YACR,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,UACpC;AACA,eAAK,SAAS,KAAK,gBAAgB;AACnC,eAAK,QACF,WAAW,kBAAkB,cAAc,EAC3C,MAAM,MAAM;AAAA,UAAC,CAAC;AAAA,QACnB;AACA,aAAK,cAAc;AACnB,aAAK,eAAe;AACpB;AAAA,MAEF,KAAK;AACH,YAAI,KAAK,SAAS,mBAAmB;AACnC,gBAAM,QAAS,KAAK,KAAK,SAAoB;AAC7C,cAAI,KAAK,kBAAkB,OAAO;AAChC,kBAAM,OAAO,KAAK,cAAc;AAAA,cAC9B,CAAC,MAAM,EAAE,OAAO,KAAK;AAAA,YACvB;AACA,gBAAI,MAAM;AACR,mBAAK,QAAQ;AAAA,YACf;AACA,iBAAK,QACF,wBAAwB,KAAK,gBAAgB,KAAK,EAClD,MAAM,MAAM;AAAA,YAAC,CAAC;AAAA,UACnB;AAAA,QACF;AACA;AAAA,MAEF;AACE;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,MAAc,mBACZ,WACuB;AACvB,UAAM,UAAwB,CAAC;AAC/B,eAAW,QAAQ,WAAW;AAC5B,YAAM,SAAS,MAAM,KAAK,aAAa,YAAY,IAAI;AACvD,cAAQ,KAAK,MAAM;AAAA,IACrB;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,iBAAiB,IAA2B;AAChD,SAAK,iBAAiB;AACtB,SAAK,oBAAoB;AACzB,SAAK,WAAW,MAAM,KAAK,OACxB,YAAY,EAAE,EACd,MAAM,MAAM,KAAK,QAAQ,cAAc,EAAE,CAAC;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,eAAe,OAA8B;AACjD,QAAI,KAAK,YAAa;AAEtB,SAAK,cAAc;AACnB,SAAK,eAAe;AACpB,SAAK,UAAU;AACf,SAAK,oBAAoB;AACzB,SAAK,kBAAkB,IAAI,gBAAgB;AAE3C,QAAI;AACF,YAAM,SAAS,KAAK,OAAO;AAAA,QACzB;AAAA,QACA,KAAK;AAAA,QACL,KAAK,iBAAiB;AAAA,MACxB;AACA,YAAM,KAAK;AAAA,QACT;AAAA,QACA,KAAK,kBAAkB;AAAA,QACvB;AAAA,QACA;AAAA;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QACxD,WAAW;AAAA,MACb,CAAC;AAAA,IACH,UAAE;AACA,WAAK,cAAc;AACnB,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AAAA;AAAA,EAGA,SAAe;AACb,SAAK,iBAAiB,MAAM;AAC5B,SAAK,kBAAkB;AACvB,SAAK,cAAc;AACnB,SAAK,oBAAoB;AACzB,SAAK,KAAK,EAAE,MAAM,eAAe,CAAC;AAAA,EACpC;AAAA;AAAA,EAGA,aAAmB;AACjB,QAAI,KAAK,cAAc;AACrB,WAAK,OAAO,UAAU,KAAK,YAAY,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IACzD;AACA,SAAK,OAAO;AACZ,SAAK,eAAe;AAEpB,SAAK,UAAU,MAAM;AAAA,EACvB;AAAA,EAEA,MAAM,wBAAyC;AAC7C,UAAM,KAAK,WAAW;AACtB,UAAM,eAAe,MAAM,KAAK,QAAQ;AAAA,MACtC;AAAA,MACA;AAAA,IACF;AACA,SAAK,cAAc,QAAQ,YAAY;AACvC,SAAK,iBAAiB;AACtB,SAAK,WAAW,CAAC;AACjB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,mBACJ,IACA,OAQA,oBACe;AACf,SAAK,iBAAiB;AACtB,SAAK,oBAAoB;AAEzB,UAAM,CAAC,gBAAgB,YAAY,IAAI,MAAM,QAAQ,WAAW;AAAA,MAC9D,KAAK,OAAO,YAAY,EAAE,EAAE,MAAM,MAAM,KAAK,QAAQ,cAAc,EAAE,CAAC;AAAA,MACtE,KAAK,OAAO,sBAAsB,IAAI,KAAK;AAAA,IAC7C,CAAC;AAED,SAAK,WACH,eAAe,WAAW,cAAc,eAAe,QAAQ,CAAC;AAMlE,QAAI,aAAa,WAAW,aAAa;AACvC,UAAI,qBAAqB,CAAC;AAC1B,iBAAW,MAAM,aAAa,OAAO;AACnC,cAAM,OAAQ,GAAG,KAAK,QAAmB,GAAG;AAC5C,YAAI,CAAC,QAAQ,SAAS,OAAQ;AAK9B,YAAI,CAAC,sBAAsB,SAAS,iBAAiB;AACnD,eAAK,KAAK;AAAA,YACR,MAAM;AAAA,YACN,SAAS;AAAA,UACX,CAAC;AACD,+BAAqB;AAAA,QACvB;AAEA,cAAM,OAAO,EAAE,GAAG,GAAG,MAAM,KAAK;AAChC,YAAI;AACF,gBAAM,KAAK;AAAA,YACT;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA;AAAA,UACF;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,mBAAmB,IAA2B;AAClD,QAAI;AACF,YAAM,KAAK,OAAO,mBAAmB,EAAE;AAAA,IACzC,QAAQ;AAAA,IAER;AACA,UAAM,KAAK,QAAQ,mBAAmB,EAAE;AACxC,SAAK,gBAAgB,KAAK,cAAc,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AACjE,QAAI,KAAK,mBAAmB,IAAI;AAC9B,WAAK,iBAAiB;AACtB,WAAK,WAAW,CAAC;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,iBAAiB,MAAuB;AACtC,QAAI,KAAK,mBAAmB,IAAI,IAAI,GAAG;AACrC,WAAK,mBAAmB,OAAO,IAAI;AACnC,aAAO;AAAA,IACT;AACA,SAAK,mBAAmB,IAAI,IAAI;AAChC,WAAO;AAAA,EACT;AACF;;;AClgBO,IAAM,gBAAgB;AAAA;AAAA,EAE3B,WAAW;AAAA,EACX,cAAc;AAAA;AAAA,EAGd,cAAc;AAAA,EACd,aAAa;AAAA;AAAA,EAGb,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,WAAW;AAAA;AAAA,EAGX,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,WAAW;AAAA;AAAA,EAGX,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,eAAe;AAAA,EACf,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,cAAc;AAAA,EACd,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,uBAAuB;AAAA,EACvB,qBAAqB;AAAA,EACrB,sBAAsB;AAAA,EACtB,oBAAoB;AAAA,EACpB,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,cAAc;AAAA;AAAA,EAGd,QAAQ;AACV;;;ACjFO,IAAM,gBAAN,MAAoB;AAAA,EAQzB,YAAY,SAAsB;AANlC,SAAQ,SAAsB;AAC9B,SAAQ,wBAAuC;AAC/C,SAAQ,kBAAkB,oBAAI,IAAoB;AAClD,SAAQ,WAA2B,CAAC;AACpC,SAAQ,QAA6B;AAGnC,SAAK,UAAU;AACf,SAAK,OAAO;AAAA,EACd;AAAA;AAAA,EAIA,IAAI,QAAqB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,uBAAsC;AACxC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,iBAA8C;AAChD,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAIA,GAAG,SAAmC;AACpC,SAAK,SAAS,KAAK,OAAO;AAC1B,WAAO,MAAM;AACX,WAAK,WAAW,KAAK,SAAS,OAAO,CAAC,MAAM,MAAM,OAAO;AAAA,IAC3D;AAAA,EACF;AAAA,EAEQ,KAAK,OAAiC;AAC5C,eAAW,WAAW,KAAK,UAAU;AACnC,UAAI;AACF,gBAAQ,KAAK;AAAA,MACf,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,SAAS,OAA0B;AACzC,SAAK,SAAS;AACd,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN;AAAA,MACA,gBAAgB,KAAK;AAAA,IACvB,CAAC;AAAA,EACH;AAAA;AAAA,EAIQ,SAAe;AACrB,SAAK,QAAQ,KAAK,QAAQ,GAAG,CAAC,UAAqB;AACjD,WAAK,eAAe,KAAK;AAAA,IAC3B,CAAC;AAAA,EACH;AAAA,EAEQ,eAAe,OAAwB;AAC7C,UAAM,SAAS,KAAK,QAAQ;AAG5B,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,gBAAgB;AAAA,MAChB;AAAA,IACF,CAAC;AAGD,QAAI,MAAM,SAAS,cAAc,aAAa;AAC5C,UAAI,KAAK,WAAW,aAAa;AAC/B,aAAK,SAAS,MAAM;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,KAAK,SAAiB,SAAsC;AAChE,QAAI,KAAK,WAAW,YAAa;AAGjC,QAAI,CAAC,KAAK,uBAAuB;AAC/B,YAAM,KAAK,MAAM,KAAK,QAAQ,sBAAsB;AACpD,WAAK,sBAAsB,EAAE;AAAA,IAC/B;AAEA,SAAK,SAAS,WAAW;AAEzB,QAAI;AACF,YAAM,KAAK,QAAQ,KAAK,SAAS;AAAA,QAC/B,cAAc,SAAS;AAAA,QACvB,WAAW,SAAS;AAAA,QACpB,WAAW,SAAS;AAAA,QACpB,UAAU,SAAS;AAAA,MACrB,CAAC;AAAA,IACH,QAAQ;AAAA,IAER;AAEA,SAAK,eAAe;AAAA,EACtB;AAAA;AAAA,EAIA,MAAM,aAA4B;AAChC,QAAI,KAAK,WAAW,YAAa;AAEjC,UAAM,WAAW,KAAK,QAAQ,SAAS;AAAA,MACrC,CAAC,MAAwB,EAAE,SAAS;AAAA,IACtC;AACA,UAAM,cAAc,SAAS,SAAS,SAAS,CAAC;AAChD,QAAI,CAAC,YAAa;AAElB,SAAK,SAAS,WAAW;AAEzB,QAAI;AACF,YAAM,KAAK,QAAQ;AAAA,QACjB,YAAY;AAAA,QACZ,YAAY;AAAA,MACd;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,SAAK,eAAe;AAAA,EACtB;AAAA;AAAA,EAIA,MAAM,SAAS,gBAAuC;AACpD,QAAI,mBAAmB,KAAK,sBAAuB;AAGnD,QAAI,KAAK,WAAW,aAAa;AAC/B,YAAM,YAAY,KAAK;AACvB,YAAM,QAAQ,KAAK,QAAQ;AAC3B,UAAI,aAAa,OAAO;AACtB,aAAK,gBAAgB,IAAI,WAAW,KAAK;AACzC,aAAK,KAAK;AAAA,UACR,MAAM;AAAA,UACN,MAAM,KAAK;AAAA,QACb,CAAC;AAAA,MACH;AACA,WAAK,QAAQ,OAAO;AAAA,IACtB;AAGA,QAAI,KAAK,gBAAgB,IAAI,cAAc,GAAG;AAC5C,WAAK,gBAAgB,OAAO,cAAc;AAC1C,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN,MAAM,KAAK;AAAA,MACb,CAAC;AAAA,IACH;AAEA,SAAK,sBAAsB,cAAc;AACzC,UAAM,KAAK,QAAQ,cAAc;AAAA,EACnC;AAAA;AAAA,EAIA,MAAM,qBAAsC;AAC1C,UAAM,KAAK,MAAM,KAAK,QAAQ,sBAAsB;AACpD,SAAK,sBAAsB,EAAE;AAC7B,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,mBAAmB,IAA2B;AAClD,UAAM,KAAK,QAAQ,mBAAmB,EAAE;AACxC,SAAK,gBAAgB,OAAO,EAAE;AAC9B,QAAI,KAAK,0BAA0B,IAAI;AACrC,WAAK,wBAAwB;AAC7B,WAAK,KAAK,EAAE,MAAM,uBAAuB,gBAAgB,KAAK,CAAC;AAAA,IACjE;AAAA,EACF;AAAA;AAAA,EAIA,OAAa;AACX,SAAK,QAAQ,WAAW;AACxB,SAAK,SAAS,MAAM;AAAA,EACtB;AAAA;AAAA,EAIA,UAAgB;AACd,QAAI,KAAK,OAAO;AACd,WAAK,MAAM;AACX,WAAK,QAAQ;AAAA,IACf;AACA,SAAK,WAAW,CAAC;AAAA,EACnB;AAAA;AAAA,EAIQ,iBAAuB;AAC7B,QAAI,KAAK,WAAW,aAAa;AAC/B,WAAK,SAAS,MAAM;AAAA,IACtB;AAAA,EACF;AAAA;AAAA,EAIA,MAAc,QAAQ,gBAAuC;AAC3D,SAAK,SAAS,WAAW;AAGzB,QAAI,cAA6B;AACjC,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,QAAQ,OAAO,aAAa,cAAc;AACjE,oBAAc,IAAI;AAAA,IACpB,QAAQ;AAAA,IAER;AAEA,QAAI,aAAa;AAEf,YAAM,KAAK,QAAQ,iBAAiB,cAAc;AAClD,WAAK,SAAS,WAAW;AACzB,UAAI;AACF,cAAM,KAAK,QAAQ,eAAe,WAAW;AAAA,MAC/C,QAAQ;AAAA,MAER;AACA,UAAI,KAAK,WAAW,aAAa;AAC/B,aAAK,SAAS,MAAM;AAAA,MACtB;AAAA,IACF,OAAO;AAEL,YAAM,KAAK,QAAQ,iBAAiB,cAAc;AAElD,UAAI;AACF,cAAM,OAAO,MAAM,KAAK,QAAQ,OAAO,IAMrC,qBAAqB,mBAAmB,cAAc,CAAC,OAAO;AAChE,cAAM,gBAAgB,KAAK;AAAA,UACzB,CAAC,MAA0B,EAAE,WAAW;AAAA,QAC1C;AAQA,cAAM,eAAe,KAAK,QAAQ,SAAS;AAAA,UACzC,CAAC,MAAM,EAAE,SAAS;AAAA,QACpB;AAEA,iBAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AAC7C,gBAAM,MAAM,cAAc,CAAC;AAC3B,gBAAM,cAAc,aAAa,CAAC,GAAG;AACrC,gBAAM,KAAK,QAAQ;AAAA,YACjB;AAAA,YACA,IAAI;AAAA,YACJ;AAAA,UACF;AAAA,QACF;AAEA,YAAI,cAAc,SAAS,GAAG;AAC5B,eAAK,KAAK;AAAA,YACR,MAAM;AAAA,YACN;AAAA,YACA,OAAO,cAAc;AAAA,UACvB,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AAAA,MAER;AAEA,WAAK,SAAS,MAAM;AAAA,IACtB;AAAA,EACF;AAAA;AAAA,EAIQ,sBAAsB,IAAkB;AAC9C,SAAK,wBAAwB;AAC7B,SAAK,KAAK,EAAE,MAAM,uBAAuB,gBAAgB,GAAG,CAAC;AAAA,EAC/D;AACF;;;ACtTA,SAAS,YAAY,KAAoC;AACvD,QAAM,OAAQ,IAAI,KAAK,QAAmB,IAAI;AAC9C,MAAI,CAAC,QAAQ,SAAS,OAAQ,QAAO;AACrC,SAAO,EAAE,GAAG,IAAI,MAAM,KAAK;AAC7B;AAOO,SAAS,aAAa,KAA+B;AAC1D,QAAM,OAAO,YAAY,GAAG;AAC5B,MAAI,CAAC,KAAM,QAAO,CAAC;AACnB,QAAM,QAAQ,mBAAmB,IAAI;AACrC,SAAO,QAAQ,CAAC,KAAK,IAAI,CAAC;AAC5B;AAOO,SAAS,aACd,WACA,cACA,aACA,UACM;AACN,QAAM,WAAW,aAAa,OAAO,CAAC,MAAM,EAAE,SAAS,MAAM;AAC7D,MAAI,UAAU;AACd,MAAI,uBAAuB;AAE3B,aAAW,OAAO,WAAW;AAC3B,UAAM,OAAQ,IAAI,KAAK,QAAmB,IAAI;AAE9C,QAAI,SAAS,gBAAgB;AAC3B,6BAAuB;AAAA,IACzB;AAEA,QACE,SAAS,mBACT,wBACA,UAAU,SAAS,QACnB;AACA,YAAM,UAAU,SAAS,OAAO;AAChC,eAAS;AAAA,QACP,MAAM;AAAA,QACN,IAAI,eAAe,OAAO;AAAA,QAC1B,SAAS,QAAQ;AAAA,MACnB,CAAC;AACD;AACA,6BAAuB;AAAA,IACzB;AAEA,eAAW,MAAM,aAAa,GAAG,GAAG;AAClC,kBAAY,EAAE;AAAA,IAChB;AAAA,EACF;AACF;;;ACtDO,SAAS,mBAAmB,OAKjC;AACA,SACE,OAAO,UAAU,YACjB,UAAU,QACT,MAA2C,uBAAuB;AAEvE;AAUO,SAAS,sBAAsB,OAAyC;AAC7E,MAAI,YAAqB;AACzB,MAAI,OAAO,cAAc,UAAU;AAGjC,UAAM,UAAU,UAAU,KAAK;AAC/B,QAAI,CAAC,QAAQ,WAAW,GAAG,EAAG,QAAO;AACrC,QAAI;AACF,kBAAY,KAAK,MAAM,OAAO;AAAA,IAChC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,CAAC,mBAAmB,SAAS,EAAG,QAAO;AAC3C,QAAM,WAAW,UAAU;AAC3B,QAAM,MAAM,UAAU;AACtB,QAAM,UAAU,UAAU;AAC1B,MAAI,OAAO,aAAa,YAAY,CAAC,SAAU,QAAO;AACtD,MAAI,OAAO,QAAQ,YAAY,CAAC,IAAK,QAAO;AAC5C,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}