@ateam-ai-mobile/core 0.1.6 → 0.1.8

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/index.ts","../src/config.ts","../src/api/client.ts","../src/api/mcp.ts","../src/session.ts","../src/chat/store.ts","../src/utils/emitter.ts","../src/api/tenants.ts","../src/theme/tokens.ts","../src/acs/providers.ts","../src/acs/api.ts","../src/acs/widgetsStore.ts","../src/acs/homeActionsStore.ts","../src/acs/ws.ts"],"sourcesContent":["/**\n * @ateam-ai-mobile/core — headless ADAS SDK for React Native.\n *\n * The host app owns authentication and navigation; this package provides\n * everything between the ADAS backend and the UI: config/auth bridge, API\n * client, chat store with live job streaming, MCP tool calls, and theme\n * tokens. No Expo dependencies — works in Expo and bare RN apps alike.\n *\n * Quick start:\n * initAdas({\n * coreUrl: 'https://app.ateam-ai.com',\n * tokenProvider: { getToken, refresh, onAuthFailure },\n * getUser: () => ({ actorId: currentUser.id }),\n * });\n * const { messages, sendMessage } = useChatStore();\n */\n\n// Config & auth bridge\nexport {\n initAdas,\n isAdasInitialized,\n getConfig,\n getCoreApiUrl,\n getCoreWsUrl,\n getVoiceApiUrl,\n getConsumerAuthApiUrl,\n getTenant,\n getUser,\n getToken,\n refreshToken,\n} from './config';\nexport type { AdasConfig, TokenProvider, RefreshOutcome, AdasUser, AdasSession } from './config';\n\n// API client\nexport { apiFetch, apiFetchRaw } from './api/client';\n\n// MCP / connector calls\nexport {\n connectorCall,\n mcpFetch,\n mcpToolCall,\n postCommandResult,\n unwrapMcpResult,\n} from './api/mcp';\n\n// Tenant memberships\nexport { getSession, isDevHost } from './session';\nexport type { Session, SessionEnv } from './session';\nexport { listMyTenants, switchTenant } from './api/tenants';\nexport type { TenantMembership } from './api/tenants';\n\n// Chat\nexport {\n useChatStore,\n closeChatStream,\n blocksToText,\n TABLE_BLOCK_OPEN,\n TABLE_BLOCK_CLOSE,\n} from './chat/store';\nexport type {\n Message,\n MessageAttachment,\n JobProgress,\n UIAction,\n ToolCall,\n} from './chat/types';\n\n// Storage adapter contracts\nexport type {\n AttachmentCache,\n AttachmentPreview,\n OutgoingAttachment,\n} from './storage/types';\n\n// Plugin command bus\nexport { pluginCommandEmitter, PluginCommandEmitter } from './utils/emitter';\nexport type { BufferedCommand } from './utils/emitter';\n\n// Theme tokens\nexport { colors, ada, spacing, fontSize, borderRadius } from './theme/tokens';\n\n// ACS (home widgets + top-actions feed)\nexport {\n listSurface,\n listMadeForYou,\n deleteArtifact,\n patchArtifact,\n setOrder,\n recordEvent,\n listHomeActions,\n patchHomeAction,\n setHomeActionOrder,\n} from './acs/api';\nexport type {\n AcsArtifact,\n AcsSurface,\n AcsEvent,\n AcsDeleteReason,\n AcsPatchReason,\n DeleteResult,\n PatchResult,\n HomeAction,\n HomeActionLeaf,\n HomeActionReason,\n} from './acs/api';\nexport { useWidgetsStore } from './acs/widgetsStore';\nexport { useHomeActionsStore } from './acs/homeActionsStore';\nexport { connectAcsWs, disconnectAcsWs, notifyAcsWsForeground } from './acs/ws';\nexport {\n setAcsReachabilityReporter,\n setAcsCacheStorage,\n} from './acs/providers';\nexport type { AcsReachabilityReporter, AcsKvStorage } from './acs/providers';\n","/**\n * SDK configuration — the single injection point between the host app and\n * the ADAS core. The host owns authentication, identity, and URL discovery;\n * the SDK reads everything through the config registered via initAdas().\n *\n * There is intentionally ONE active config per JS context (module singleton):\n * the chat store, WebSocket, and API client below it are app-wide singletons\n * too, and a second tenant/backend inside one app is not a supported v1 case.\n */\n\nimport type { AttachmentCache } from './storage/types';\n\n/** Result of a host-driven token refresh. Mirrors the semantics the API\n * client depends on: only 'auth_failed' logs the user out; 'transient'\n * (network blip, 5xx) keeps the session so the next call can retry. */\nexport interface RefreshOutcome {\n ok: 'ok' | 'auth_failed' | 'transient';\n status: number;\n reason?: string;\n}\n\n/**\n * Host-implemented auth bridge. The SDK never shows login UI and never\n * stores credentials — the host authenticates its own users and hands the\n * SDK a way to read/refresh the ADAS JWT it minted for them (via the\n * server-to-server token exchange).\n */\nexport interface TokenProvider {\n /** Current ADAS JWT, or null when signed out. Called on every request —\n * keep it synchronous and cheap (read from memory, not disk). */\n getToken(): string | null;\n /** Called on 401. Host should mint/refresh the ADAS token and return the\n * outcome; 'ok' makes the SDK retry the failed request once. */\n refresh(): Promise<RefreshOutcome>;\n /** Auth is genuinely dead (refresh rejected, or retry still 401). Host\n * should treat this as sign-out for the embedded experience. */\n onAuthFailure?: () => void;\n}\n\nexport interface AdasUser {\n actorId: string;\n name?: string;\n email?: string;\n /** Roles from the host's identity (e.g. JWT claims) — forwarded to plugins\n * as PluginContext.user.roles. */\n roles?: string[];\n}\n\n/** A URL that is either static or resolved on each read. Hosts with a\n * runtime env switcher (dev/prod) pass a getter so the SDK always sees the\n * currently-selected backend without re-calling initAdas(). */\nexport type UrlSource = string | (() => string);\n\nexport interface AdasConfig {\n /** Core API base URL, e.g. https://app.ateam-ai.com. Pass a getter to\n * support a runtime env switch without re-initializing. */\n coreUrl: UrlSource;\n /** Voice service base URL (optional — only voice features need it). */\n voiceUrl?: UrlSource;\n /** Consumer-auth base URL, e.g. https://consumer-auth.ateam-ai.com. Only\n * needed for `switchTenant()` (re-scoping the session to another tenant\n * the user belongs to). */\n consumerAuthUrl?: UrlSource;\n tokenProvider: TokenProvider;\n /** Tenant slug — used for plugin URL resolution and voice, never sent as a\n * header (the backend derives the tenant from the JWT). Pass a getter when\n * the host resolves the tenant after startup (storage hydration, runtime\n * tenant switch). */\n tenant?: string | (() => string);\n /** Current end user as known to the host. actorId is used as the chat\n * reply context; without it, replies route to 'unknown'. */\n getUser?: () => AdasUser | null;\n /** Optional persistence for outgoing image/file attachments so previews\n * survive app restarts. No-op when omitted (previews live in memory for\n * the session only). @ateam-ai-mobile/ui ships an Expo/RN implementation. */\n attachmentCache?: AttachmentCache;\n /** Called by `switchTenant()` with the freshly-minted session for the\n * target tenant. The host MUST persist it here (store the new JWT so\n * tokenProvider.getToken() returns it, and update the active tenant) —\n * the SDK never stores credentials. Awaited before the chat reconnects. */\n onSession?: (session: AdasSession) => void | Promise<void>;\n}\n\n/** A tenant-scoped session minted by the platform (e.g. via switchTenant). */\nexport interface AdasSession {\n jwt: string;\n tenant: { id: string; name: string };\n actor: { id: string; email: string | null; roles: string[] };\n}\n\nlet _config: AdasConfig | null = null;\n\n/** Register the SDK configuration. Call once at app start, before any other\n * SDK API. Calling again replaces the config (e.g. env switch) — combine\n * with chat.clearChat() and closeChatStream() for a clean swap. */\nexport function initAdas(config: AdasConfig): void {\n _config = config;\n}\n\nexport function isAdasInitialized(): boolean {\n return _config !== null;\n}\n\nexport function getConfig(): AdasConfig {\n if (!_config) {\n throw new Error('[adas] SDK not initialized — call initAdas(config) before using any ADAS API');\n }\n return _config;\n}\n\nfunction resolveUrl(src: UrlSource): string {\n return (typeof src === 'function' ? src() : src).replace(/\\/$/, '');\n}\n\nexport function getCoreApiUrl(): string {\n return resolveUrl(getConfig().coreUrl);\n}\n\nexport function getCoreWsUrl(): string {\n return getCoreApiUrl().replace(/^http/, 'ws');\n}\n\nexport function getVoiceApiUrl(): string {\n const url = getConfig().voiceUrl;\n if (!url) throw new Error('[adas] voiceUrl not configured');\n return resolveUrl(url);\n}\n\nexport function getConsumerAuthApiUrl(): string {\n const url = getConfig().consumerAuthUrl;\n if (!url) throw new Error('[adas] consumerAuthUrl not configured — required for switchTenant()');\n return resolveUrl(url);\n}\n\nexport function getTenant(): string {\n const t = getConfig().tenant;\n return (typeof t === 'function' ? t() : t) || '';\n}\n\nexport function getUser(): AdasUser | null {\n return getConfig().getUser?.() || null;\n}\n\n// ─── Internal token helpers (thin wrappers over the host's TokenProvider) ──\n\nexport function getToken(): string | null {\n return getConfig().tokenProvider.getToken();\n}\n\nexport async function refreshToken(): Promise<RefreshOutcome> {\n return getConfig().tokenProvider.refresh();\n}\n\nexport function notifyAuthFailure(): void {\n getConfig().tokenProvider.onAuthFailure?.();\n}\n","import { getCoreApiUrl, getVoiceApiUrl, getToken, refreshToken, notifyAuthFailure } from '../config';\n\ntype FetchOptions = RequestInit & {\n baseUrl?: 'core' | 'voice';\n};\n\n/**\n * Base API client with JWT injection and 401 refresh handling.\n *\n * Auth failure semantics (preserved from the reference app): a transient\n * 5xx or network blip on the RETRY after a successful refresh must NOT be\n * treated as sign-out. notifyAuthFailure() fires only when auth is genuinely\n * broken:\n * - Refresh itself was rejected with 401/403 → auth failure\n * - Retry with the fresh JWT still returns 401/403 → auth failure\n * Anything else throws but keeps the session intact — the caller may retry\n * on the next user action.\n */\nexport async function apiFetch<T = unknown>(\n path: string,\n options: FetchOptions = {},\n): Promise<T> {\n const { baseUrl = 'core', ...fetchOptions } = options;\n const base = baseUrl === 'voice' ? getVoiceApiUrl() : getCoreApiUrl();\n const url = `${base}${path}`;\n\n const token = getToken();\n\n const headers: Record<string, string> = {\n ...(fetchOptions.headers as Record<string, string>),\n };\n\n if (token && !headers['Authorization']) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n // Note: we intentionally DO NOT send a tenant header. The backend derives\n // the tenant from the JWT — a client-supplied tenant paired with a leaked\n // JWT is a tenant-pivot footgun. Tenant comes from the token, not the client.\n\n if (!headers['Content-Type'] && fetchOptions.body && typeof fetchOptions.body === 'string') {\n headers['Content-Type'] = 'application/json';\n }\n\n const res = await fetch(url, { ...fetchOptions, headers });\n\n if (res.status === 401 && token) {\n console.warn(`[adas] 401 on ${path} — attempting refresh`);\n const outcome = await refreshToken();\n if (outcome.ok === 'ok') {\n const newToken = getToken();\n headers['Authorization'] = `Bearer ${newToken}`;\n const retryRes = await fetch(url, { ...fetchOptions, headers });\n if (retryRes.ok) {\n return retryRes.json() as Promise<T>;\n }\n if (retryRes.status === 401 || retryRes.status === 403) {\n // Still auth-broken with a fresh JWT — genuinely dead session.\n console.warn(`[adas] retry after refresh still ${retryRes.status} on ${path} — auth failure`);\n notifyAuthFailure();\n throw new Error('Session expired');\n }\n // Non-auth error on retry. Surface it, but the session is fine.\n const text = await retryRes.text().catch(() => '');\n throw new Error(`API ${retryRes.status}: ${text}`);\n }\n if (outcome.ok === 'auth_failed') {\n console.warn(`[adas] refresh auth_failed on ${path} (${outcome.reason}) — auth failure`);\n notifyAuthFailure();\n throw new Error('Session expired');\n }\n // 'transient' — network/5xx on refresh. Keep the session; surface the 401.\n throw new Error(`API 401: refresh transient (${outcome.reason})`);\n }\n\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`API ${res.status}: ${text}`);\n }\n\n return res.json() as Promise<T>;\n}\n\n/**\n * Fetch with raw response (non-JSON responses, streaming bodies).\n */\nexport async function apiFetchRaw(\n path: string,\n options: FetchOptions = {},\n): Promise<Response> {\n const { baseUrl = 'core', ...fetchOptions } = options;\n const base = baseUrl === 'voice' ? getVoiceApiUrl() : getCoreApiUrl();\n const url = `${base}${path}`;\n\n const token = getToken();\n const headers: Record<string, string> = {\n ...(fetchOptions.headers as Record<string, string>),\n };\n\n if (token && !headers['Authorization']) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n return fetch(url, { ...fetchOptions, headers });\n}\n","/**\n * Shared MCP client — single API layer for ALL plugin communication.\n *\n * Two call patterns:\n * 1. connectorCall(connectorId, tool, args) → POST /api/connectors/:id/call\n * 2. mcpFetch(body) → POST /mcp (raw JSON-RPC passthrough)\n */\nimport { apiFetchRaw } from './client';\n\n/**\n * Call a tool on a specific connector via POST /api/connectors/:id/call.\n *\n * NOTE: intentionally bypasses the /mcp proxy (which routes to adas-mcp).\n * adas-mcp only knows skill-generated tools, NOT raw connector tools like\n * triggers.list — the direct connector API is the reliable path.\n */\nexport async function connectorCall(\n connectorId: string,\n tool: string,\n args: Record<string, any> = {},\n): Promise<{ ok: boolean; result: any; error?: string }> {\n try {\n const res = await apiFetchRaw(\n `/api/connectors/${encodeURIComponent(connectorId)}/call`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ tool, args }),\n },\n );\n\n if (!res.ok) {\n const text = await res.text();\n return { ok: false, result: null, error: `HTTP ${res.status}: ${text}` };\n }\n\n const data = await res.json();\n // Normalize: Core may return { ok, result } or raw MCP { content: [...] }\n if (data.ok !== undefined) return data;\n return { ok: true, result: data };\n } catch (err) {\n return { ok: false, result: null, error: err instanceof Error ? err.message : String(err) };\n }\n}\n\n/** Raw JSON-RPC passthrough to /mcp. Returns the full JSON-RPC response. */\nexport async function mcpFetch(body: any): Promise<any> {\n const res = await apiFetchRaw('/mcp', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: typeof body === 'string' ? body : JSON.stringify(body),\n });\n\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`MCP fetch failed: HTTP ${res.status}: ${text}`);\n }\n\n return res.json();\n}\n\n/** JSON-RPC tools/call to /mcp (for core tools like cp.fe_api). */\nexport async function mcpToolCall(\n toolName: string,\n args: Record<string, any> = {},\n): Promise<any> {\n return mcpFetch({\n jsonrpc: '2.0',\n id: Date.now(),\n method: 'tools/call',\n params: { name: toolName, arguments: args },\n });\n}\n\n/** Post a plugin command result back to Core. */\nexport async function postCommandResult(\n correlationId: string,\n result: any,\n error?: string,\n): Promise<void> {\n await apiFetchRaw('/api/plugin-command-result', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ correlationId, result, error }),\n });\n}\n\n/**\n * Unwrap MCP JSON-RPC text content → parsed business data.\n * Server returns: { content: [{ type: \"text\", text: \"{...}\" }] }\n */\nexport function unwrapMcpResult(result: any): any {\n if (!result) return result;\n if (!result.content) return result;\n const textItem = Array.isArray(result.content)\n ? result.content.find((c: any) => c.type === 'text')\n : null;\n if (!textItem?.text) return result;\n try {\n return JSON.parse(textItem.text);\n } catch {\n return textItem.text;\n }\n}\n","// packages/core/src/session.ts\n//\n// THE single answer to \"where am I / what am I talking to / who am I\", derived\n// from the SDK config the REQUESTS themselves use — getTenant / getConsumerAuthApiUrl\n// / getCoreApiUrl / getUser. Because the label reads from the same place the\n// request reads, they cannot disagree. Host-agnostic: any host that calls\n// initAdas() gets a correct session with no parallel copy to drift.\n//\n// Rule: DERIVE, NEVER STORE — see Docs/DERIVE_NEVER_STORE.md. This lives in the\n// SDK (not a host app) so iOS/Android/RN hosts share one derivation; the host's\n// React hook (e.g. useSession) is a thin wrapper that just re-renders on change.\n\nimport { getTenant, getConsumerAuthApiUrl, getCoreApiUrl, getUser } from './config';\n\nexport type SessionEnv = 'dev' | 'prod';\n\nexport interface Session {\n /** Tenant slug the app is actually scoped to (getTenant = signed-in tenant,\n * falling back to the pre-auth choice). Null when neither is set. */\n tenant: string | null;\n /** Human label for the tenant — slug today; callers must not compute their own. */\n tenantName: string;\n /** dev | prod — derived from the resolved host, never a stored word. */\n env: SessionEnv;\n /** The actual backend host — a host can't be ambiguous the way a word can. */\n host: string;\n actorId: string | null;\n email: string | null;\n}\n\nfunction hostOf(u: string | undefined | null): string {\n if (!u) return '';\n try { return new URL(u).host; } catch { return u; }\n}\n\n/** Is a resolved base URL a dev backend? Keyed on the hostname (dev-app…,\n * dev-consumer-auth…) so it matches wherever the request goes. */\nexport function isDevHost(u: string | undefined | null): boolean {\n const h = hostOf(u);\n return !!h && (h.includes('dev-') || /(^|\\.)dev\\./.test(h));\n}\n\n/** Compute the session from the live SDK config. Plain function — a host hook\n * wraps this for reactivity; imperative code can call it directly. */\nexport function getSession(): Session {\n let consumerUrl = '';\n try { consumerUrl = getConsumerAuthApiUrl(); } catch { /* not configured yet */ }\n let coreUrl = '';\n try { coreUrl = getCoreApiUrl(); } catch { /* not configured yet */ }\n\n const tenant = getTenant() || null;\n const user = getUser();\n\n return {\n tenant,\n tenantName: tenant || '—',\n env: isDevHost(consumerUrl) || isDevHost(coreUrl) ? 'dev' : 'prod',\n host: hostOf(consumerUrl || coreUrl),\n actorId: user?.actorId ?? null,\n email: user?.email ?? null,\n };\n}\n","/**\n * Chat store — talks to the ADAS Core API.\n *\n * Flow:\n * 1. loadConversation() → GET /api/conversation → display history\n * 2. sendMessage(text) → POST /api/chat { goal } → returns jobId\n * 3. WebSocket (poll fallback) → real-time progress\n * 4. When done → GET /api/conversation → formatted response\n *\n * Platform notes: no Expo/AsyncStorage imports. Attachment persistence is\n * delegated to the host via config.attachmentCache; identity and tokens come\n * from the host via initAdas().\n */\nimport { create } from 'zustand';\nimport { getCoreApiUrl, getCoreWsUrl, getToken, getTenant, getUser, getConfig } from '../config';\nimport { pluginCommandEmitter } from '../utils/emitter';\nimport type { OutgoingAttachment } from '../storage/types';\nimport type { Message, MessageAttachment, JobProgress, UIAction } from './types';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface ChatState {\n messages: Message[];\n loading: boolean;\n error: string | null;\n activeJobId: string | null;\n conversationLoaded: boolean;\n /** Load conversation history from Core */\n loadConversation: () => Promise<void>;\n /** Send a message via POST /api/chat — creates a job, streams progress */\n sendMessage: (text: string, skillSlug?: string, attachments?: OutgoingAttachment[]) => Promise<void>;\n /** Respond to a paused sys.askUser question */\n respondToQuestion: (jobId: string, inputId: string, answer: string) => Promise<void>;\n /** Update in-flight job progress (called by the stream handler) */\n updateJobProgress: (jobId: string, progress: Partial<JobProgress>) => void;\n /** Reset chat (also re-fetches history on next mount — used on auth switch) */\n clearChat: () => void;\n /** Start a fresh empty chat without re-fetching history */\n newChat: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction coreHeaders(): Record<string, string> {\n const token = getToken();\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (token) headers['Authorization'] = `Bearer ${token}`;\n return headers;\n}\n\nfunction isTerminalStatus(status?: unknown): boolean {\n const s = typeof status === 'string' ? status.toLowerCase() : '';\n return s === 'done' || s === 'error' || s === 'failed' || s === 'completed';\n}\n\nfunction normalizeJobStatus(snap: any): JobProgress['status'] {\n const status = typeof snap?.status === 'string' ? snap.status.toLowerCase() : '';\n if (status === 'done' || status === 'completed') return 'done';\n if (status === 'error' || status === 'failed') return 'error';\n if (status === 'paused' || status === 'waiting') return 'paused';\n if (status === 'unknown') return 'unknown';\n return 'running';\n}\n\nfunction extractPendingQuestion(snap: any): any {\n return snap?.pendingQuestion || snap?.state?.pendingQuestion || snap?.state?.waiting || null;\n}\n\nfunction extractActorId(snap: any): string | undefined {\n const actorId =\n snap?.actorId ||\n snap?.actor?.actorId ||\n snap?.actor?.id ||\n snap?.state?.actorId ||\n snap?.state?.actor?.actorId ||\n snap?.state?.actor?.id;\n return typeof actorId === 'string' && actorId.length > 0 ? actorId : undefined;\n}\n\nfunction extractLiveProgress(snap: any): JobProgress['liveProgress'] | undefined {\n const lp = snap?.liveProgress || snap?.state?.__live_progress;\n const latest = Array.isArray(lp) ? lp[lp.length - 1] : lp;\n if (!latest?.message) return undefined;\n return {\n message: String(latest.message || ''),\n ...(latest.step != null ? { step: latest.step } : {}),\n ...(latest.total != null ? { total: latest.total } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Extract the most-recently-called tool from job exec steps.\n// snap.state.steps.__exec = { iter_0: [{tool, ok, reason}], iter_1: [...] }\n// ---------------------------------------------------------------------------\nfunction extractCurrentTool(state: any): string | undefined {\n const exec = state?.steps?.__exec;\n if (!exec || typeof exec !== 'object') return undefined;\n const latest = Object.keys(exec)\n .map(k => ({ k, n: parseInt(k.replace('iter_', ''), 10) }))\n .filter(({ n }) => !isNaN(n))\n .sort((a, b) => b.n - a.n)[0];\n if (!latest) return undefined;\n const steps = exec[latest.k];\n if (!Array.isArray(steps) || !steps.length) return undefined;\n const last = steps[steps.length - 1];\n return (last?.tool || last?.phase) as string | undefined;\n}\n\nfunction snapshotToProgress(snap: any, rootJobId: string): Partial<JobProgress> {\n const pq = extractPendingQuestion(snap);\n const progress: Partial<JobProgress> = {\n jobId: rootJobId,\n actorId: extractActorId(snap),\n status: pq ? 'paused' : normalizeJobStatus(snap),\n subStatus: snap?.subStatus || snap?.phase || snap?.state?.visual?.phase,\n phase: snap?.phase || snap?.state?.visual?.phase,\n iteration: snap?.iteration || snap?.state?.plannerIter,\n skillSlug: snap?.skillSlug || snap?.activeSkill,\n currentTool: snap?.currentTool || snap?.lastTool || extractCurrentTool(snap?.state),\n };\n\n const liveProgress = extractLiveProgress(snap);\n if (liveProgress) progress.liveProgress = liveProgress;\n\n // Human-language status (additive; absent when disabled/uncached).\n if (typeof snap?.humanStatus === 'string' && snap.humanStatus) progress.humanStatus = snap.humanStatus;\n if (snap?.humanLabels && typeof snap.humanLabels === 'object') progress.humanLabels = snap.humanLabels;\n\n if (pq) {\n progress.pendingQuestion = {\n inputId: pq.correlation_id || pq.correlationId || pq.inputId || '',\n question: pq.prompt_message || pq.question || 'The assistant needs your input',\n inputType: pq.inputType || 'text',\n options: pq.options || pq.requested_fields,\n placeholder: pq.placeholder,\n };\n }\n\n return progress;\n}\n\nfunction isTerminalSnapshot(snap: any): boolean {\n if (extractPendingQuestion(snap)) return false;\n return snap?.done === true || isTerminalStatus(snap?.status);\n}\n\n/**\n * Sentinel delimiters used to embed structured table data inside a flat\n * message string. UI layers split on these and render the contents with a\n * native table component instead of markdown tables — markdown tables look\n * terrible on narrow phone screens.\n *\n * Unicode Private Use Area chars (U+E100/U+E101) so they never collide with\n * anything the model could legitimately emit.\n */\nexport const TABLE_BLOCK_OPEN = '\\uE100';\nexport const TABLE_BLOCK_CLOSE = '\\uE101';\n\n/**\n * Render a `{ columns: [...], rows: [...] }` payload as a sentinel-wrapped\n * JSON marker. Returns null if the shape doesn't match.\n */\nfunction renderTableShape(content: any): string | null {\n if (!content || typeof content !== 'object') return null;\n const cols = Array.isArray(content.columns) ? content.columns : null;\n const rows = Array.isArray(content.rows) ? content.rows : null;\n if (!cols || !rows) return null;\n // encodeURIComponent so the JSON can never contain newlines or sentinel chars.\n const payload = encodeURIComponent(JSON.stringify({ columns: cols, rows }));\n return `${TABLE_BLOCK_OPEN}${payload}${TABLE_BLOCK_CLOSE}`;\n}\n\n/**\n * Extract display text from a conversation turn's assistant blocks.\n * Handles multiple block kinds (text, code, json, markdown, table, html).\n */\nexport function blocksToText(blocks?: Array<{ kind: string; content: any }>): string {\n if (!blocks || blocks.length === 0) return '';\n // Mirror the web renderer: when a human-readable text/markdown block exists in\n // the same turn, drop internal-state json/code blocks (planning state,\n // askUser payloads) — otherwise they render as a raw ```json blob AFTER the\n // real answer. Standalone json/code (no text block, e.g. a table-only turn)\n // still renders as before.\n const hasTextBlock = blocks.some(\n b => b.kind === 'text' || b.kind === 'markdown' || b.kind === 'description',\n );\n return blocks.map(b => {\n if ((b.kind === 'json' || b.kind === 'code') && hasTextBlock) return '';\n if (b.kind === 'text' || b.kind === 'markdown' || b.kind === 'description') {\n return typeof b.content === 'string' ? b.content : '';\n }\n if (b.kind === 'code') {\n const code = typeof b.content === 'object' ? b.content.content : b.content;\n return `\\`\\`\\`\\n${code}\\n\\`\\`\\``;\n }\n if (b.kind === 'table' && b.content && typeof b.content === 'object') {\n const md = renderTableShape(b.content);\n if (md) return md;\n // Fall through to generic JSON rendering if shape is unexpected.\n }\n if (b.kind === 'json') {\n // Skip pendingQuestion state objects — internal sys.askUser data\n if (b.content && typeof b.content === 'object' && 'pendingQuestion' in b.content) return '';\n // Polisher sometimes wraps tables as kind:'json' when the content has\n // columns/rows — render those as table blocks too.\n const md = renderTableShape(b.content);\n if (md) return md;\n return '```json\\n' + JSON.stringify(b.content, null, 2) + '\\n```';\n }\n if (b.kind === 'html') {\n return typeof b.content === 'string' ? b.content : '';\n }\n return typeof b.content === 'string' ? b.content : JSON.stringify(b.content);\n }).filter(Boolean).join('\\n\\n');\n}\n\n/** Extract image/file attachments from conversation blocks */\nfunction blocksToAttachments(blocks?: Array<{ kind: string; hash?: string; url?: string; mimeType?: string; name?: string }>): MessageAttachment[] | undefined {\n if (!blocks || blocks.length === 0) return undefined;\n const atts: MessageAttachment[] = [];\n for (const b of blocks) {\n if ((b.kind === 'image' || b.kind === 'file') && (b.url || b.hash)) {\n atts.push({\n mimeType: b.mimeType || (b.kind === 'image' ? 'image/jpeg' : 'application/octet-stream'),\n name: b.name || 'attachment',\n previewUri: b.url ? `${getCoreApiUrl()}${b.url}` : undefined,\n });\n }\n }\n return atts.length > 0 ? atts : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Store\n// ---------------------------------------------------------------------------\n\nexport const useChatStore = create<ChatState>((set, get) => ({\n messages: [],\n loading: false,\n error: null,\n activeJobId: null,\n conversationLoaded: false,\n\n // -----------------------------------------------------------------------\n // Load conversation history from Core\n // GET /api/conversation → array of turns [{user, assistant, jobId, ...}]\n // -----------------------------------------------------------------------\n loadConversation: async () => {\n try {\n const attachmentCache = getConfig().attachmentCache;\n await attachmentCache?.ready();\n\n const res = await fetch(`${getCoreApiUrl()}/api/conversation?limit=20`, {\n headers: coreHeaders(),\n });\n if (!res.ok) {\n console.warn('[adas-chat] Failed to load conversation:', res.status);\n set({ conversationLoaded: true });\n return;\n }\n\n const data = await res.json();\n if (!data.ok || !data.items) {\n set({ conversationLoaded: true });\n return;\n }\n\n // Persisted turns from /api/conversation are the SOLE source of history.\n // Live job state is mirrored only via WS into the placeholder bubble\n // created by sendMessage; in-flight local state is preserved below.\n const items: any[] = [...data.items].reverse();\n const persistedMessages: Message[] = [];\n const seenJobIds = new Set<string>();\n for (const turn of items) {\n // Deduplicate by jobId (chain jobs can produce duplicate turns)\n if (turn.jobId && seenJobIds.has(turn.jobId)) continue;\n if (turn.jobId) seenJobIds.add(turn.jobId);\n\n // User message\n const userText = blocksToText(turn.user?.blocks);\n let userAtts = blocksToAttachments(turn.user?.blocks);\n // Merge locally cached attachments if API doesn't have them\n if (!userAtts && turn.jobId) {\n userAtts = attachmentCache?.get(turn.jobId);\n }\n const turnChainId = typeof turn.chainId === 'string' ? turn.chainId : undefined;\n if (userText || userAtts) {\n persistedMessages.push({\n id: `user-${turn.jobId}`,\n role: 'user',\n content: userText || '',\n timestamp: new Date(turn.startedAt || Date.now()),\n skillSlug: turn.skillSlug,\n ...(userAtts ? { attachments: userAtts } : {}),\n ...(turnChainId ? { chainId: turnChainId } : {}),\n });\n }\n // Assistant response\n const assistantText = blocksToText(turn.assistant?.blocks);\n const assistantAtts = blocksToAttachments(turn.assistant?.blocks);\n const assistantActions = Array.isArray(turn.assistant?.actions) ? turn.assistant.actions : undefined;\n const turnKind = typeof turn.kind === 'string' ? turn.kind : undefined;\n if (assistantText || assistantAtts || assistantActions?.length) {\n persistedMessages.push({\n id: `bot-${turn.jobId}`,\n role: 'assistant',\n content: assistantText || '',\n timestamp: new Date(turn.finishedAt || turn.startedAt || Date.now()),\n skillSlug: turn.skillSlug,\n ...(assistantAtts ? { attachments: assistantAtts } : {}),\n ...(assistantActions?.length ? { actions: assistantActions } : {}),\n ...(turnChainId ? { chainId: turnChainId } : {}),\n ...(turnKind ? { kind: turnKind } : {}),\n });\n }\n }\n\n // Preserve in-flight LOCAL state across pull-to-refresh. /api/conversation\n // is the truth for HISTORY; anything mid-flight lives only as a local\n // WS-driven placeholder created by sendMessage.\n const current = get().messages;\n const keptPlaceholders: Message[] = [];\n for (const m of current) {\n if (!m.id.startsWith('job-') || !m.jobProgress || !m.chainId) continue;\n const matchIdx = persistedMessages.findIndex(pm =>\n pm.role === 'assistant' && pm.chainId === m.chainId\n );\n if (matchIdx >= 0) {\n // The persisted turn for this chain has arrived. Move the live\n // progress overlay (pill) onto it — but only while still in flight.\n const st = m.jobProgress.status;\n if (st === 'running' || st === 'paused') {\n persistedMessages[matchIdx] = { ...persistedMessages[matchIdx], jobProgress: m.jobProgress };\n }\n // Drop the placeholder either way.\n } else {\n // No persisted turn yet — chain still in flight. Keep it.\n keptPlaceholders.push(m);\n }\n }\n\n // Optimistic user bubbles (id=`user-<ms>` from sendMessage). Keep until\n // a persisted user turn arrives. Dedup by chainId — content match has\n // false positives (\"Yes\", \"Continue\", etc.).\n const isLocalUserId = (id: string) => /^user-\\d+$/.test(id);\n const keptUserBubbles = current.filter(m => {\n if (m.role !== 'user' || !isLocalUserId(m.id)) return false;\n if (!m.chainId) return true; // POST in flight — can't dedup yet\n return !persistedMessages.some(pm =>\n pm.role === 'user' && pm.chainId === m.chainId\n );\n });\n\n const finalMessages = [...persistedMessages, ...keptUserBubbles, ...keptPlaceholders];\n finalMessages.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());\n\n let firstRunning: Message | undefined = keptPlaceholders.find(m => m.jobProgress?.status === 'running');\n\n // Cold-start / post-refresh reattach: WS subscriptions die on app\n // restart. Fetch ALL active chains for this actor (chain-native SlimChain\n // API) and reattach EACH — multiple concurrent chains must all come back\n // in progress, not just the most-recently-updated one. Falls back to the\n // legacy single-chain /api/chain/current on older backends.\n try {\n let activeChains: any[] = [];\n const chainsRes = await fetch(`${getCoreApiUrl()}/api/chains/active`, {\n headers: coreHeaders(),\n });\n if (chainsRes.ok) {\n const body = await chainsRes.json();\n activeChains = Array.isArray(body?.chains) ? body.chains : [];\n } else {\n // Legacy backend without the plural endpoint — single most-recent chain.\n const legacy = await fetch(`${getCoreApiUrl()}/api/chain/current`, {\n headers: coreHeaders(),\n });\n if (legacy.ok) {\n const lb = await legacy.json();\n if (lb?.data?.id) activeChains = [lb.data];\n }\n }\n\n for (const slim of activeChains) {\n if (!slim?.id) continue;\n const chainId = String(slim.id);\n const alreadyTracked = finalMessages.some(m =>\n m.role === 'assistant' && m.jobProgress?.jobId === chainId\n );\n if (alreadyTracked) continue;\n\n // Chain-native status: prefer the whole-chain aggregate (chainStatus)\n // over the active job's per-job status, so e.g. a chain still running\n // across a handoff isn't shown as \"done\". A pending question still\n // overrides to \"paused\" inside snapshotToProgress.\n const progress = snapshotToProgress(\n { ...slim, status: slim.chainStatus || slim.status },\n chainId,\n );\n const chainCreatedAt = typeof slim.createdAt === 'number'\n ? slim.createdAt\n : (typeof slim.lastUpdate === 'number' ? slim.lastUpdate : Date.now());\n const chainLastUpdate = typeof slim.lastUpdate === 'number' ? slim.lastUpdate : chainCreatedAt;\n const jobProgress = {\n jobId: chainId,\n actorId: extractActorId(slim),\n status: 'running',\n chainStartedAt: chainCreatedAt,\n ...progress,\n } as JobProgress;\n\n // Synthesize the user bubble if a hard refresh wiped the optimistic\n // one and the chain hasn't persisted its turn yet.\n const goal = typeof slim.goal === 'string' ? slim.goal : '';\n const userExists = finalMessages.some(m =>\n m.role === 'user' && (m.chainId === chainId || (goal && m.content === goal))\n );\n if (goal && !userExists) {\n finalMessages.push({\n id: `user-${chainId}`,\n role: 'user',\n content: goal,\n timestamp: new Date(chainCreatedAt),\n chainId,\n });\n }\n\n // Assistant side: attach to matching persisted turn if one exists;\n // otherwise push a placeholder bubble.\n let matchIdx = -1;\n for (let i = finalMessages.length - 1; i >= 0; i--) {\n const m = finalMessages[i];\n if (m.role === 'assistant' && m.chainId === chainId) {\n matchIdx = i;\n break;\n }\n }\n if (matchIdx >= 0) {\n finalMessages[matchIdx] = { ...finalMessages[matchIdx], jobProgress };\n if (jobProgress.status === 'running' && !firstRunning) firstRunning = finalMessages[matchIdx];\n } else {\n const reattachBubble: Message = {\n id: `job-${chainId}`,\n role: 'assistant',\n content: 'Working on it...',\n timestamp: new Date(chainLastUpdate),\n jobProgress,\n chainId,\n };\n finalMessages.push(reattachBubble);\n if (jobProgress.status === 'running' && !firstRunning) firstRunning = reattachBubble;\n }\n\n // Re-open the live subscription for this chain.\n streamJob(chainId, getTenant(), getToken());\n }\n\n finalMessages.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());\n } catch {}\n\n set({\n messages: finalMessages,\n conversationLoaded: true,\n error: null,\n activeJobId: firstRunning?.jobProgress?.jobId || null,\n loading: !!firstRunning,\n });\n } catch (err: any) {\n console.warn('[adas-chat] Error loading conversation:', err.message);\n set({ conversationLoaded: true });\n }\n },\n\n // -----------------------------------------------------------------------\n // Send message via Core API\n // POST /api/chat { goal } → { jobId } → stream → fetch turn result\n // -----------------------------------------------------------------------\n sendMessage: async (text: string, skillSlug?: string, attachments?: OutgoingAttachment[]) => {\n // No `if (loading) return` guard: Chain Additions needs the user to send\n // WHILE a chain is running. The backend router decides attach/cancel/new.\n\n const userMsg: Message = {\n id: `user-${Date.now()}`,\n role: 'user',\n content: text,\n timestamp: new Date(),\n ...(attachments?.length ? {\n attachments: attachments.map(a => ({\n mimeType: a.mimeType,\n name: a.name,\n previewUri: a.mimeType.startsWith('image/') ? `data:${a.mimeType};base64,${a.data}` : undefined,\n }))\n } : {}),\n };\n\n set(s => ({ messages: [...s.messages, userMsg], loading: true, error: null }));\n\n try {\n // Backend auto-resolves the skill via conversation continuity → channel\n // routing → default. triggerContext.channel + replyContext.chatId are\n // required by handoff-controller-mcp's schema — without them handoffs\n // fail silently.\n const actor = getUser();\n const body: Record<string, unknown> = {\n goal: text,\n // Opt in to the Chain Additions router (attach/cancel/new while a\n // chain runs). Backend ignores it where not applicable — safe to send.\n chainRouting: true,\n triggerContext: {\n channel: 'mobile',\n replyContext: { chatId: actor?.actorId || 'unknown' },\n },\n };\n if (skillSlug) body.skillSlug = skillSlug;\n if (attachments?.length) body.attachments = attachments;\n\n const res = await fetch(`${getCoreApiUrl()}/api/chat`, {\n method: 'POST',\n headers: coreHeaders(),\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n let errMsg = `Server error (${res.status})`;\n try {\n const text = await res.text();\n // Don't show raw HTML — extract a short message\n if (text.startsWith('<') || text.startsWith('<!')) {\n errMsg = res.status === 502 ? 'Server is temporarily unavailable. Please try again.' : `Server error (${res.status})`;\n } else {\n const parsed = JSON.parse(text);\n errMsg = parsed.error || errMsg;\n }\n } catch {}\n throw new Error(errMsg);\n }\n\n const data = await res.json();\n\n if (!data.ok) {\n throw new Error(data.error || 'Failed to send message');\n }\n\n // Chain Additions: the router may ATTACH this message to the running\n // chain or CANCEL it. These replies carry NO jobId — handle them and\n // don't start a second stream.\n if (data.mode === 'chain_addition' || data.mode === 'chain_cancel') {\n const ack = data.mode === 'chain_addition'\n ? (data.ack || 'Got it — added to the running task.')\n : 'Stopping the task…';\n set(s => ({\n messages: s.messages\n .map(m => (m.id === userMsg.id && data.chainId) ? { ...m, chainId: data.chainId } : m)\n .concat({\n id: `ack-${Date.now()}`,\n role: 'assistant',\n content: ack,\n timestamp: new Date(),\n ...(data.chainId ? { chainId: data.chainId } : {}),\n }),\n }));\n return;\n }\n\n const jobId = data.id || data.jobId;\n if (!jobId) {\n throw new Error('No job ID returned');\n }\n\n // Cache attachments so they survive loadConversation reload. Awaited\n // intentionally — fire-and-forget can lose the write on quick reload.\n if (attachments?.length) {\n await getConfig().attachmentCache?.save(jobId, attachments);\n }\n\n // Placeholder assistant bubble = live mirror of the active chain.\n // Carries chainId so a subsequent loadConversation refresh can adopt\n // this placeholder's progress. For the root job, chainId == jobId.\n const progressMsg: Message = {\n id: `job-${jobId}`,\n role: 'assistant',\n content: 'Working on it...',\n timestamp: new Date(),\n jobProgress: {\n jobId,\n actorId: actor?.actorId,\n status: 'running',\n subStatus: 'Starting...',\n chainStartedAt: Date.now(),\n },\n chainId: jobId,\n };\n\n // Attach chainId to the optimistic user bubble so loadConversation can\n // dedup against persisted user turns by chainId.\n set(s => ({\n messages: s.messages.map(m =>\n m.id === userMsg.id ? { ...m, chainId: jobId } : m\n ).concat(progressMsg),\n activeJobId: jobId,\n }));\n\n // Start streaming for real-time progress\n streamJob(jobId, getTenant(), getToken());\n } catch (err: any) {\n set(s => ({\n messages: [...s.messages, {\n id: `error-${Date.now()}`,\n role: 'system' as const,\n content: `Error: ${err.message}`,\n timestamp: new Date(),\n }],\n loading: false,\n }));\n }\n },\n\n // -----------------------------------------------------------------------\n // Respond to sys.askUser paused question\n // -----------------------------------------------------------------------\n respondToQuestion: async (jobId: string, inputId: string, answer: string) => {\n try {\n const res = await fetch(`${getCoreApiUrl()}/api/job/${jobId}/respond`, {\n method: 'POST',\n headers: coreHeaders(),\n body: JSON.stringify({ inputId, answer }),\n });\n const data = await res.json();\n if (!data.ok) {\n console.warn('[adas-chat] Failed to respond to question:', data.error);\n }\n } catch (err: any) {\n console.warn('[adas-chat] Error responding to question:', err.message);\n }\n },\n\n // -----------------------------------------------------------------------\n // Update job progress (called by the stream handler)\n // -----------------------------------------------------------------------\n updateJobProgress: (jobId: string, progress: Partial<JobProgress>) => {\n set(s => ({\n messages: s.messages.map(m => {\n if (m.jobProgress?.jobId === jobId) {\n const updated = { ...m.jobProgress, ...progress };\n // Clear pendingQuestion when status is no longer paused\n if (progress.status && progress.status !== 'paused') {\n updated.pendingQuestion = undefined;\n }\n // Persisted turns (id `bot-…`) own their content/actions from\n // /api/conversation — the live mirror must never overwrite them.\n const isPersisted = m.id.startsWith('bot-');\n const content = isPersisted\n ? m.content\n : (progress.resultMessage\n || progress.pendingQuestion?.question\n || m.content);\n const actions = isPersisted\n ? m.actions\n : (progress.resultActions?.length ? progress.resultActions : m.actions);\n return {\n ...m,\n jobProgress: updated as JobProgress,\n content,\n ...(actions ? { actions } : {}),\n };\n }\n return m;\n }),\n loading: (progress.status === 'done' || progress.status === 'error' || progress.status === 'paused')\n ? false\n : s.loading,\n activeJobId: (progress.status === 'done' || progress.status === 'error' || progress.status === 'paused')\n ? null\n : s.activeJobId,\n }));\n },\n\n clearChat: () => {\n set({\n messages: [],\n loading: false,\n error: null,\n activeJobId: null,\n conversationLoaded: false,\n });\n },\n newChat: () => {\n set({\n messages: [],\n loading: false,\n error: null,\n activeJobId: null,\n conversationLoaded: true,\n });\n },\n}));\n\n// ---------------------------------------------------------------------------\n// WebSocket Streaming — persistent connection, reused across jobs\n// ---------------------------------------------------------------------------\n\n// Track processed plugin command correlationIds to avoid duplicates\nconst _processedPluginCmds = new Set<string>();\n\nfunction dispatchPluginCommands(cmds: any[]) {\n for (const cmd of cmds) {\n const corrId = cmd?.correlationId || cmd?.correlation_id;\n if (corrId && _processedPluginCmds.has(corrId)) continue;\n if (corrId) _processedPluginCmds.add(corrId);\n\n pluginCommandEmitter.emit('plugin-command', cmd);\n const pluginId = cmd?.pluginId || cmd?.plugin_id || cmd?.args?.plugin_id;\n if (pluginId) {\n const props = cmd?.props ?? cmd?.args?.props ?? undefined;\n pluginCommandEmitter.emit('focus-plugin', { pluginId, props });\n }\n }\n}\n\n// Persistent WS connection — stays open across jobs\nlet _ws: WebSocket | null = null;\nlet _wsReady = false;\nlet _currentJobId: string | null = null;\n\nfunction ensureWs(tenant: string, token: string | null): WebSocket {\n if (_ws && _wsReady) return _ws;\n\n const wsUrl = `${getCoreWsUrl()}/ws?token=${encodeURIComponent(token || '')}&tenant=${encodeURIComponent(tenant)}`;\n const ws = new WebSocket(wsUrl);\n _ws = ws;\n _wsReady = false;\n\n ws.onopen = () => {\n _wsReady = true;\n // If a job is waiting for the connection, subscribe now\n if (_currentJobId) {\n ws.send(JSON.stringify({ type: 'subscribe', jobId: _currentJobId }));\n }\n };\n\n ws.onmessage = (ev) => {\n try {\n const msg = JSON.parse(typeof ev.data === 'string' ? ev.data : '');\n const { event, data } = msg;\n const { updateJobProgress } = useChatStore.getState();\n // Route by the event's OWN chain id — the server streams ALL of the\n // user's active chains down one connection, so each event updates its\n // own bubble. Fall back to the last-subscribed job for older backends.\n const jobId = (data && (data.id || data.chainId)) || _currentJobId;\n\n // Always process plugin_command events even when _currentJobId is null\n // (e.g., during handoff relay)\n if (event === 'plugin_command') {\n dispatchPluginCommands([data]);\n return;\n }\n if (event === 'plugin-command-ack' || event === 'pong') return;\n\n if (!jobId) return;\n\n switch (event) {\n case 'init':\n case 'job':\n case 'update': {\n const snap = data;\n const progress = snapshotToProgress(snap, jobId);\n\n if (isTerminalSnapshot(snap)) {\n // Handoff chain: root job finished but relay job is still running.\n // The backend broadcasts relay progress as synthetic updates on\n // this same subscription — stay subscribed and show running.\n if (snap.status === 'done' && (snap.isHandoff || snap.state?.handoffTransfer)) {\n updateJobProgress(jobId, { status: 'running' });\n return;\n }\n _currentJobId = null;\n if (progress.status === 'paused' && progress.pendingQuestion) {\n updateJobProgress(jobId, progress);\n } else {\n fetchTurnResult(jobId, snap);\n }\n return;\n }\n\n updateJobProgress(jobId, progress);\n break;\n }\n case 'chain_final':\n // Authoritative chain-termination event (fires once when the WHOLE\n // chain is done — handoff-forgiven). Preferred over per-job `final`.\n _currentJobId = null;\n fetchTurnResult(jobId, data);\n break;\n case 'final':\n _currentJobId = null;\n fetchTurnResult(jobId, data);\n break;\n }\n } catch (err) {\n console.warn('[adas-chat] WS parse error:', err);\n }\n };\n\n ws.onerror = () => {\n _ws = null;\n _wsReady = false;\n // Fall back to polling for current job\n if (_currentJobId) {\n const jid = _currentJobId;\n _currentJobId = null;\n pollJob(jid, token);\n }\n };\n\n ws.onclose = () => {\n _ws = null;\n _wsReady = false;\n if (_currentJobId) {\n const jid = _currentJobId;\n _currentJobId = null;\n pollJob(jid, token);\n }\n };\n\n return ws;\n}\n\nfunction streamJob(jobId: string, tenant: string, token: string | null) {\n _currentJobId = jobId;\n const ws = ensureWs(tenant, token);\n\n // If WS is already open, subscribe immediately; otherwise onopen handles it\n if (_wsReady) {\n ws.send(JSON.stringify({ type: 'subscribe', jobId }));\n }\n}\n\n/** Tear down the live connection (env/auth switch, SDK unmount). */\nexport function closeChatStream(): void {\n _currentJobId = null;\n if (_ws) {\n try { _ws.close(); } catch {}\n }\n _ws = null;\n _wsReady = false;\n}\n\n/**\n * When a job completes, fetch the conversation turn to get the formatted\n * response. Falls back to extracting from the job snapshot.\n */\nasync function fetchTurnResult(jobId: string, jobSnap: any) {\n // Clean up dedup set (cap to prevent unbounded growth)\n if (_processedPluginCmds.size > 200) _processedPluginCmds.clear();\n const { updateJobProgress } = useChatStore.getState();\n const status = normalizeJobStatus(jobSnap) === 'error' ? 'error' : 'done';\n\n // Handoff chain: if this job handed off, wait for the chain to settle by\n // polling job status — done:true only when the relay job completes.\n if (jobSnap.isHandoff || jobSnap.state?.handoffTransfer || jobSnap.result?.contentType === 'handoff') {\n try {\n for (let i = 0; i < 40; i++) { // up to ~60s\n const statusRes = await fetch(`${getCoreApiUrl()}/api/job/${jobId}/status`, {\n headers: coreHeaders(),\n });\n if (statusRes.ok) {\n const statusData = await statusRes.json();\n if (statusData.done && !statusData.isHandoff) {\n break;\n }\n updateJobProgress(jobId, { status: 'running' });\n }\n await new Promise(r => setTimeout(r, 1500));\n }\n } catch {}\n }\n\n // Fetch the most recent conversation turn. By actor rather than by jobId,\n // because handoff chains create child jobs — the real reply lives on the\n // LAST child, not on the root job we submitted.\n for (let attempt = 0; attempt < 5; attempt++) {\n try {\n const res = await fetch(`${getCoreApiUrl()}/api/conversation?limit=1`, {\n headers: coreHeaders(),\n });\n\n if (res.ok) {\n const data = await res.json();\n const items = Array.isArray(data.items) ? data.items : [];\n const turn = items[0];\n if (turn?.assistant || turn?.turn?.assistant) {\n const assistant = turn.assistant || turn.turn?.assistant;\n const text = blocksToText(assistant.blocks);\n const actions: UIAction[] | undefined =\n Array.isArray(assistant.actions) && assistant.actions.length\n ? assistant.actions\n : undefined;\n if (text) {\n updateJobProgress(jobId, {\n status,\n resultMessage: text,\n ...(actions ? { resultActions: actions } : {}),\n });\n return;\n }\n }\n }\n } catch {}\n await new Promise(r => setTimeout(r, 800));\n }\n\n // Fallback: extract from job snapshot\n const fallback =\n jobSnap.result?.message ||\n jobSnap.state?.finalReply ||\n (status === 'error' ? 'Something went wrong. Please try again.' : 'Done.');\n\n updateJobProgress(jobId, { status, resultMessage: fallback });\n}\n\n// ---------------------------------------------------------------------------\n// Polling fallback (when WS is unavailable)\n// ---------------------------------------------------------------------------\n\nasync function pollJob(jobId: string, token: string | null) {\n const { updateJobProgress } = useChatStore.getState();\n\n for (let i = 0; i < 120; i++) {\n try {\n const res = await fetch(`${getCoreApiUrl()}/api/job/${jobId}/status`, {\n headers: {\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n },\n });\n\n if (!res.ok) {\n if (res.status === 404) return;\n continue;\n }\n\n const snap = await res.json();\n const progress = snapshotToProgress(snap, jobId);\n\n // sys.askUser — same logic as the WS handler\n if (progress.status === 'paused' && progress.pendingQuestion) {\n updateJobProgress(jobId, progress);\n return; // Stop polling — waiting for user input\n }\n\n updateJobProgress(jobId, progress);\n\n if (isTerminalSnapshot(snap)) {\n fetchTurnResult(jobId, snap);\n return;\n }\n } catch {}\n\n await new Promise(r => setTimeout(r, 1500));\n }\n\n // Timeout\n updateJobProgress(jobId, {\n status: 'error',\n resultMessage: 'Job timed out. Please try again.',\n });\n}\n","/**\n * Lightweight event emitter bridging the chat job stream → plugin bridges\n * (React Native has no Node 'events' module).\n *\n * Events: 'plugin-command' (raw command), 'focus-plugin' ({ pluginId, props }),\n * 'plugin-close' ({ pluginId }), 'prepare-chat-message' ({ text }).\n */\n\ntype Listener = (data: any) => void;\n\nexport interface BufferedCommand {\n pluginId: string;\n command: string;\n args?: any;\n correlationId?: string;\n _bufferedAt: number;\n}\n\nconst RECENT_TTL_MS = 10000;\nconst RECENT_MAX = 20;\n\nexport class PluginCommandEmitter {\n private listeners = new Map<string, Set<Listener>>();\n // Ring buffer of plugin commands seen recently. Lets a plugin that mounts\n // AFTER its first command was emitted (e.g. a surface mounted by the\n // dispatcher in response to focus-plugin) still pick that command up via\n // `drainPluginCommands(pluginId)`. Self-cleans on TTL + size cap.\n private recent: BufferedCommand[] = [];\n\n on(event: string, fn: Listener): () => void {\n if (!this.listeners.has(event)) this.listeners.set(event, new Set());\n this.listeners.get(event)!.add(fn);\n return () => this.off(event, fn);\n }\n\n off(event: string, fn: Listener): void {\n this.listeners.get(event)?.delete(fn);\n }\n\n emit(event: string, data: any): void {\n if (event === 'plugin-command' && data && data.pluginId && data.command && data.command !== '__focus') {\n this.recent.push({\n pluginId: data.pluginId,\n command: data.command,\n args: data.args,\n correlationId: data.correlationId,\n _bufferedAt: Date.now(),\n });\n if (this.recent.length > RECENT_MAX) this.recent.shift();\n }\n this.listeners.get(event)?.forEach((fn) => {\n try { fn(data); } catch {}\n });\n }\n\n /** Pop and return any plugin-commands targeted at `pluginId` that were\n * emitted within the last RECENT_TTL_MS. Used by the native bridge on\n * mount so the surface-dispatcher path doesn't drop the first command. */\n drainPluginCommands(pluginId: string): BufferedCommand[] {\n const now = Date.now();\n const out: BufferedCommand[] = [];\n const remaining: BufferedCommand[] = [];\n for (const cmd of this.recent) {\n if (now - cmd._bufferedAt > RECENT_TTL_MS) continue; // expired, drop\n if (cmd.pluginId === pluginId) out.push(cmd);\n else remaining.push(cmd);\n }\n this.recent = remaining;\n return out;\n }\n}\n\n/** App-wide bus for plugin commands arriving over the chat stream. */\nexport const pluginCommandEmitter = new PluginCommandEmitter();\n","import { getConsumerAuthApiUrl, getToken, getConfig, type AdasSession } from '../config';\nimport { useChatStore, closeChatStream } from '../chat/store';\n\nexport interface TenantMembership {\n id: string;\n name: string;\n role: string | null;\n}\n\n/**\n * List the tenants the CURRENT user belongs to (GET /api/tenants/list).\n *\n * The list is membership-scoped: for consumer JWTs the backend returns ONLY\n * tenants where the user's login identity (google/apple oauth or embed id)\n * has an active actor; builder JWTs get their user-doc memberships. It never\n * leaks the full tenant list. Returns [] when signed out or unknown.\n */\nexport async function listMyTenants(): Promise<TenantMembership[]> {\n const token = getToken();\n if (!token) return [];\n try {\n const res = await fetch(`${getConsumerAuthApiUrl()}/platform/consumer/my-tenants`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n const data = await res.json().catch(() => null);\n if (!res.ok || !data?.ok || !Array.isArray(data.tenants)) return [];\n return data.tenants.map((t: any) => ({\n id: t.id,\n name: t.name || t.id,\n role: t.role ?? null,\n }));\n } catch {\n return [];\n }\n}\n\n/**\n * Re-scope the whole session to another tenant the user already belongs to.\n *\n * The backend treats the JWT tenant claim as the sole source of truth, so\n * switching = minting a fresh JWT for the target tenant's actor. The SDK\n * owns the call (POST consumer-auth /platform/consumer/switch-tenant, authed\n * by the current token); the HOST persists the new session via\n * `config.onSession` (the SDK never stores credentials). The chat stream is\n * then torn down and cleared so it reconnects against the new tenant.\n *\n * Requires `consumerAuthUrl` and `onSession` in the initAdas() config.\n * Throws on any non-OK response (e.g. not a member of the target tenant).\n */\nexport async function switchTenant(targetTenantId: string): Promise<AdasSession> {\n const token = getToken();\n if (!token) throw new Error('[adas] switchTenant: not signed in');\n\n const res = await fetch(`${getConsumerAuthApiUrl()}/platform/consumer/switch-tenant`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ target_tenant: targetTenantId }),\n });\n\n const data = await res.json().catch(() => null);\n if (!res.ok || !data?.ok || !data.jwt) {\n throw new Error(data?.error || `switchTenant failed (${res.status})`);\n }\n\n const session: AdasSession = {\n jwt: data.jwt,\n tenant: data.tenant,\n actor: data.actor,\n };\n\n // Host persists the new token + active tenant BEFORE we reconnect, so the\n // fresh stream/requests pick up the target-tenant credentials.\n const onSession = getConfig().onSession;\n if (!onSession) {\n throw new Error('[adas] switchTenant: config.onSession is required to persist the new session');\n }\n await onSession(session);\n\n // Tear down the old-tenant stream and history; the chat screen reloads\n // against the new tenant on its next mount / loadConversation().\n closeChatStream();\n useChatStore.getState().clearChat();\n\n return session;\n}\n","// ADAS design tokens — shared by @ateam-ai-mobile/ui and host apps that want to match.\n\nexport const colors = {\n dark: {\n bgPrimary: '#0a0e14',\n bgSecondary: '#111820',\n bgTertiary: '#1a2230',\n bgInput: '#0d1219',\n bgHover: '#1e2a3a',\n border: 'rgba(255, 255, 255, 0.08)',\n borderHover: 'rgba(255, 255, 255, 0.15)',\n textPrimary: '#e8ecf1',\n textSecondary: '#8b95a5',\n textMuted: '#5a6577',\n accent: '#2563eb',\n accentHover: '#3b82f6',\n accentSoft: 'rgba(37, 99, 235, 0.15)',\n green: '#16a34a',\n greenSoft: 'rgba(22, 163, 74, 0.15)',\n red: '#dc2626',\n redSoft: 'rgba(220, 38, 38, 0.15)',\n amber: '#f59e0b',\n amberSoft: 'rgba(245, 158, 11, 0.15)',\n purple: '#8b5cf6',\n purpleSoft: 'rgba(139, 92, 246, 0.15)',\n // Message-specific\n userBubble: '#2563eb',\n botBubble: '#1a2230',\n botBubbleBorder: 'rgba(255, 255, 255, 0.08)',\n },\n light: {\n bgPrimary: '#ffffff',\n bgSecondary: '#f8f9fa',\n bgTertiary: '#f1f3f5',\n bgInput: '#ffffff',\n bgHover: '#e9ecef',\n border: 'rgba(0, 0, 0, 0.08)',\n borderHover: 'rgba(0, 0, 0, 0.15)',\n textPrimary: '#1a1a2e',\n textSecondary: '#6b7280',\n textMuted: '#9ca3af',\n accent: '#2563eb',\n accentHover: '#1d4ed8',\n accentSoft: 'rgba(37, 99, 235, 0.1)',\n green: '#16a34a',\n greenSoft: 'rgba(22, 163, 74, 0.1)',\n red: '#dc2626',\n redSoft: 'rgba(220, 38, 38, 0.1)',\n amber: '#f59e0b',\n amberSoft: 'rgba(245, 158, 11, 0.1)',\n purple: '#8b5cf6',\n purpleSoft: 'rgba(139, 92, 246, 0.1)',\n userBubble: '#2563eb',\n botBubble: '#f1f3f5',\n botBubbleBorder: 'rgba(0, 0, 0, 0.08)',\n },\n} as const;\n\n// Ada palette — warm, paper, personal. Used by the consumer chat experience.\nexport const ada = {\n paper: '#FAF6EE',\n paperSoft: '#F2EADC',\n paperDeep: '#EBE0CC',\n ink: '#1a1410',\n inkSoft: '#5a4d3f',\n inkMuted: '#9a8870',\n border: 'rgba(26, 20, 16, 0.08)',\n borderStrong: 'rgba(26, 20, 16, 0.16)',\n orange: '#FF7A28',\n orangeDeep: '#B8410A',\n orangeSoft: 'rgba(255, 122, 40, 0.14)',\n userBubble: '#1a1410',\n userBubbleText: '#FAF6EE',\n botBubble: '#F2EADC',\n botBubbleBorder: 'rgba(26, 20, 16, 0.06)',\n botBubbleText: '#1a1410',\n} as const;\n\nexport const spacing = {\n xs: 4,\n sm: 8,\n md: 12,\n lg: 16,\n xl: 20,\n xxl: 24,\n xxxl: 32,\n} as const;\n\nexport const fontSize = {\n xs: 11,\n sm: 13,\n md: 15,\n lg: 17,\n xl: 20,\n xxl: 24,\n title: 28,\n} as const;\n\nexport const borderRadius = {\n sm: 8,\n md: 12,\n lg: 16,\n xl: 20,\n full: 9999,\n} as const;\n","/**\n * ACS host DI seams. Both optional — the ACS layer works without them:\n * no reachability reporting, and the home-actions cache lives in memory only.\n */\n\n/** Host hook fired on every ACS fetch outcome — a response (any status)\n * proves the server is reachable; a thrown fetch means it isn't. The\n * reference app wires this to its connectivity banner store. */\nexport interface AcsReachabilityReporter {\n up: () => void;\n down: () => void;\n}\n\nlet _reachability: AcsReachabilityReporter | null = null;\n\nexport function setAcsReachabilityReporter(r: AcsReachabilityReporter | null): void {\n _reachability = r;\n}\n\nexport function reportAcsServerUp(): void {\n try { _reachability?.up(); } catch {}\n}\n\nexport function reportAcsServerDown(): void {\n try { _reachability?.down(); } catch {}\n}\n\n/** Minimal async KV used to persist the home-actions feed across cold starts\n * (AsyncStorage-shaped). @ateam-ai-mobile/expo wires AsyncStorage in\n * setupExpoProviders(); without a provider the cache is a no-op. */\nexport interface AcsKvStorage {\n getItem(key: string): Promise<string | null>;\n setItem(key: string, value: string): Promise<void>;\n}\n\nlet _kv: AcsKvStorage | null = null;\n\nexport function setAcsCacheStorage(kv: AcsKvStorage | null): void {\n _kv = kv;\n}\n\nexport function getAcsCacheStorage(): AcsKvStorage | null {\n return _kv;\n}\n","/**\n * Client for Core's /api/acs/* proxy → ACS service. Powers the \"widgets\"\n * surface on home: AI-generated artifacts that render as carousel tiles and\n * full-screen interactive panels via the bundleUrl — plus the home\n * \"top actions\" feed.\n *\n * Endpoints:\n * POST /api/acs/surface — list active artifacts for a surface\n * POST /api/acs/event — usage tracking (clicked/dismissed/etc.)\n * GET /api/acs/made-for-you — full library, all lifecycle stages\n * DELETE /api/acs/artifacts/{id} — user-initiated retire (idempotent)\n * PATCH /api/acs/artifacts/{id} — visibility + rename\n * PUT /api/acs/order — drag-reorder\n * GET /api/acs/home-actions — top-actions feed (+ PATCH/PUT order)\n * WS topics acs.artifacts / acs.home-actions — cross-device sync (see ./ws)\n */\n\nimport { getCoreApiUrl, getToken } from '../config';\nimport { reportAcsServerUp, reportAcsServerDown } from './providers';\n\nexport interface AcsArtifact {\n id: string;\n name: string;\n description?: string;\n iconHint?: string;\n manifest?: Record<string, any>;\n bundleUrl: string;\n version: number;\n provenance?: Record<string, any>;\n\n // Tile-shape fields (consumers branch on these when present; safe defaults\n // when absent).\n kind?: 'user' | 'featured';\n visibility?: 'pinned' | 'hidden';\n pinOrder?: number;\n canDelete?: boolean;\n canRename?: boolean;\n canHide?: boolean;\n previewMode?: 'live' | 'snapshot' | 'none';\n previewUrl?: string;\n}\n\n// Closed-ish enum — accept arbitrary strings so the backend can introduce new\n// surface names without a client release blocking it.\nexport type AcsSurface = 'home' | 'library' | (string & {});\n\n// Closed enum on the backend side.\nexport type AcsEvent = 'clicked' | 'dismissed' | 'acted' | 'ignored';\n\nexport type AcsDeleteReason =\n | 'user_long_press_delete'\n | 'user_swipe_delete'\n | 'user_bulk_delete'\n | (string & {});\n\nexport type AcsPatchReason =\n | 'user_long_press_hide'\n | 'user_long_press_unhide'\n | 'user_long_press_rename'\n | (string & {});\n\nasync function authedFetch(path: string, init?: RequestInit): Promise<Response> {\n const token = getToken();\n try {\n const res = await fetch(`${getCoreApiUrl()}${path}`, {\n ...init,\n headers: {\n 'Content-Type': 'application/json',\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...(init?.headers || {}),\n },\n });\n // Any response (even 4xx/5xx) proves the server is reachable.\n reportAcsServerUp();\n return res;\n } catch (e) {\n // Thrown = network error / timeout / DNS / TLS → server unreachable.\n reportAcsServerDown();\n throw e;\n }\n}\n\n/**\n * List artifacts surfaced for the given surface name. Returns [] on error\n * (network, auth, or empty result) — callers shouldn't have to distinguish.\n * Use surface=\"home\" for the carousel.\n */\nexport async function listSurface(\n surface: AcsSurface,\n context?: Record<string, any>,\n): Promise<AcsArtifact[]> {\n try {\n const res = await authedFetch('/api/acs/surface', {\n method: 'POST',\n body: JSON.stringify({ surface, ...(context ? { context } : {}) }),\n });\n if (!res.ok) {\n console.warn(`[acs] /surface ${surface} returned ${res.status}`);\n return [];\n }\n const data = await res.json();\n return Array.isArray(data?.items) ? (data.items as AcsArtifact[]) : [];\n } catch (e: any) {\n console.warn('[acs] /surface failed:', e?.message || e);\n return [];\n }\n}\n\n/**\n * Full library — every artifact across all lifecycle stages. Used by a\n * Library screen's Yours + Hidden sections.\n */\nexport async function listMadeForYou(): Promise<AcsArtifact[]> {\n try {\n const res = await authedFetch('/api/acs/made-for-you', { method: 'GET' });\n if (!res.ok) {\n console.warn(`[acs] /made-for-you returned ${res.status}`);\n return [];\n }\n const data = await res.json();\n return Array.isArray(data?.artifacts) ? (data.artifacts as AcsArtifact[]) : [];\n } catch (e: any) {\n console.warn('[acs] /made-for-you failed:', e?.message || e);\n return [];\n }\n}\n\nexport interface DeleteResult {\n ok: boolean;\n /** True when the server already had this artifact retired. Treat as success. */\n already?: boolean;\n /** True for HTTP 404 — artifact gone or never owned by caller. Treat as success\n * (remove from local state) per the backend's contract: no existence leak. */\n notFound?: boolean;\n /** HTTP status returned by the server. */\n status: number;\n error?: string;\n}\n\n/**\n * Retire a user widget. Per backend contract:\n * 200 ok → success\n * 200 ok + already:true → idempotent, treat as success\n * 404 → already gone or not owned, treat as success\n * 5xx / network → real failure, caller should rollback optimistic remove\n */\nexport async function deleteArtifact(\n artifactId: string,\n reason: AcsDeleteReason = 'user_long_press_delete',\n): Promise<DeleteResult> {\n try {\n const res = await authedFetch(\n `/api/acs/artifacts/${encodeURIComponent(artifactId)}`,\n { method: 'DELETE', body: JSON.stringify({ reason }) },\n );\n const data = await res.json().catch(() => ({} as any));\n if (res.ok) {\n return { ok: true, already: !!data?.already, status: res.status };\n }\n if (res.status === 404) {\n return { ok: true, notFound: true, status: 404 };\n }\n return { ok: false, status: res.status, error: data?.error || `HTTP ${res.status}` };\n } catch (e: any) {\n return { ok: false, status: 0, error: e?.message || 'network error' };\n }\n}\n\nexport interface PatchResult {\n ok: boolean;\n /** Updated tile when the server returns the new state. */\n artifact?: AcsArtifact;\n already?: boolean;\n notFound?: boolean;\n status: number;\n error?: string;\n}\n\n/**\n * Update an artifact's visibility (hide/unhide) and/or name (rename). One\n * call covers both because backend offers a single PATCH endpoint with\n * optional fields. At least one of visibility/name must be provided.\n *\n * Per backend contract (mirrors DELETE semantics):\n * 200 ok → success\n * 200 ok + already:true → idempotent no-op, treat as success\n * 404 → not found / not owned, treat as success\n * 400 / 403 → real failure, surface to user\n * 5xx / network → real failure, caller should rollback\n */\nexport async function patchArtifact(\n artifactId: string,\n body: { visibility?: 'pinned' | 'hidden'; name?: string },\n reason: AcsPatchReason,\n): Promise<PatchResult> {\n try {\n const res = await authedFetch(\n `/api/acs/artifacts/${encodeURIComponent(artifactId)}`,\n { method: 'PATCH', body: JSON.stringify({ ...body, reason }) },\n );\n const data = await res.json().catch(() => ({} as any));\n if (res.ok) {\n return {\n ok: true,\n already: !!data?.already,\n artifact: data?.artifact || undefined,\n status: res.status,\n };\n }\n if (res.status === 404) {\n return { ok: true, notFound: true, status: 404 };\n }\n return {\n ok: false,\n status: res.status,\n error: data?.error || `HTTP ${res.status}`,\n };\n } catch (e: any) {\n return { ok: false, status: 0, error: e?.message || 'network error' };\n }\n}\n\n/**\n * Persist a new pin order. Body lists currently-pinned artifact IDs in the\n * new visual order; server validates ownership + pinned state of each ID.\n *\n * Returns { ok: false, error } on failure. 400 means the array contained\n * an id that's not owned or not pinned — caller should refetch /surface\n * and re-render rather than fight the server.\n */\nexport async function setOrder(pinned: string[]): Promise<{ ok: boolean; status: number; error?: string }> {\n try {\n const res = await authedFetch('/api/acs/order', {\n method: 'PUT',\n body: JSON.stringify({ pinned }),\n });\n if (res.ok) return { ok: true, status: res.status };\n const data = await res.json().catch(() => ({} as any));\n return { ok: false, status: res.status, error: data?.error || `HTTP ${res.status}` };\n } catch (e: any) {\n return { ok: false, status: 0, error: e?.message || 'network error' };\n }\n}\n\n/**\n * Record a usage event. Fire-and-forget — never throws, never blocks the UI.\n * Use surface to tell the backend which surface the event came from\n * (e.g. 'home' for the carousel).\n */\nexport async function recordEvent(\n artifactId: string,\n event: AcsEvent,\n surface?: AcsSurface,\n): Promise<void> {\n try {\n await authedFetch('/api/acs/event', {\n method: 'POST',\n body: JSON.stringify({ artifactId, event, ...(surface ? { surface } : {}) }),\n });\n } catch {\n // intentional swallow — usage tracking failures shouldn't surface to the user\n }\n}\n\n// ─── Home Actions (top-actions feed) ────────────────────────────────────\n// Server-managed recommended-actions feed for the home \"What's on your mind?\"\n// screen. The card universe is open + agent-authored; the only fixed contract\n// is this wire shape + the leaf dispatch (send_message | open_widget).\n// Backend: GET /api/acs/home-actions, PUT /order, PATCH /:id,\n// WS topic acs.home-actions.\n\nexport type HomeActionLeaf =\n | { type: 'send_message'; prompt: string; skillSlug?: string }\n | { type: 'open_widget'; artifactId: string }\n | { type: string; [k: string]: any }; // forward-compat: open_url/run_tool/…\n\nexport interface HomeAction {\n actionId: string;\n pinOrder?: number;\n title: string;\n subtitle?: string;\n icon?: string;\n action: HomeActionLeaf;\n source?: 'curated' | 'pinned_by_user' | 'skill_authored' | (string & {});\n canDismiss?: boolean;\n canPin?: boolean;\n pinned?: boolean;\n}\n\n/** List the home top-actions feed. Returns [] on any error. */\nexport async function listHomeActions(): Promise<HomeAction[]> {\n try {\n const res = await authedFetch('/api/acs/home-actions', { method: 'GET' });\n if (!res.ok) {\n console.warn(`[acs] /home-actions returned ${res.status}`);\n return [];\n }\n const data = await res.json();\n return Array.isArray(data?.actions) ? (data.actions as HomeAction[]) : [];\n } catch (e: any) {\n console.warn('[acs] /home-actions failed:', e?.message || e);\n return [];\n }\n}\n\nexport type HomeActionReason =\n | 'user_pin' | 'user_unpin' | 'user_dismiss' | 'user_drag_reorder'\n | (string & {});\n\n/** Pin/unpin or dismiss a card. 200 already / 404 both count as success. */\nexport async function patchHomeAction(\n actionId: string,\n body: { pinned?: boolean; dismissed?: boolean },\n reason: HomeActionReason,\n): Promise<{ ok: boolean; action?: HomeAction; status: number; error?: string }> {\n try {\n const res = await authedFetch(\n `/api/acs/home-actions/${encodeURIComponent(actionId)}`,\n { method: 'PATCH', body: JSON.stringify({ ...body, reason }) },\n );\n const data = await res.json().catch(() => ({} as any));\n if (res.ok) return { ok: true, action: data?.action || undefined, status: res.status };\n if (res.status === 404) return { ok: true, status: 404 };\n return { ok: false, status: res.status, error: data?.error || `HTTP ${res.status}` };\n } catch (e: any) {\n return { ok: false, status: 0, error: e?.message || 'network error' };\n }\n}\n\n/** Persist a new card order. Optimistic; caller rolls back on failure. */\nexport async function setHomeActionOrder(\n order: string[],\n): Promise<{ ok: boolean; status: number; error?: string }> {\n try {\n const res = await authedFetch('/api/acs/home-actions/order', {\n method: 'PUT',\n body: JSON.stringify({ order }),\n });\n if (res.ok) return { ok: true, status: res.status };\n const data = await res.json().catch(() => ({} as any));\n return { ok: false, status: res.status, error: data?.error || `HTTP ${res.status}` };\n } catch (e: any) {\n return { ok: false, status: 0, error: e?.message || 'network error' };\n }\n}\n","/**\n * Tracks the user's ACS widget tiles for the home carousel. Single source of\n * truth for /api/acs/surface state. Optimistic ops with rollback on failure\n * — backend contract treats 404 / already:true as success (per spec), so the\n * store mirrors that policy: a delete that the server says was already done\n * stays removed locally.\n *\n * Cross-device freshness: the WS subscription (./ws) forwards `acs.artifacts`\n * events into the applyServer* methods; hosts should also refresh() on app\n * foreground (\"WS is best-effort; the GET is the source of truth\").\n */\n\nimport { create } from 'zustand';\nimport {\n listSurface,\n deleteArtifact,\n patchArtifact,\n setOrder,\n recordEvent,\n type AcsArtifact,\n type AcsDeleteReason,\n} from './api';\n\ninterface WidgetsState {\n /** Tiles to render in the home carousel. Order matches what /surface\n * returned; sort by pinOrder when present. */\n tiles: AcsArtifact[];\n /** True after the first /surface fetch completes (success OR empty). */\n hydrated: boolean;\n /** True while a /surface request is in flight. */\n loading: boolean;\n /** Last error string from /surface; null on success or pre-fetch. */\n error: string | null;\n\n /** First load — idempotent; safe to call from multiple effects. */\n hydrate: () => Promise<void>;\n /** Force re-fetch; call on app foreground and after destructive ops. */\n refresh: () => Promise<void>;\n /** Reset to empty (sign-out, env switch). */\n clear: () => void;\n\n /**\n * Optimistic remove. Returns { ok: false, error } only on real network /\n * 5xx failures — 404 and already:true rollback to ok:true per the backend\n * contract (no existence leak; the row is gone either way).\n */\n remove: (artifactId: string, reason?: AcsDeleteReason) => Promise<{ ok: boolean; error?: string }>;\n\n /** Optimistic hide (visibility → 'hidden'). Tile disappears from carousel. */\n hide: (artifactId: string) => Promise<{ ok: boolean; error?: string }>;\n\n /** Optimistic unhide (visibility → 'pinned'). Tile reappears in carousel. */\n unhide: (artifactId: string) => Promise<{ ok: boolean; error?: string }>;\n\n /** Optimistic rename. Trims input and rejects empty / overlong names locally. */\n rename: (artifactId: string, name: string) => Promise<{ ok: boolean; error?: string }>;\n\n /**\n * Persist a new pin order to the server. `pinnedIds` is the visible order\n * of currently-pinned tiles. Optimistic — applies locally first, rolls\n * back on server rejection.\n */\n reorder: (pinnedIds: string[]) => Promise<{ ok: boolean; error?: string }>;\n\n // ─── Server-driven mutations (no API call) ─────────────────────────────\n // The WS subscription (./ws) forwards `acs.artifacts` events into these\n // methods. They ONLY mutate local state — they DO NOT call the API (the\n // server is the source of the event, calling back would loop).\n\n /** WS 'updated' event — replace the tile by id (hide/unhide/rename land here). */\n applyServerUpdate: (artifact: AcsArtifact) => void;\n /** WS 'retired' event — remove the tile by id. */\n applyServerRetire: (artifactId: string) => void;\n /** WS 'order' event — re-rank tiles to match the pinned ID array. */\n applyServerOrder: (pinnedIds: string[]) => void;\n\n /** Fire-and-forget usage event. Caller doesn't await. */\n track: (artifactId: string, event: 'clicked' | 'dismissed' | 'acted' | 'ignored') => void;\n}\n\nexport const useWidgetsStore = create<WidgetsState>((set, get) => ({\n tiles: [],\n hydrated: false,\n loading: false,\n error: null,\n\n hydrate: async () => {\n if (get().hydrated || get().loading) return;\n await get().refresh();\n },\n\n refresh: async () => {\n if (get().loading) return;\n set({ loading: true, error: null });\n try {\n const tiles = await listSurface('home');\n set({ tiles, loading: false, hydrated: true });\n } catch (e: any) {\n set({ loading: false, hydrated: true, error: e?.message || 'Failed to load widgets' });\n }\n },\n\n clear: () => set({ tiles: [], hydrated: false, loading: false, error: null }),\n\n remove: async (artifactId, reason = 'user_long_press_delete') => {\n const before = get().tiles;\n // Optimistic: snap removed immediately so the user sees the tile vanish.\n set({ tiles: before.filter(t => t.id !== artifactId) });\n\n const result = await deleteArtifact(artifactId, reason);\n if (result.ok) {\n // Includes 200, 200 already:true, and 404 — all \"gone server-side too\".\n return { ok: true };\n }\n\n // Real failure — rollback.\n set({ tiles: before });\n return { ok: false, error: result.error || `HTTP ${result.status}` };\n },\n\n hide: async (artifactId) => {\n const before = get().tiles;\n set({\n tiles: before.map(t =>\n t.id === artifactId ? { ...t, visibility: 'hidden' as const } : t,\n ),\n });\n const r = await patchArtifact(artifactId, { visibility: 'hidden' }, 'user_long_press_hide');\n if (r.ok) {\n // If the server returned the updated artifact, sync our copy to it\n // (covers race conditions and server-side normalisation).\n if (r.artifact) {\n set({\n tiles: get().tiles.map(t => (t.id === artifactId ? { ...t, ...r.artifact! } : t)),\n });\n }\n return { ok: true };\n }\n set({ tiles: before });\n return { ok: false, error: r.error || `HTTP ${r.status}` };\n },\n\n unhide: async (artifactId) => {\n const before = get().tiles;\n set({\n tiles: before.map(t =>\n t.id === artifactId ? { ...t, visibility: 'pinned' as const } : t,\n ),\n });\n const r = await patchArtifact(artifactId, { visibility: 'pinned' }, 'user_long_press_unhide');\n if (r.ok) {\n if (r.artifact) {\n set({\n tiles: get().tiles.map(t => (t.id === artifactId ? { ...t, ...r.artifact! } : t)),\n });\n }\n return { ok: true };\n }\n set({ tiles: before });\n return { ok: false, error: r.error || `HTTP ${r.status}` };\n },\n\n rename: async (artifactId, name) => {\n // Local validation mirrors backend's 60-char cap from the spec. Keep\n // this strict so we don't hit the API with garbage that will 400 anyway.\n const trimmed = name.trim();\n if (!trimmed) return { ok: false, error: 'Name cannot be empty' };\n if (trimmed.length > 60) return { ok: false, error: 'Name must be 60 characters or less' };\n\n const before = get().tiles;\n set({\n tiles: before.map(t => (t.id === artifactId ? { ...t, name: trimmed } : t)),\n });\n const r = await patchArtifact(artifactId, { name: trimmed }, 'user_long_press_rename');\n if (r.ok) {\n if (r.artifact) {\n set({\n tiles: get().tiles.map(t => (t.id === artifactId ? { ...t, ...r.artifact! } : t)),\n });\n }\n return { ok: true };\n }\n set({ tiles: before });\n return { ok: false, error: r.error || `HTTP ${r.status}` };\n },\n\n reorder: async (pinnedIds) => {\n const before = get().tiles;\n // Optimistic: assign pinOrder by position in pinnedIds. Hidden tiles\n // are not touched. If the server rejects, we roll back to `before`.\n const idToOrder = new Map(pinnedIds.map((id, i) => [id, i] as const));\n set({\n tiles: before.map(t => {\n const o = idToOrder.get(t.id);\n return o == null ? t : { ...t, pinOrder: o };\n }),\n });\n const r = await setOrder(pinnedIds);\n if (r.ok) return { ok: true };\n set({ tiles: before });\n return { ok: false, error: r.error || `HTTP ${r.status}` };\n },\n\n applyServerUpdate: (artifact) => {\n set({\n tiles: get().tiles.map(t => (t.id === artifact.id ? { ...t, ...artifact } : t)),\n });\n },\n\n applyServerRetire: (artifactId) => {\n set({ tiles: get().tiles.filter(t => t.id !== artifactId) });\n },\n\n applyServerOrder: (pinnedIds) => {\n const idToOrder = new Map(pinnedIds.map((id, i) => [id, i] as const));\n set({\n tiles: get().tiles.map(t => {\n const o = idToOrder.get(t.id);\n return o == null ? t : { ...t, pinOrder: o };\n }),\n });\n },\n\n track: (artifactId, event) => {\n // Fire-and-forget. Surface tagged so backend analytics knows where the\n // click happened (home carousel vs library, etc.).\n recordEvent(artifactId, event, 'home');\n },\n}));\n","/**\n * The home \"What's on your mind?\" top-actions feed. Server-managed: the list,\n * its order, pin and dismiss state all live on the backend (ACS home-actions\n * API). This store is a thin local mirror with optimistic mutations + rollback\n * — same discipline as widgetsStore.\n *\n * The cards themselves are agent-authored and open-ended; clients only know\n * the wire shape + the fixed leaf dispatch (send_message | open_widget), so\n * this store stays dumb on purpose.\n */\n\nimport { create } from 'zustand';\nimport {\n listHomeActions,\n patchHomeAction,\n setHomeActionOrder,\n type HomeAction,\n} from './api';\nimport { getAcsCacheStorage } from './providers';\n\n// Persisted (when the host wires setAcsCacheStorage) so the home cards render\n// instantly on cold start and survive a dead/empty server response. The\n// server is still source of truth — when a non-empty result arrives we\n// overwrite the cache; an empty/failed fetch leaves the last good cards on\n// screen.\nconst CACHE_KEY = 'ateam.homeActions.v1';\n\nfunction persist(actions: HomeAction[]): void {\n // Fire-and-forget; never block the UI on disk.\n getAcsCacheStorage()?.setItem(CACHE_KEY, JSON.stringify(actions)).catch(() => {});\n}\n\ninterface HomeActionsState {\n actions: HomeAction[];\n hydrated: boolean;\n loading: boolean;\n\n hydrate: () => Promise<void>;\n refresh: () => Promise<void>;\n clear: () => void;\n\n /** Optimistic dismiss — removes locally, rolls back on real failure. */\n dismiss: (actionId: string) => Promise<{ ok: boolean; error?: string }>;\n /** Optimistic pin/unpin. */\n setPinned: (actionId: string, pinned: boolean) => Promise<{ ok: boolean; error?: string }>;\n /** Optimistic reorder — `orderedIds` is the new visual order. */\n reorder: (orderedIds: string[]) => Promise<{ ok: boolean; error?: string }>;\n\n // ─── WS-driven (local mutation only, no API call) ───\n applyServerUpsert: (action: HomeAction) => void;\n applyServerRemove: (actionId: string) => void;\n applyServerOrder: (orderedIds: string[]) => void;\n}\n\nfunction sortByPin(a: HomeAction, b: HomeAction): number {\n // pinned first, then pinOrder asc, then stable.\n if (!!a.pinned !== !!b.pinned) return a.pinned ? -1 : 1;\n const ao = typeof a.pinOrder === 'number' ? a.pinOrder : Number.MAX_SAFE_INTEGER;\n const bo = typeof b.pinOrder === 'number' ? b.pinOrder : Number.MAX_SAFE_INTEGER;\n return ao - bo;\n}\n\nexport const useHomeActionsStore = create<HomeActionsState>((set, get) => ({\n actions: [],\n hydrated: false,\n loading: false,\n\n hydrate: async () => {\n if (get().hydrated || get().loading) return;\n // 1. Paint cached cards immediately (survives cold start / offline).\n try {\n const cached = await getAcsCacheStorage()?.getItem(CACHE_KEY);\n if (cached) {\n const parsed = JSON.parse(cached) as HomeAction[];\n if (Array.isArray(parsed) && parsed.length > 0) {\n // Preserve the order we cached (which was the server's priority\n // order) — home layouts may split positionally, so re-sorting\n // would reshuffle the hero cards.\n set({ actions: parsed, hydrated: true });\n }\n }\n } catch { /* corrupt cache — ignore, server refresh will fix it */ }\n // 2. Then refresh from the server.\n await get().refresh();\n },\n\n refresh: async () => {\n if (get().loading) return;\n set({ loading: true });\n const actions = await listHomeActions();\n // Only overwrite when the server actually returned cards. An empty result\n // (dead API, transient 5xx, or genuinely-empty) must NOT wipe the cards\n // already on screen — keep the last good set persisted.\n if (actions.length > 0) {\n // Server delivers cards already priority-ordered — do NOT re-sort\n // (home layouts may be positional, e.g. a top-3 / rest split).\n set({ actions, loading: false, hydrated: true });\n persist(actions);\n } else {\n set({ loading: false, hydrated: true });\n }\n },\n\n // clear() wipes in-memory state on sign-out AND drops the disk cache — a\n // different user must not see the prior user's cards.\n clear: () => {\n set({ actions: [], hydrated: false, loading: false });\n getAcsCacheStorage()?.setItem(CACHE_KEY, '[]').catch(() => {});\n },\n\n dismiss: async (actionId) => {\n const before = get().actions;\n const next = before.filter(a => a.actionId !== actionId);\n set({ actions: next });\n persist(next);\n const r = await patchHomeAction(actionId, { dismissed: true }, 'user_dismiss');\n if (r.ok) return { ok: true };\n set({ actions: before });\n persist(before);\n return { ok: false, error: r.error || `HTTP ${r.status}` };\n },\n\n setPinned: async (actionId, pinned) => {\n const before = get().actions;\n const next = before.map(a => (a.actionId === actionId ? { ...a, pinned } : a)).sort(sortByPin);\n set({ actions: next });\n persist(next);\n const r = await patchHomeAction(actionId, { pinned }, pinned ? 'user_pin' : 'user_unpin');\n if (r.ok) {\n if (r.action) {\n const merged = get().actions.map(a => (a.actionId === actionId ? { ...a, ...r.action! } : a)).sort(sortByPin);\n set({ actions: merged });\n persist(merged);\n }\n return { ok: true };\n }\n set({ actions: before });\n persist(before);\n return { ok: false, error: r.error || `HTTP ${r.status}` };\n },\n\n reorder: async (orderedIds) => {\n const before = get().actions;\n const idx = new Map(orderedIds.map((id, i) => [id, i] as const));\n const next = before.map(a => {\n const o = idx.get(a.actionId);\n return o == null ? a : { ...a, pinOrder: o };\n }).sort(sortByPin);\n set({ actions: next });\n persist(next);\n const r = await setHomeActionOrder(orderedIds);\n if (r.ok) return { ok: true };\n set({ actions: before });\n persist(before);\n return { ok: false, error: r.error || `HTTP ${r.status}` };\n },\n\n applyServerUpsert: (action) => {\n const rest = get().actions.filter(a => a.actionId !== action.actionId);\n const next = [...rest, action].sort(sortByPin);\n set({ actions: next });\n persist(next);\n },\n applyServerRemove: (actionId) => {\n const next = get().actions.filter(a => a.actionId !== actionId);\n set({ actions: next });\n persist(next);\n },\n applyServerOrder: (orderedIds) => {\n const idx = new Map(orderedIds.map((id, i) => [id, i] as const));\n const next = get().actions.map(a => {\n const o = idx.get(a.actionId);\n return o == null ? a : { ...a, pinOrder: o };\n }).sort(sortByPin);\n set({ actions: next });\n persist(next);\n },\n}));\n","/**\n * Long-lived WebSocket subscription to the `acs.artifacts` and\n * `acs.home-actions` topics. Protocol (backend websocket.js):\n *\n * client → server : { type: \"subscribe\", topic: \"acs.artifacts\" }\n * server → client : { event: \"subscribed\", data: { topic: \"acs.artifacts\" } }\n * server → client : { topic: \"acs.artifacts\", event: \"updated\", artifact: {...} }\n * server → client : { topic: \"acs.artifacts\", event: \"retired\", artifactId: \"...\" }\n * server → client : { topic: \"acs.artifacts\", event: \"order\", pinned: [...] }\n *\n * Events flow into the stores' applyServer* methods so we don't loop (those\n * methods mutate local state only — no API calls).\n *\n * Lifecycle:\n * connectAcsWs(token, tenant) starts a single connection + subscribe.\n * On disconnect, exponential-backoff reconnect (capped). Call\n * notifyAcsWsForeground() from the host's AppState listener to\n * force-reconnect when the socket dropped while backgrounded (this module\n * is host-agnostic — no react-native import — so the host owns AppState).\n * On signout, disconnectAcsWs() closes the socket and stops reconnects.\n *\n * Backend's spec note: \"WS is best-effort; the GET is the source of truth\"\n * — so widgetsStore.refresh() on foreground stays the safety net even when\n * the WS is healthy. This file is the realtime cherry on top.\n */\n\nimport { getCoreWsUrl } from '../config';\nimport { useWidgetsStore } from './widgetsStore';\nimport { useHomeActionsStore } from './homeActionsStore';\n\nlet _ws: WebSocket | null = null;\nlet _wantOpen = false;\nlet _retryAttempt = 0;\nlet _retryTimer: ReturnType<typeof setTimeout> | null = null;\nlet _token: string | null = null;\nlet _tenant: string | null = null;\n\nconst TOPIC = 'acs.artifacts';\nconst TOPIC_HOME = 'acs.home-actions';\nconst MAX_BACKOFF_MS = 30_000;\n\nfunction backoffMs(attempt: number): number {\n // 0.5s, 1s, 2s, 4s, ... capped at 30s. Tiny jitter to avoid thundering herd.\n const base = Math.min(MAX_BACKOFF_MS, 500 * Math.pow(2, attempt));\n const jitter = Math.random() * 250;\n return base + jitter;\n}\n\nfunction send(obj: any) {\n if (_ws && _ws.readyState === 1) {\n try { _ws.send(JSON.stringify(obj)); } catch (e) { console.warn('[acsWs] send failed', e); }\n }\n}\n\nfunction handleEvent(msg: any) {\n // The subscribe confirmation comes as { event: \"subscribed\", data: {...} }\n // (no `topic` field). Real events come as { topic, event, ...payload }.\n if (msg?.event === 'subscribed') {\n console.log('[acsWs] subscribed to', msg?.data?.topic);\n return;\n }\n\n // Home-actions feed events route to its own store.\n if (msg?.topic === TOPIC_HOME) {\n const home = useHomeActionsStore.getState();\n switch (msg.event) {\n case 'updated':\n if (msg.action && typeof msg.action.actionId === 'string') home.applyServerUpsert(msg.action);\n break;\n case 'removed':\n if (typeof msg.actionId === 'string') home.applyServerRemove(msg.actionId);\n break;\n case 'order':\n if (Array.isArray(msg.order)) home.applyServerOrder(msg.order.filter((id: any) => typeof id === 'string'));\n break;\n case 'refreshed':\n home.refresh();\n break;\n default:\n break;\n }\n return;\n }\n\n if (msg?.topic !== TOPIC) return;\n\n const store = useWidgetsStore.getState();\n switch (msg.event) {\n case 'updated':\n if (msg.artifact && typeof msg.artifact.id === 'string') {\n store.applyServerUpdate(msg.artifact);\n }\n break;\n case 'retired':\n if (typeof msg.artifactId === 'string') {\n store.applyServerRetire(msg.artifactId);\n }\n break;\n case 'order':\n if (Array.isArray(msg.pinned)) {\n store.applyServerOrder(msg.pinned.filter((id: any) => typeof id === 'string'));\n }\n break;\n case 'created':\n // Backend spec lists `created` as TODO. Until it ships, host foreground\n // refresh catches new widgets. When it lands the WS will also fire\n // `updated` for the new tile, which is already handled. For now, treat\n // any inbound created the same as updated.\n if (msg.artifact && typeof msg.artifact.id === 'string') {\n store.applyServerUpdate(msg.artifact);\n }\n break;\n default:\n // Unknown event — ignore so we stay forward-compatible with new types.\n break;\n }\n}\n\nfunction openSocket() {\n if (_ws) return;\n if (!_token || !_tenant) return;\n\n const url = `${getCoreWsUrl()}/ws?token=${encodeURIComponent(_token)}&tenant=${encodeURIComponent(_tenant)}`;\n console.log(`[acsWs] opening`);\n const ws = new WebSocket(url);\n _ws = ws;\n\n ws.onopen = () => {\n _retryAttempt = 0;\n send({ type: 'subscribe', topic: TOPIC });\n send({ type: 'subscribe', topic: TOPIC_HOME });\n };\n ws.onmessage = (e) => {\n try {\n const msg = JSON.parse(typeof e.data === 'string' ? e.data : '');\n handleEvent(msg);\n } catch (err) {\n // Non-JSON or malformed frame — ignore, log for debugging.\n console.warn('[acsWs] parse failed', err);\n }\n };\n ws.onerror = (e: any) => {\n console.warn('[acsWs] error', e?.message || e);\n };\n ws.onclose = () => {\n _ws = null;\n if (!_wantOpen) return;\n const delay = backoffMs(_retryAttempt++);\n console.log(`[acsWs] closed; reconnecting in ${Math.round(delay)}ms (attempt ${_retryAttempt})`);\n if (_retryTimer) clearTimeout(_retryTimer);\n _retryTimer = setTimeout(openSocket, delay);\n };\n}\n\n/**\n * Call from the host's AppState listener when the app returns to the\n * foreground — if the socket dropped while backgrounded, reconnect\n * immediately rather than waiting out the backoff timer.\n */\nexport function notifyAcsWsForeground() {\n if (_wantOpen && !_ws) {\n if (_retryTimer) { clearTimeout(_retryTimer); _retryTimer = null; }\n _retryAttempt = 0;\n openSocket();\n }\n}\n\n/**\n * Start the ACS WS subscription. Idempotent — calling twice with the same\n * token/tenant is a no-op. Call once the host says the user is signed in\n * (JWT available).\n */\nexport function connectAcsWs(token: string, tenant: string) {\n if (_ws && _wantOpen && _token === token && _tenant === tenant) return;\n // Token or tenant changed → tear down + reopen.\n if (_ws) {\n try { _ws.close(); } catch {}\n _ws = null;\n }\n _token = token;\n _tenant = tenant;\n _wantOpen = true;\n openSocket();\n}\n\n/**\n * Stop the ACS WS subscription. Call on signout / env switch.\n */\nexport function disconnectAcsWs() {\n _wantOpen = false;\n _token = null;\n _tenant = null;\n if (_retryTimer) { clearTimeout(_retryTimer); _retryTimer = null; }\n if (_ws) {\n try { _ws.close(); } catch {}\n _ws = null;\n }\n}\n"],"mappings":"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","names":["text","import_zustand","import_zustand","_ws"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/api/client.ts","../src/api/mcp.ts","../src/session.ts","../src/authSession.ts","../src/pushRegistration.ts","../src/acs/widgetsStore.ts","../src/acs/providers.ts","../src/acs/api.ts","../src/acs/homeActionsStore.ts","../src/acs/ws.ts","../src/lifecycle.ts","../src/gate.ts","../src/consumerAuth.ts","../src/deepLinks.ts","../src/workspace.ts","../src/chat/store.ts","../src/utils/emitter.ts","../src/api/tenants.ts","../src/theme/tokens.ts"],"sourcesContent":["/**\n * @ateam-ai-mobile/core — headless ADAS SDK for React Native.\n *\n * The host app owns authentication and navigation; this package provides\n * everything between the ADAS backend and the UI: config/auth bridge, API\n * client, chat store with live job streaming, MCP tool calls, and theme\n * tokens. No Expo dependencies — works in Expo and bare RN apps alike.\n *\n * Quick start:\n * initAdas({\n * coreUrl: 'https://app.ateam-ai.com',\n * tokenProvider: { getToken, refresh, onAuthFailure },\n * getUser: () => ({ actorId: currentUser.id }),\n * });\n * const { messages, sendMessage } = useChatStore();\n */\n\n// Config & auth bridge\nexport {\n initAdas,\n isAdasInitialized,\n getConfig,\n getCoreApiUrl,\n getCoreWsUrl,\n getVoiceApiUrl,\n getConsumerAuthApiUrl,\n getTenant,\n getUser,\n getToken,\n refreshToken,\n} from './config';\nexport type { AdasConfig, TokenProvider, RefreshOutcome, AdasUser, AdasSession } from './config';\n\n// API client\nexport { apiFetch, apiFetchRaw } from './api/client';\n\n// MCP / connector calls\nexport {\n connectorCall,\n mcpFetch,\n mcpToolCall,\n postCommandResult,\n unwrapMcpResult,\n} from './api/mcp';\n\n// Tenant memberships\nexport { getSession, isDevHost } from './session';\nexport type { Session, SessionEnv } from './session';\n\n// SDK-owned session state machine (R6) — JWT lifecycle, refresh table, restore,\n// logout orchestration. The host provides secure storage + a logout teardown hook.\nexport { createAuthSession, userFromJwt } from './authSession';\nexport type {\n AuthSession,\n AuthSessionState,\n AuthSessionDeps,\n SessionPersistence,\n SessionUser,\n SessionStatus,\n} from './authSession';\n\n// Push registration lifecycle + app-foreground coordinator (R7) — the host\n// supplies the native token acquisition + the backend calls; the SDK owns the\n// register/re-register/unregister lifecycle and the foreground reaction order.\nexport { createPushRegistration } from './pushRegistration';\nexport type { PushRegistration, PushBackend } from './pushRegistration';\nexport { onAppForeground, notifyAppForeground } from './lifecycle';\n\n// App-gate cascade (R9) — the pure \"which phase to render\" decision the host's\n// root layout consumes; the host keeps the branded phase screens + the router.\nexport { resolveAdasPhase } from './gate';\nexport type { AdasPhase, AdasGateFlags } from './gate';\n\n// Consumer-auth flow (R9) — config fetch + provider/handoff/invite/open-join\n// exchanges + channel OTP. The host acquires the native id_token; this trades it\n// for a session JWT. Reads only the SDK config getters.\nexport {\n fetchConsumerConfig,\n signInWithGoogle,\n signInWithApple,\n exchangeHandoff,\n previewInvite,\n joinWithGoogle,\n joinWithApple,\n openJoinWithGoogle,\n openJoinWithApple,\n linkChannelStart,\n linkChannelVerify,\n} from './consumerAuth';\nexport type {\n ConsumerProviderConfig,\n ConsumerCapabilityConfig,\n ConsumerSignInResult,\n OtpStartResult,\n HandoffExchangeResult,\n InvitePreview,\n} from './consumerAuth';\n\n// Deep-link code parsing (R9) — generic invite/handoff extraction; the URL\n// scheme stays host-side.\nexport { parseDeepLinkParam, parseInviteCode, parseHandoffCode } from './deepLinks';\n\n// Workspace subscription state (server-resolved; GET /api/session)\nexport {\n useWorkspaceStore,\n capabilityEnabled,\n useCapability,\n} from './workspace';\nexport type {\n WorkspaceSnapshot,\n WorkspaceState,\n WorkspaceCapabilities,\n} from './workspace';\nexport { listMyTenants, switchTenant } from './api/tenants';\nexport type { TenantMembership } from './api/tenants';\n\n// Chat\nexport {\n setClientDeclarationProvider,\n useChatStore,\n closeChatStream,\n blocksToText,\n TABLE_BLOCK_OPEN,\n TABLE_BLOCK_CLOSE,\n} from './chat/store';\nexport type {\n Message,\n MessageAttachment,\n JobProgress,\n UIAction,\n ToolCall,\n} from './chat/types';\n\n// Storage adapter contracts\nexport type {\n AttachmentCache,\n AttachmentPreview,\n OutgoingAttachment,\n} from './storage/types';\n\n// Plugin command bus\nexport { pluginCommandEmitter, PluginCommandEmitter } from './utils/emitter';\nexport type { BufferedCommand } from './utils/emitter';\n\n// Theme tokens\nexport { colors, ada, spacing, fontSize, borderRadius } from './theme/tokens';\n\n// ACS (home widgets + top-actions feed)\nexport {\n listSurface,\n listMadeForYou,\n deleteArtifact,\n patchArtifact,\n setOrder,\n recordEvent,\n listHomeActions,\n patchHomeAction,\n setHomeActionOrder,\n} from './acs/api';\nexport type {\n AcsArtifact,\n AcsSurface,\n AcsEvent,\n AcsDeleteReason,\n AcsPatchReason,\n DeleteResult,\n PatchResult,\n HomeAction,\n HomeActionLeaf,\n HomeActionReason,\n} from './acs/api';\nexport { useWidgetsStore } from './acs/widgetsStore';\nexport { useHomeActionsStore } from './acs/homeActionsStore';\nexport { connectAcsWs, disconnectAcsWs, notifyAcsWsForeground } from './acs/ws';\nexport {\n setAcsReachabilityReporter,\n setAcsCacheStorage,\n} from './acs/providers';\nexport type { AcsReachabilityReporter, AcsKvStorage } from './acs/providers';\n","/**\n * SDK configuration — the single injection point between the host app and\n * the ADAS core. The host owns authentication, identity, and URL discovery;\n * the SDK reads everything through the config registered via initAdas().\n *\n * There is intentionally ONE active config per JS context (module singleton):\n * the chat store, WebSocket, and API client below it are app-wide singletons\n * too, and a second tenant/backend inside one app is not a supported v1 case.\n */\n\nimport type { AttachmentCache } from './storage/types';\n\n/** Result of a host-driven token refresh. Mirrors the semantics the API\n * client depends on: only 'auth_failed' logs the user out; 'transient'\n * (network blip, 5xx) keeps the session so the next call can retry. */\nexport interface RefreshOutcome {\n ok: 'ok' | 'auth_failed' | 'transient';\n status: number;\n reason?: string;\n}\n\n/**\n * Host-implemented auth bridge. The SDK never shows login UI and never\n * stores credentials — the host authenticates its own users and hands the\n * SDK a way to read/refresh the ADAS JWT it minted for them (via the\n * server-to-server token exchange).\n */\nexport interface TokenProvider {\n /** Current ADAS JWT, or null when signed out. Called on every request —\n * keep it synchronous and cheap (read from memory, not disk). */\n getToken(): string | null;\n /** Called on 401. Host should mint/refresh the ADAS token and return the\n * outcome; 'ok' makes the SDK retry the failed request once. */\n refresh(): Promise<RefreshOutcome>;\n /** Auth is genuinely dead (refresh rejected, or retry still 401). Host\n * should treat this as sign-out for the embedded experience. */\n onAuthFailure?: () => void;\n}\n\nexport interface AdasUser {\n actorId: string;\n name?: string;\n email?: string;\n /** Roles from the host's identity (e.g. JWT claims) — forwarded to plugins\n * as PluginContext.user.roles. */\n roles?: string[];\n}\n\n/** A URL that is either static or resolved on each read. Hosts with a\n * runtime env switcher (dev/prod) pass a getter so the SDK always sees the\n * currently-selected backend without re-calling initAdas(). */\nexport type UrlSource = string | (() => string);\n\nexport interface AdasConfig {\n /** Core API base URL, e.g. https://app.ateam-ai.com. Pass a getter to\n * support a runtime env switch without re-initializing. */\n coreUrl: UrlSource;\n /** Voice service base URL (optional — only voice features need it). */\n voiceUrl?: UrlSource;\n /** Consumer-auth base URL, e.g. https://consumer-auth.ateam-ai.com. Only\n * needed for `switchTenant()` (re-scoping the session to another tenant\n * the user belongs to). */\n consumerAuthUrl?: UrlSource;\n tokenProvider: TokenProvider;\n /** Tenant slug — used for plugin URL resolution and voice, never sent as a\n * header (the backend derives the tenant from the JWT). Pass a getter when\n * the host resolves the tenant after startup (storage hydration, runtime\n * tenant switch). */\n tenant?: string | (() => string);\n /** Current end user as known to the host. actorId is used as the chat\n * reply context; without it, replies route to 'unknown'. */\n getUser?: () => AdasUser | null;\n /** Optional persistence for outgoing image/file attachments so previews\n * survive app restarts. No-op when omitted (previews live in memory for\n * the session only). @ateam-ai-mobile/ui ships an Expo/RN implementation. */\n attachmentCache?: AttachmentCache;\n /** Called by `switchTenant()` with the freshly-minted session for the\n * target tenant. The host MUST persist it here (store the new JWT so\n * tokenProvider.getToken() returns it, and update the active tenant) —\n * the SDK never stores credentials. Awaited before the chat reconnects. */\n onSession?: (session: AdasSession) => void | Promise<void>;\n}\n\n/** A tenant-scoped session minted by the platform (e.g. via switchTenant). */\nexport interface AdasSession {\n jwt: string;\n tenant: { id: string; name: string };\n actor: { id: string; email: string | null; roles: string[] };\n}\n\nlet _config: AdasConfig | null = null;\n\n/** Register the SDK configuration. Call once at app start, before any other\n * SDK API. Calling again replaces the config (e.g. env switch) — combine\n * with chat.clearChat() and closeChatStream() for a clean swap. */\nexport function initAdas(config: AdasConfig): void {\n _config = config;\n}\n\nexport function isAdasInitialized(): boolean {\n return _config !== null;\n}\n\nexport function getConfig(): AdasConfig {\n if (!_config) {\n throw new Error('[adas] SDK not initialized — call initAdas(config) before using any ADAS API');\n }\n return _config;\n}\n\nfunction resolveUrl(src: UrlSource): string {\n return (typeof src === 'function' ? src() : src).replace(/\\/$/, '');\n}\n\nexport function getCoreApiUrl(): string {\n return resolveUrl(getConfig().coreUrl);\n}\n\nexport function getCoreWsUrl(): string {\n return getCoreApiUrl().replace(/^http/, 'ws');\n}\n\nexport function getVoiceApiUrl(): string {\n const url = getConfig().voiceUrl;\n if (!url) throw new Error('[adas] voiceUrl not configured');\n return resolveUrl(url);\n}\n\nexport function getConsumerAuthApiUrl(): string {\n const url = getConfig().consumerAuthUrl;\n if (!url) throw new Error('[adas] consumerAuthUrl not configured — required for switchTenant()');\n return resolveUrl(url);\n}\n\nexport function getTenant(): string {\n const t = getConfig().tenant;\n return (typeof t === 'function' ? t() : t) || '';\n}\n\nexport function getUser(): AdasUser | null {\n return getConfig().getUser?.() || null;\n}\n\n// ─── Internal token helpers (thin wrappers over the host's TokenProvider) ──\n\nexport function getToken(): string | null {\n return getConfig().tokenProvider.getToken();\n}\n\nexport async function refreshToken(): Promise<RefreshOutcome> {\n return getConfig().tokenProvider.refresh();\n}\n\nexport function notifyAuthFailure(): void {\n getConfig().tokenProvider.onAuthFailure?.();\n}\n","import { getCoreApiUrl, getVoiceApiUrl, getToken, refreshToken, notifyAuthFailure } from '../config';\n\ntype FetchOptions = RequestInit & {\n baseUrl?: 'core' | 'voice';\n};\n\n/**\n * Base API client with JWT injection and 401 refresh handling.\n *\n * Auth failure semantics (preserved from the reference app): a transient\n * 5xx or network blip on the RETRY after a successful refresh must NOT be\n * treated as sign-out. notifyAuthFailure() fires only when auth is genuinely\n * broken:\n * - Refresh itself was rejected with 401/403 → auth failure\n * - Retry with the fresh JWT still returns 401/403 → auth failure\n * Anything else throws but keeps the session intact — the caller may retry\n * on the next user action.\n */\nexport async function apiFetch<T = unknown>(\n path: string,\n options: FetchOptions = {},\n): Promise<T> {\n const { baseUrl = 'core', ...fetchOptions } = options;\n const base = baseUrl === 'voice' ? getVoiceApiUrl() : getCoreApiUrl();\n const url = `${base}${path}`;\n\n const token = getToken();\n\n const headers: Record<string, string> = {\n ...(fetchOptions.headers as Record<string, string>),\n };\n\n if (token && !headers['Authorization']) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n // Note: we intentionally DO NOT send a tenant header. The backend derives\n // the tenant from the JWT — a client-supplied tenant paired with a leaked\n // JWT is a tenant-pivot footgun. Tenant comes from the token, not the client.\n\n if (!headers['Content-Type'] && fetchOptions.body && typeof fetchOptions.body === 'string') {\n headers['Content-Type'] = 'application/json';\n }\n\n const res = await fetch(url, { ...fetchOptions, headers });\n\n if (res.status === 401 && token) {\n console.warn(`[adas] 401 on ${path} — attempting refresh`);\n const outcome = await refreshToken();\n if (outcome.ok === 'ok') {\n const newToken = getToken();\n headers['Authorization'] = `Bearer ${newToken}`;\n const retryRes = await fetch(url, { ...fetchOptions, headers });\n if (retryRes.ok) {\n return retryRes.json() as Promise<T>;\n }\n if (retryRes.status === 401 || retryRes.status === 403) {\n // Still auth-broken with a fresh JWT — genuinely dead session.\n console.warn(`[adas] retry after refresh still ${retryRes.status} on ${path} — auth failure`);\n notifyAuthFailure();\n throw new Error('Session expired');\n }\n // Non-auth error on retry. Surface it, but the session is fine.\n const text = await retryRes.text().catch(() => '');\n throw new Error(`API ${retryRes.status}: ${text}`);\n }\n if (outcome.ok === 'auth_failed') {\n console.warn(`[adas] refresh auth_failed on ${path} (${outcome.reason}) — auth failure`);\n notifyAuthFailure();\n throw new Error('Session expired');\n }\n // 'transient' — network/5xx on refresh. Keep the session; surface the 401.\n throw new Error(`API 401: refresh transient (${outcome.reason})`);\n }\n\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`API ${res.status}: ${text}`);\n }\n\n return res.json() as Promise<T>;\n}\n\n/**\n * Fetch with raw response (non-JSON responses, streaming bodies).\n */\nexport async function apiFetchRaw(\n path: string,\n options: FetchOptions = {},\n): Promise<Response> {\n const { baseUrl = 'core', ...fetchOptions } = options;\n const base = baseUrl === 'voice' ? getVoiceApiUrl() : getCoreApiUrl();\n const url = `${base}${path}`;\n\n const token = getToken();\n const headers: Record<string, string> = {\n ...(fetchOptions.headers as Record<string, string>),\n };\n\n if (token && !headers['Authorization']) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n return fetch(url, { ...fetchOptions, headers });\n}\n","/**\n * Shared MCP client — single API layer for ALL plugin communication.\n *\n * Two call patterns:\n * 1. connectorCall(connectorId, tool, args) → POST /api/connectors/:id/call\n * 2. mcpFetch(body) → POST /mcp (raw JSON-RPC passthrough)\n */\nimport { apiFetchRaw } from './client';\n\n/**\n * Call a tool on a specific connector via POST /api/connectors/:id/call.\n *\n * NOTE: intentionally bypasses the /mcp proxy (which routes to adas-mcp).\n * adas-mcp only knows skill-generated tools, NOT raw connector tools like\n * triggers.list — the direct connector API is the reliable path.\n */\nexport async function connectorCall(\n connectorId: string,\n tool: string,\n args: Record<string, any> = {},\n): Promise<{ ok: boolean; result: any; error?: string }> {\n try {\n const res = await apiFetchRaw(\n `/api/connectors/${encodeURIComponent(connectorId)}/call`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ tool, args }),\n },\n );\n\n if (!res.ok) {\n const text = await res.text();\n return { ok: false, result: null, error: `HTTP ${res.status}: ${text}` };\n }\n\n const data = await res.json();\n // Normalize: Core may return { ok, result } or raw MCP { content: [...] }\n if (data.ok !== undefined) return data;\n return { ok: true, result: data };\n } catch (err) {\n return { ok: false, result: null, error: err instanceof Error ? err.message : String(err) };\n }\n}\n\n/** Raw JSON-RPC passthrough to /mcp. Returns the full JSON-RPC response. */\nexport async function mcpFetch(body: any): Promise<any> {\n const res = await apiFetchRaw('/mcp', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: typeof body === 'string' ? body : JSON.stringify(body),\n });\n\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`MCP fetch failed: HTTP ${res.status}: ${text}`);\n }\n\n return res.json();\n}\n\n/** JSON-RPC tools/call to /mcp (for core tools like cp.fe_api). */\nexport async function mcpToolCall(\n toolName: string,\n args: Record<string, any> = {},\n): Promise<any> {\n return mcpFetch({\n jsonrpc: '2.0',\n id: Date.now(),\n method: 'tools/call',\n params: { name: toolName, arguments: args },\n });\n}\n\n/** Post a plugin command result back to Core. */\nexport async function postCommandResult(\n correlationId: string,\n result: any,\n error?: string,\n): Promise<void> {\n await apiFetchRaw('/api/plugin-command-result', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ correlationId, result, error }),\n });\n}\n\n/**\n * Unwrap MCP JSON-RPC text content → parsed business data.\n * Server returns: { content: [{ type: \"text\", text: \"{...}\" }] }\n */\nexport function unwrapMcpResult(result: any): any {\n if (!result) return result;\n if (!result.content) return result;\n const textItem = Array.isArray(result.content)\n ? result.content.find((c: any) => c.type === 'text')\n : null;\n if (!textItem?.text) return result;\n try {\n return JSON.parse(textItem.text);\n } catch {\n return textItem.text;\n }\n}\n","// packages/core/src/session.ts\n//\n// THE single answer to \"where am I / what am I talking to / who am I\", derived\n// from the SDK config the REQUESTS themselves use — getTenant / getConsumerAuthApiUrl\n// / getCoreApiUrl / getUser. Because the label reads from the same place the\n// request reads, they cannot disagree. Host-agnostic: any host that calls\n// initAdas() gets a correct session with no parallel copy to drift.\n//\n// Rule: DERIVE, NEVER STORE — see Docs/DERIVE_NEVER_STORE.md. This lives in the\n// SDK (not a host app) so iOS/Android/RN hosts share one derivation; the host's\n// React hook (e.g. useSession) is a thin wrapper that just re-renders on change.\n\nimport { getTenant, getConsumerAuthApiUrl, getCoreApiUrl, getUser } from './config';\n\nexport type SessionEnv = 'dev' | 'prod';\n\nexport interface Session {\n /** Tenant slug the app is actually scoped to (getTenant = signed-in tenant,\n * falling back to the pre-auth choice). Null when neither is set. */\n tenant: string | null;\n /** Human label for the tenant — slug today; callers must not compute their own. */\n tenantName: string;\n /** dev | prod — derived from the resolved host, never a stored word. */\n env: SessionEnv;\n /** The actual backend host — a host can't be ambiguous the way a word can. */\n host: string;\n actorId: string | null;\n email: string | null;\n}\n\nfunction hostOf(u: string | undefined | null): string {\n if (!u) return '';\n try { return new URL(u).host; } catch { return u; }\n}\n\n/** Is a resolved base URL a dev backend? Keyed on the hostname (dev-app…,\n * dev-consumer-auth…) so it matches wherever the request goes. */\nexport function isDevHost(u: string | undefined | null): boolean {\n const h = hostOf(u);\n return !!h && (h.includes('dev-') || /(^|\\.)dev\\./.test(h));\n}\n\n/** Compute the session from the live SDK config. Plain function — a host hook\n * wraps this for reactivity; imperative code can call it directly. */\nexport function getSession(): Session {\n let consumerUrl = '';\n try { consumerUrl = getConsumerAuthApiUrl(); } catch { /* not configured yet */ }\n let coreUrl = '';\n try { coreUrl = getCoreApiUrl(); } catch { /* not configured yet */ }\n\n const tenant = getTenant() || null;\n const user = getUser();\n\n return {\n tenant,\n tenantName: tenant || '—',\n env: isDevHost(consumerUrl) || isDevHost(coreUrl) ? 'dev' : 'prod',\n host: hostOf(consumerUrl || coreUrl),\n actorId: user?.actorId ?? null,\n email: user?.email ?? null,\n };\n}\n","/**\n * SDK-owned session state machine (R6).\n *\n * Until R6 the JWT lifecycle lived in the app (apps/mobile/src/stores/\n * authStore.ts): token storage, the decode/user projection, the startup\n * restore + silent refresh, and the refresh decision table. A second host\n * (host-demo, a third-party app) had to re-implement all of it. R6 moves the\n * MECHANISM here; the host keeps only what is genuinely its own — secure\n * storage and the side effects of signing out.\n *\n * Two seams keep this free of any app/Expo import and free of an auth<->bridge\n * cycle (the plan's explicit constraint):\n * - `persistence` the host's secure JWT storage (get/save/clear). The SDK\n * never imports expo-secure-store.\n * - `onLogout` the host's teardown, run in ONE place so the order is\n * owned here: stop the device/relay bridge, unregister push\n * (while the token still exists), clear plugin storage +\n * bundle cache + device-tool registry, close chat. The SDK\n * calls it; it never imports the stores that do the work.\n *\n * The refresh decision table and JWT projection are moved VERBATIM from the app\n * so behaviour is identical (the R0.5 authRefresh baseline is the oracle).\n */\nimport { getCoreApiUrl, type RefreshOutcome } from './config';\n\nexport interface SessionUser {\n actorId: string;\n email?: string;\n displayName?: string;\n // Explicit role axes (Phase 4). `roles` is legacy (empty for newer tokens).\n tenantRole?: string | null;\n serviceRole?: string | null;\n platformRoles?: string[];\n roles: string[];\n}\n\nexport type SessionStatus = 'loading' | 'authenticated' | 'signed_out';\n\nexport interface AuthSessionState {\n token: string | null;\n user: SessionUser | null;\n status: SessionStatus;\n}\n\n/**\n * The host's secure JWT storage. Only the token lives here — tenant selection\n * and env are resolved through the config getters, not stored by the session\n * (derive-never-store).\n */\nexport interface SessionPersistence {\n loadToken(): Promise<string | null>;\n saveToken(token: string): Promise<void>;\n clearToken(): Promise<void>;\n}\n\nexport interface AuthSessionDeps {\n persistence: SessionPersistence;\n /**\n * Host teardown for sign-out, run by `logout()` WHILE THE TOKEN STILL EXISTS\n * (push unregister needs the JWT) and BEFORE the SDK clears it. The host does\n * the app-specific work — stop the bridge, unregister push, clear the tenant\n * keys, evict plugin storage / bundle cache / device-tool registry, close\n * chat — in this one hook, so there is no auth->bridge import cycle.\n */\n onLogout?: () => void | Promise<void>;\n /**\n * Called when a token is adopted (login, or a successful refresh) with the\n * projected user, so the host can persist side data (e.g. its TENANT key)\n * without the session reaching into app storage.\n */\n onToken?: (token: string, user: SessionUser | null) => void | Promise<void>;\n}\n\n// ── JWT projection (verbatim from the app) ──────────────────────────────────\n\nfunction decodeJwtPayload(token: string): Record<string, any> | null {\n try {\n const segs = token.split('.');\n if (segs.length !== 3) return null;\n // base64url → base64\n const b64 = segs[1].replace(/-/g, '+').replace(/_/g, '/');\n // Pad to a multiple of 4\n const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4);\n const json = typeof atob === 'function'\n ? atob(padded)\n // Hermes always has atob; fallback for older runtimes\n : Buffer.from(padded, 'base64').toString('utf8');\n return JSON.parse(json);\n } catch {\n return null;\n }\n}\n\nexport function userFromJwt(token: string): SessionUser | null {\n const claims = decodeJwtPayload(token);\n if (!claims?.sub) return null;\n return {\n actorId: String(claims.sub),\n email: claims.email,\n displayName: claims.name || claims.email,\n tenantRole: claims.tenantRole ?? null,\n serviceRole: claims.serviceRole ?? null,\n platformRoles: Array.isArray(claims.platformRoles) ? claims.platformRoles : [],\n roles: Array.isArray(claims.roles) ? claims.roles : [],\n };\n}\n\n// ── Refresh decision table (verbatim from the app) ──────────────────────────\n//\n// Three outcomes so a caller can tell \"log out\" from \"transient blip\":\n// ok — a fresh JWT, adopt it.\n// auth_failed — 401/403 or a malformed 2xx: session genuinely broken.\n// transient — network / 5xx / other non-auth: keep the session, retry later.\n\ntype RefreshResult =\n | { ok: 'ok'; token: string }\n | { ok: 'auth_failed'; status: number; reason: string }\n | { ok: 'transient'; status?: number; reason: string };\n\n/**\n * Hit Core's POST /api/auth/refresh with the (possibly expired) token. The\n * endpoint accepts expired tokens within a grace period. Standalone so\n * `restore()` can call it before anything reads `getToken()`.\n */\nasync function attemptRefresh(expiredToken: string): Promise<RefreshResult> {\n let res: Response;\n try {\n res = await fetch(`${getCoreApiUrl()}/api/auth/refresh`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${expiredToken}`,\n 'Content-Type': 'application/json',\n },\n });\n } catch (e: any) {\n console.warn('[adas/session] refresh fetch threw (network):', e?.message || e);\n return { ok: 'transient', reason: `network: ${e?.message || 'unknown'}` };\n }\n if (res.status === 401 || res.status === 403) {\n console.warn(`[adas/session] refresh rejected with ${res.status} — auth genuinely broken`);\n return { ok: 'auth_failed', status: res.status, reason: `core ${res.status}` };\n }\n if (!res.ok) {\n console.warn(`[adas/session] refresh server error ${res.status} — treating as transient`);\n return { ok: 'transient', status: res.status, reason: `core ${res.status}` };\n }\n const body = await res.json().catch(() => null);\n if (typeof body?.token === 'string') return { ok: 'ok', token: body.token };\n console.warn('[adas/session] refresh returned 2xx but no token — treating as auth_failed');\n return { ok: 'auth_failed', status: res.status, reason: 'malformed refresh response' };\n}\n\nexport interface AuthSession {\n /** Current token (synchronous, in-memory) — the TokenProvider read path. */\n getToken(): string | null;\n getUser(): SessionUser | null;\n getState(): AuthSessionState;\n subscribe(cb: (s: AuthSessionState) => void): () => void;\n /** Startup: load the persisted token, project the user, and silently refresh\n * an expired JWT (ok → adopt, auth_failed → sign out, transient → keep). */\n restore(): Promise<void>;\n /** Adopt a freshly-minted token (host authenticated the user). */\n login(token: string): Promise<void>;\n /** Sign out: host teardown (with token still present) → clear token → state. */\n logout(): Promise<void>;\n /** The 401 refresh path. Adopts a new token on success; returns the API\n * client's outcome shape ('ok' → retry, 'auth_failed' → sign out). */\n refresh(): Promise<RefreshOutcome>;\n}\n\nexport function createAuthSession(deps: AuthSessionDeps): AuthSession {\n let state: AuthSessionState = { token: null, user: null, status: 'loading' };\n const subs = new Set<(s: AuthSessionState) => void>();\n const set = (patch: Partial<AuthSessionState>) => {\n state = { ...state, ...patch };\n for (const cb of subs) cb(state);\n };\n\n async function adopt(token: string): Promise<void> {\n await deps.persistence.saveToken(token);\n const user = userFromJwt(token);\n set({ token, user, status: 'authenticated' });\n await deps.onToken?.(token, user);\n }\n\n async function refresh(): Promise<RefreshOutcome> {\n const current = state.token;\n if (!current) return { ok: 'auth_failed', status: 0, reason: 'no token in store' };\n const outcome = await attemptRefresh(current);\n if (outcome.ok === 'ok') { await adopt(outcome.token); return { ok: 'ok', status: 200 }; }\n if (outcome.ok === 'auth_failed') return { ok: 'auth_failed', status: outcome.status, reason: outcome.reason };\n return { ok: 'transient', status: outcome.status ?? 0, reason: outcome.reason };\n }\n\n async function logout(): Promise<void> {\n // Host teardown runs FIRST, while the token still exists — push unregister\n // needs the JWT — then the SDK clears the token and flips to signed_out.\n await deps.onLogout?.();\n await deps.persistence.clearToken();\n set({ token: null, user: null, status: 'signed_out' });\n }\n\n return {\n getToken: () => state.token,\n getUser: () => state.user,\n getState: () => state,\n subscribe(cb) { subs.add(cb); return () => { subs.delete(cb); }; },\n\n async restore() {\n const token = await deps.persistence.loadToken();\n if (!token) { set({ status: 'signed_out' }); return; }\n const user = userFromJwt(token);\n if (!user) { await deps.persistence.clearToken(); set({ status: 'signed_out' }); return; }\n const claims = decodeJwtPayload(token);\n const exp = typeof claims?.exp === 'number' ? claims.exp * 1000 : 0;\n if (exp && Date.now() >= exp) {\n const outcome = await attemptRefresh(token);\n if (outcome.ok === 'ok') { await adopt(outcome.token); return; }\n if (outcome.ok === 'auth_failed') {\n await deps.persistence.clearToken();\n set({ token: null, user: null, status: 'signed_out' });\n return;\n }\n // transient — keep the stale token; apiFetch retries and next launch\n // refreshes again. Better than a re-login on a server blip.\n set({ token, user, status: 'authenticated' });\n return;\n }\n set({ token, user, status: 'authenticated' });\n },\n\n async login(token: string) {\n const user = userFromJwt(token);\n if (!user) throw new Error('Invalid JWT — could not decode subject');\n await adopt(token);\n },\n\n logout,\n refresh,\n };\n}\n","/**\n * SDK-owned push registration lifecycle (R7).\n *\n * R6 left the sign-out push unregister as a temporary APP hook (authStore's\n * teardown dynamically imported getCurrentPushToken + unregisterDevice). R7\n * finishes it with an SDK-owned service: the token→backend LIFECYCLE lives here\n * (register when a token is acquired, re-register when it refreshes, unregister\n * on sign-out, remember the current token), and the host supplies only the two\n * genuinely native/host pieces via an injected backend:\n * - registerDevice / unregisterDevice (the Core notif-api calls)\n * The host's native layer (FCM/APNs acquisition, permission prompts, token\n * refresh listeners) feeds tokens in via setToken(); it does not own the\n * backend lifecycle any more.\n *\n * No Expo/RN import here — the same seam works for any host.\n */\nexport interface PushBackend {\n /** Register a device push token with the backend (needs a live JWT). */\n registerDevice(token: string): Promise<void>;\n /** Unregister a device push token from the backend. */\n unregisterDevice(token: string): Promise<void>;\n}\n\nexport interface PushRegistration {\n /**\n * Adopt a freshly-acquired native push token and register it with the backend.\n * Idempotent — re-registering the SAME token is a no-op; a NEW token (refresh)\n * registers the new one. Pass null to forget without a backend call.\n * Best-effort: a backend failure is logged, never thrown (push is not\n * allowed to break sign-in).\n */\n setToken(token: string | null): Promise<void>;\n /** The current registered token, or null. */\n getToken(): string | null;\n /**\n * Sign-out: unregister the current token from the backend and forget it.\n * Fire-and-forget on the network call so a flaky connection can't BLOCK\n * sign-out (the ordering contract: this runs while the JWT still exists, then\n * the session clears the JWT). Returns once the token is forgotten locally.\n */\n unregister(): Promise<void>;\n}\n\nexport function createPushRegistration(backend: PushBackend): PushRegistration {\n let token: string | null = null;\n\n return {\n getToken: () => token,\n\n async setToken(next) {\n if (!next) { token = null; return; }\n if (next === token) return; // already registered this token\n token = next;\n try {\n await backend.registerDevice(next);\n } catch (e: any) {\n console.warn('[adas/push] registerDevice failed:', e?.message || e);\n }\n },\n\n async unregister() {\n const t = token;\n token = null; // forget locally immediately — a slow network must not block sign-out\n if (t) {\n backend.unregisterDevice(t).catch((e: any) =>\n console.warn('[adas/push] unregisterDevice failed:', e?.message || e),\n );\n }\n },\n };\n}\n","/**\n * Tracks the user's ACS widget tiles for the home carousel. Single source of\n * truth for /api/acs/surface state. Optimistic ops with rollback on failure\n * — backend contract treats 404 / already:true as success (per spec), so the\n * store mirrors that policy: a delete that the server says was already done\n * stays removed locally.\n *\n * Cross-device freshness: the WS subscription (./ws) forwards `acs.artifacts`\n * events into the applyServer* methods; hosts should also refresh() on app\n * foreground (\"WS is best-effort; the GET is the source of truth\").\n */\n\nimport { create } from 'zustand';\nimport {\n listSurface,\n deleteArtifact,\n patchArtifact,\n setOrder,\n recordEvent,\n type AcsArtifact,\n type AcsDeleteReason,\n} from './api';\n\ninterface WidgetsState {\n /** Tiles to render in the home carousel. Order matches what /surface\n * returned; sort by pinOrder when present. */\n tiles: AcsArtifact[];\n /** True after the first /surface fetch completes (success OR empty). */\n hydrated: boolean;\n /** True while a /surface request is in flight. */\n loading: boolean;\n /** Last error string from /surface; null on success or pre-fetch. */\n error: string | null;\n\n /** First load — idempotent; safe to call from multiple effects. */\n hydrate: () => Promise<void>;\n /** Force re-fetch; call on app foreground and after destructive ops. */\n refresh: () => Promise<void>;\n /** Reset to empty (sign-out, env switch). */\n clear: () => void;\n\n /**\n * Optimistic remove. Returns { ok: false, error } only on real network /\n * 5xx failures — 404 and already:true rollback to ok:true per the backend\n * contract (no existence leak; the row is gone either way).\n */\n remove: (artifactId: string, reason?: AcsDeleteReason) => Promise<{ ok: boolean; error?: string }>;\n\n /** Optimistic hide (visibility → 'hidden'). Tile disappears from carousel. */\n hide: (artifactId: string) => Promise<{ ok: boolean; error?: string }>;\n\n /** Optimistic unhide (visibility → 'pinned'). Tile reappears in carousel. */\n unhide: (artifactId: string) => Promise<{ ok: boolean; error?: string }>;\n\n /** Optimistic rename. Trims input and rejects empty / overlong names locally. */\n rename: (artifactId: string, name: string) => Promise<{ ok: boolean; error?: string }>;\n\n /**\n * Persist a new pin order to the server. `pinnedIds` is the visible order\n * of currently-pinned tiles. Optimistic — applies locally first, rolls\n * back on server rejection.\n */\n reorder: (pinnedIds: string[]) => Promise<{ ok: boolean; error?: string }>;\n\n // ─── Server-driven mutations (no API call) ─────────────────────────────\n // The WS subscription (./ws) forwards `acs.artifacts` events into these\n // methods. They ONLY mutate local state — they DO NOT call the API (the\n // server is the source of the event, calling back would loop).\n\n /** WS 'updated' event — replace the tile by id (hide/unhide/rename land here). */\n applyServerUpdate: (artifact: AcsArtifact) => void;\n /** WS 'retired' event — remove the tile by id. */\n applyServerRetire: (artifactId: string) => void;\n /** WS 'order' event — re-rank tiles to match the pinned ID array. */\n applyServerOrder: (pinnedIds: string[]) => void;\n\n /** Fire-and-forget usage event. Caller doesn't await. */\n track: (artifactId: string, event: 'clicked' | 'dismissed' | 'acted' | 'ignored') => void;\n}\n\nexport const useWidgetsStore = create<WidgetsState>((set, get) => ({\n tiles: [],\n hydrated: false,\n loading: false,\n error: null,\n\n hydrate: async () => {\n if (get().hydrated || get().loading) return;\n await get().refresh();\n },\n\n refresh: async () => {\n if (get().loading) return;\n set({ loading: true, error: null });\n try {\n const tiles = await listSurface('home');\n set({ tiles, loading: false, hydrated: true });\n } catch (e: any) {\n set({ loading: false, hydrated: true, error: e?.message || 'Failed to load widgets' });\n }\n },\n\n clear: () => set({ tiles: [], hydrated: false, loading: false, error: null }),\n\n remove: async (artifactId, reason = 'user_long_press_delete') => {\n const before = get().tiles;\n // Optimistic: snap removed immediately so the user sees the tile vanish.\n set({ tiles: before.filter(t => t.id !== artifactId) });\n\n const result = await deleteArtifact(artifactId, reason);\n if (result.ok) {\n // Includes 200, 200 already:true, and 404 — all \"gone server-side too\".\n return { ok: true };\n }\n\n // Real failure — rollback.\n set({ tiles: before });\n return { ok: false, error: result.error || `HTTP ${result.status}` };\n },\n\n hide: async (artifactId) => {\n const before = get().tiles;\n set({\n tiles: before.map(t =>\n t.id === artifactId ? { ...t, visibility: 'hidden' as const } : t,\n ),\n });\n const r = await patchArtifact(artifactId, { visibility: 'hidden' }, 'user_long_press_hide');\n if (r.ok) {\n // If the server returned the updated artifact, sync our copy to it\n // (covers race conditions and server-side normalisation).\n if (r.artifact) {\n set({\n tiles: get().tiles.map(t => (t.id === artifactId ? { ...t, ...r.artifact! } : t)),\n });\n }\n return { ok: true };\n }\n set({ tiles: before });\n return { ok: false, error: r.error || `HTTP ${r.status}` };\n },\n\n unhide: async (artifactId) => {\n const before = get().tiles;\n set({\n tiles: before.map(t =>\n t.id === artifactId ? { ...t, visibility: 'pinned' as const } : t,\n ),\n });\n const r = await patchArtifact(artifactId, { visibility: 'pinned' }, 'user_long_press_unhide');\n if (r.ok) {\n if (r.artifact) {\n set({\n tiles: get().tiles.map(t => (t.id === artifactId ? { ...t, ...r.artifact! } : t)),\n });\n }\n return { ok: true };\n }\n set({ tiles: before });\n return { ok: false, error: r.error || `HTTP ${r.status}` };\n },\n\n rename: async (artifactId, name) => {\n // Local validation mirrors backend's 60-char cap from the spec. Keep\n // this strict so we don't hit the API with garbage that will 400 anyway.\n const trimmed = name.trim();\n if (!trimmed) return { ok: false, error: 'Name cannot be empty' };\n if (trimmed.length > 60) return { ok: false, error: 'Name must be 60 characters or less' };\n\n const before = get().tiles;\n set({\n tiles: before.map(t => (t.id === artifactId ? { ...t, name: trimmed } : t)),\n });\n const r = await patchArtifact(artifactId, { name: trimmed }, 'user_long_press_rename');\n if (r.ok) {\n if (r.artifact) {\n set({\n tiles: get().tiles.map(t => (t.id === artifactId ? { ...t, ...r.artifact! } : t)),\n });\n }\n return { ok: true };\n }\n set({ tiles: before });\n return { ok: false, error: r.error || `HTTP ${r.status}` };\n },\n\n reorder: async (pinnedIds) => {\n const before = get().tiles;\n // Optimistic: assign pinOrder by position in pinnedIds. Hidden tiles\n // are not touched. If the server rejects, we roll back to `before`.\n const idToOrder = new Map(pinnedIds.map((id, i) => [id, i] as const));\n set({\n tiles: before.map(t => {\n const o = idToOrder.get(t.id);\n return o == null ? t : { ...t, pinOrder: o };\n }),\n });\n const r = await setOrder(pinnedIds);\n if (r.ok) return { ok: true };\n set({ tiles: before });\n return { ok: false, error: r.error || `HTTP ${r.status}` };\n },\n\n applyServerUpdate: (artifact) => {\n set({\n tiles: get().tiles.map(t => (t.id === artifact.id ? { ...t, ...artifact } : t)),\n });\n },\n\n applyServerRetire: (artifactId) => {\n set({ tiles: get().tiles.filter(t => t.id !== artifactId) });\n },\n\n applyServerOrder: (pinnedIds) => {\n const idToOrder = new Map(pinnedIds.map((id, i) => [id, i] as const));\n set({\n tiles: get().tiles.map(t => {\n const o = idToOrder.get(t.id);\n return o == null ? t : { ...t, pinOrder: o };\n }),\n });\n },\n\n track: (artifactId, event) => {\n // Fire-and-forget. Surface tagged so backend analytics knows where the\n // click happened (home carousel vs library, etc.).\n recordEvent(artifactId, event, 'home');\n },\n}));\n","/**\n * ACS host DI seams. Both optional — the ACS layer works without them:\n * no reachability reporting, and the home-actions cache lives in memory only.\n */\n\n/** Host hook fired on every ACS fetch outcome — a response (any status)\n * proves the server is reachable; a thrown fetch means it isn't. The\n * reference app wires this to its connectivity banner store. */\nexport interface AcsReachabilityReporter {\n up: () => void;\n down: () => void;\n}\n\nlet _reachability: AcsReachabilityReporter | null = null;\n\nexport function setAcsReachabilityReporter(r: AcsReachabilityReporter | null): void {\n _reachability = r;\n}\n\nexport function reportAcsServerUp(): void {\n try { _reachability?.up(); } catch {}\n}\n\nexport function reportAcsServerDown(): void {\n try { _reachability?.down(); } catch {}\n}\n\n/** Minimal async KV used to persist the home-actions feed across cold starts\n * (AsyncStorage-shaped). @ateam-ai-mobile/expo wires AsyncStorage in\n * setupExpoProviders(); without a provider the cache is a no-op. */\nexport interface AcsKvStorage {\n getItem(key: string): Promise<string | null>;\n setItem(key: string, value: string): Promise<void>;\n}\n\nlet _kv: AcsKvStorage | null = null;\n\nexport function setAcsCacheStorage(kv: AcsKvStorage | null): void {\n _kv = kv;\n}\n\nexport function getAcsCacheStorage(): AcsKvStorage | null {\n return _kv;\n}\n","/**\n * Client for Core's /api/acs/* proxy → ACS service. Powers the \"widgets\"\n * surface on home: AI-generated artifacts that render as carousel tiles and\n * full-screen interactive panels via the bundleUrl — plus the home\n * \"top actions\" feed.\n *\n * Endpoints:\n * POST /api/acs/surface — list active artifacts for a surface\n * POST /api/acs/event — usage tracking (clicked/dismissed/etc.)\n * GET /api/acs/made-for-you — full library, all lifecycle stages\n * DELETE /api/acs/artifacts/{id} — user-initiated retire (idempotent)\n * PATCH /api/acs/artifacts/{id} — visibility + rename\n * PUT /api/acs/order — drag-reorder\n * GET /api/acs/home-actions — top-actions feed (+ PATCH/PUT order)\n * WS topics acs.artifacts / acs.home-actions — cross-device sync (see ./ws)\n */\n\nimport { getCoreApiUrl, getToken } from '../config';\nimport { reportAcsServerUp, reportAcsServerDown } from './providers';\n\nexport interface AcsArtifact {\n id: string;\n name: string;\n description?: string;\n iconHint?: string;\n manifest?: Record<string, any>;\n bundleUrl: string;\n version: number;\n provenance?: Record<string, any>;\n\n // Tile-shape fields (consumers branch on these when present; safe defaults\n // when absent).\n kind?: 'user' | 'featured';\n visibility?: 'pinned' | 'hidden';\n pinOrder?: number;\n canDelete?: boolean;\n canRename?: boolean;\n canHide?: boolean;\n previewMode?: 'live' | 'snapshot' | 'none';\n previewUrl?: string;\n}\n\n// Closed-ish enum — accept arbitrary strings so the backend can introduce new\n// surface names without a client release blocking it.\nexport type AcsSurface = 'home' | 'library' | (string & {});\n\n// Closed enum on the backend side.\nexport type AcsEvent = 'clicked' | 'dismissed' | 'acted' | 'ignored';\n\nexport type AcsDeleteReason =\n | 'user_long_press_delete'\n | 'user_swipe_delete'\n | 'user_bulk_delete'\n | (string & {});\n\nexport type AcsPatchReason =\n | 'user_long_press_hide'\n | 'user_long_press_unhide'\n | 'user_long_press_rename'\n | (string & {});\n\nasync function authedFetch(path: string, init?: RequestInit): Promise<Response> {\n const token = getToken();\n try {\n const res = await fetch(`${getCoreApiUrl()}${path}`, {\n ...init,\n headers: {\n 'Content-Type': 'application/json',\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...(init?.headers || {}),\n },\n });\n // Any response (even 4xx/5xx) proves the server is reachable.\n reportAcsServerUp();\n return res;\n } catch (e) {\n // Thrown = network error / timeout / DNS / TLS → server unreachable.\n reportAcsServerDown();\n throw e;\n }\n}\n\n/**\n * List artifacts surfaced for the given surface name. Returns [] on error\n * (network, auth, or empty result) — callers shouldn't have to distinguish.\n * Use surface=\"home\" for the carousel.\n */\nexport async function listSurface(\n surface: AcsSurface,\n context?: Record<string, any>,\n): Promise<AcsArtifact[]> {\n try {\n const res = await authedFetch('/api/acs/surface', {\n method: 'POST',\n body: JSON.stringify({ surface, ...(context ? { context } : {}) }),\n });\n if (!res.ok) {\n console.warn(`[acs] /surface ${surface} returned ${res.status}`);\n return [];\n }\n const data = await res.json();\n return Array.isArray(data?.items) ? (data.items as AcsArtifact[]) : [];\n } catch (e: any) {\n console.warn('[acs] /surface failed:', e?.message || e);\n return [];\n }\n}\n\n/**\n * Full library — every artifact across all lifecycle stages. Used by a\n * Library screen's Yours + Hidden sections.\n */\nexport async function listMadeForYou(): Promise<AcsArtifact[]> {\n try {\n const res = await authedFetch('/api/acs/made-for-you', { method: 'GET' });\n if (!res.ok) {\n console.warn(`[acs] /made-for-you returned ${res.status}`);\n return [];\n }\n const data = await res.json();\n return Array.isArray(data?.artifacts) ? (data.artifacts as AcsArtifact[]) : [];\n } catch (e: any) {\n console.warn('[acs] /made-for-you failed:', e?.message || e);\n return [];\n }\n}\n\nexport interface DeleteResult {\n ok: boolean;\n /** True when the server already had this artifact retired. Treat as success. */\n already?: boolean;\n /** True for HTTP 404 — artifact gone or never owned by caller. Treat as success\n * (remove from local state) per the backend's contract: no existence leak. */\n notFound?: boolean;\n /** HTTP status returned by the server. */\n status: number;\n error?: string;\n}\n\n/**\n * Retire a user widget. Per backend contract:\n * 200 ok → success\n * 200 ok + already:true → idempotent, treat as success\n * 404 → already gone or not owned, treat as success\n * 5xx / network → real failure, caller should rollback optimistic remove\n */\nexport async function deleteArtifact(\n artifactId: string,\n reason: AcsDeleteReason = 'user_long_press_delete',\n): Promise<DeleteResult> {\n try {\n const res = await authedFetch(\n `/api/acs/artifacts/${encodeURIComponent(artifactId)}`,\n { method: 'DELETE', body: JSON.stringify({ reason }) },\n );\n const data = await res.json().catch(() => ({} as any));\n if (res.ok) {\n return { ok: true, already: !!data?.already, status: res.status };\n }\n if (res.status === 404) {\n return { ok: true, notFound: true, status: 404 };\n }\n return { ok: false, status: res.status, error: data?.error || `HTTP ${res.status}` };\n } catch (e: any) {\n return { ok: false, status: 0, error: e?.message || 'network error' };\n }\n}\n\nexport interface PatchResult {\n ok: boolean;\n /** Updated tile when the server returns the new state. */\n artifact?: AcsArtifact;\n already?: boolean;\n notFound?: boolean;\n status: number;\n error?: string;\n}\n\n/**\n * Update an artifact's visibility (hide/unhide) and/or name (rename). One\n * call covers both because backend offers a single PATCH endpoint with\n * optional fields. At least one of visibility/name must be provided.\n *\n * Per backend contract (mirrors DELETE semantics):\n * 200 ok → success\n * 200 ok + already:true → idempotent no-op, treat as success\n * 404 → not found / not owned, treat as success\n * 400 / 403 → real failure, surface to user\n * 5xx / network → real failure, caller should rollback\n */\nexport async function patchArtifact(\n artifactId: string,\n body: { visibility?: 'pinned' | 'hidden'; name?: string },\n reason: AcsPatchReason,\n): Promise<PatchResult> {\n try {\n const res = await authedFetch(\n `/api/acs/artifacts/${encodeURIComponent(artifactId)}`,\n { method: 'PATCH', body: JSON.stringify({ ...body, reason }) },\n );\n const data = await res.json().catch(() => ({} as any));\n if (res.ok) {\n return {\n ok: true,\n already: !!data?.already,\n artifact: data?.artifact || undefined,\n status: res.status,\n };\n }\n if (res.status === 404) {\n return { ok: true, notFound: true, status: 404 };\n }\n return {\n ok: false,\n status: res.status,\n error: data?.error || `HTTP ${res.status}`,\n };\n } catch (e: any) {\n return { ok: false, status: 0, error: e?.message || 'network error' };\n }\n}\n\n/**\n * Persist a new pin order. Body lists currently-pinned artifact IDs in the\n * new visual order; server validates ownership + pinned state of each ID.\n *\n * Returns { ok: false, error } on failure. 400 means the array contained\n * an id that's not owned or not pinned — caller should refetch /surface\n * and re-render rather than fight the server.\n */\nexport async function setOrder(pinned: string[]): Promise<{ ok: boolean; status: number; error?: string }> {\n try {\n const res = await authedFetch('/api/acs/order', {\n method: 'PUT',\n body: JSON.stringify({ pinned }),\n });\n if (res.ok) return { ok: true, status: res.status };\n const data = await res.json().catch(() => ({} as any));\n return { ok: false, status: res.status, error: data?.error || `HTTP ${res.status}` };\n } catch (e: any) {\n return { ok: false, status: 0, error: e?.message || 'network error' };\n }\n}\n\n/**\n * Record a usage event. Fire-and-forget — never throws, never blocks the UI.\n * Use surface to tell the backend which surface the event came from\n * (e.g. 'home' for the carousel).\n */\nexport async function recordEvent(\n artifactId: string,\n event: AcsEvent,\n surface?: AcsSurface,\n): Promise<void> {\n try {\n await authedFetch('/api/acs/event', {\n method: 'POST',\n body: JSON.stringify({ artifactId, event, ...(surface ? { surface } : {}) }),\n });\n } catch {\n // intentional swallow — usage tracking failures shouldn't surface to the user\n }\n}\n\n// ─── Home Actions (top-actions feed) ────────────────────────────────────\n// Server-managed recommended-actions feed for the home \"What's on your mind?\"\n// screen. The card universe is open + agent-authored; the only fixed contract\n// is this wire shape + the leaf dispatch (send_message | open_widget).\n// Backend: GET /api/acs/home-actions, PUT /order, PATCH /:id,\n// WS topic acs.home-actions.\n\nexport type HomeActionLeaf =\n | { type: 'send_message'; prompt: string; skillSlug?: string }\n | { type: 'open_widget'; artifactId: string }\n | { type: string; [k: string]: any }; // forward-compat: open_url/run_tool/…\n\nexport interface HomeAction {\n actionId: string;\n pinOrder?: number;\n title: string;\n subtitle?: string;\n icon?: string;\n action: HomeActionLeaf;\n source?: 'curated' | 'pinned_by_user' | 'skill_authored' | (string & {});\n canDismiss?: boolean;\n canPin?: boolean;\n pinned?: boolean;\n}\n\n/** List the home top-actions feed. Returns [] on any error. */\nexport async function listHomeActions(): Promise<HomeAction[]> {\n try {\n const res = await authedFetch('/api/acs/home-actions', { method: 'GET' });\n if (!res.ok) {\n console.warn(`[acs] /home-actions returned ${res.status}`);\n return [];\n }\n const data = await res.json();\n return Array.isArray(data?.actions) ? (data.actions as HomeAction[]) : [];\n } catch (e: any) {\n console.warn('[acs] /home-actions failed:', e?.message || e);\n return [];\n }\n}\n\nexport type HomeActionReason =\n | 'user_pin' | 'user_unpin' | 'user_dismiss' | 'user_drag_reorder'\n | (string & {});\n\n/** Pin/unpin or dismiss a card. 200 already / 404 both count as success. */\nexport async function patchHomeAction(\n actionId: string,\n body: { pinned?: boolean; dismissed?: boolean },\n reason: HomeActionReason,\n): Promise<{ ok: boolean; action?: HomeAction; status: number; error?: string }> {\n try {\n const res = await authedFetch(\n `/api/acs/home-actions/${encodeURIComponent(actionId)}`,\n { method: 'PATCH', body: JSON.stringify({ ...body, reason }) },\n );\n const data = await res.json().catch(() => ({} as any));\n if (res.ok) return { ok: true, action: data?.action || undefined, status: res.status };\n if (res.status === 404) return { ok: true, status: 404 };\n return { ok: false, status: res.status, error: data?.error || `HTTP ${res.status}` };\n } catch (e: any) {\n return { ok: false, status: 0, error: e?.message || 'network error' };\n }\n}\n\n/** Persist a new card order. Optimistic; caller rolls back on failure. */\nexport async function setHomeActionOrder(\n order: string[],\n): Promise<{ ok: boolean; status: number; error?: string }> {\n try {\n const res = await authedFetch('/api/acs/home-actions/order', {\n method: 'PUT',\n body: JSON.stringify({ order }),\n });\n if (res.ok) return { ok: true, status: res.status };\n const data = await res.json().catch(() => ({} as any));\n return { ok: false, status: res.status, error: data?.error || `HTTP ${res.status}` };\n } catch (e: any) {\n return { ok: false, status: 0, error: e?.message || 'network error' };\n }\n}\n","/**\n * The home \"What's on your mind?\" top-actions feed. Server-managed: the list,\n * its order, pin and dismiss state all live on the backend (ACS home-actions\n * API). This store is a thin local mirror with optimistic mutations + rollback\n * — same discipline as widgetsStore.\n *\n * The cards themselves are agent-authored and open-ended; clients only know\n * the wire shape + the fixed leaf dispatch (send_message | open_widget), so\n * this store stays dumb on purpose.\n */\n\nimport { create } from 'zustand';\nimport {\n listHomeActions,\n patchHomeAction,\n setHomeActionOrder,\n type HomeAction,\n} from './api';\nimport { getAcsCacheStorage } from './providers';\n\n// Persisted (when the host wires setAcsCacheStorage) so the home cards render\n// instantly on cold start and survive a dead/empty server response. The\n// server is still source of truth — when a non-empty result arrives we\n// overwrite the cache; an empty/failed fetch leaves the last good cards on\n// screen.\nconst CACHE_KEY = 'ateam.homeActions.v1';\n\nfunction persist(actions: HomeAction[]): void {\n // Fire-and-forget; never block the UI on disk.\n getAcsCacheStorage()?.setItem(CACHE_KEY, JSON.stringify(actions)).catch(() => {});\n}\n\ninterface HomeActionsState {\n actions: HomeAction[];\n hydrated: boolean;\n loading: boolean;\n\n hydrate: () => Promise<void>;\n refresh: () => Promise<void>;\n clear: () => void;\n\n /** Optimistic dismiss — removes locally, rolls back on real failure. */\n dismiss: (actionId: string) => Promise<{ ok: boolean; error?: string }>;\n /** Optimistic pin/unpin. */\n setPinned: (actionId: string, pinned: boolean) => Promise<{ ok: boolean; error?: string }>;\n /** Optimistic reorder — `orderedIds` is the new visual order. */\n reorder: (orderedIds: string[]) => Promise<{ ok: boolean; error?: string }>;\n\n // ─── WS-driven (local mutation only, no API call) ───\n applyServerUpsert: (action: HomeAction) => void;\n applyServerRemove: (actionId: string) => void;\n applyServerOrder: (orderedIds: string[]) => void;\n}\n\nfunction sortByPin(a: HomeAction, b: HomeAction): number {\n // pinned first, then pinOrder asc, then stable.\n if (!!a.pinned !== !!b.pinned) return a.pinned ? -1 : 1;\n const ao = typeof a.pinOrder === 'number' ? a.pinOrder : Number.MAX_SAFE_INTEGER;\n const bo = typeof b.pinOrder === 'number' ? b.pinOrder : Number.MAX_SAFE_INTEGER;\n return ao - bo;\n}\n\nexport const useHomeActionsStore = create<HomeActionsState>((set, get) => ({\n actions: [],\n hydrated: false,\n loading: false,\n\n hydrate: async () => {\n if (get().hydrated || get().loading) return;\n // 1. Paint cached cards immediately (survives cold start / offline).\n try {\n const cached = await getAcsCacheStorage()?.getItem(CACHE_KEY);\n if (cached) {\n const parsed = JSON.parse(cached) as HomeAction[];\n if (Array.isArray(parsed) && parsed.length > 0) {\n // Preserve the order we cached (which was the server's priority\n // order) — home layouts may split positionally, so re-sorting\n // would reshuffle the hero cards.\n set({ actions: parsed, hydrated: true });\n }\n }\n } catch { /* corrupt cache — ignore, server refresh will fix it */ }\n // 2. Then refresh from the server.\n await get().refresh();\n },\n\n refresh: async () => {\n if (get().loading) return;\n set({ loading: true });\n const actions = await listHomeActions();\n // Only overwrite when the server actually returned cards. An empty result\n // (dead API, transient 5xx, or genuinely-empty) must NOT wipe the cards\n // already on screen — keep the last good set persisted.\n if (actions.length > 0) {\n // Server delivers cards already priority-ordered — do NOT re-sort\n // (home layouts may be positional, e.g. a top-3 / rest split).\n set({ actions, loading: false, hydrated: true });\n persist(actions);\n } else {\n set({ loading: false, hydrated: true });\n }\n },\n\n // clear() wipes in-memory state on sign-out AND drops the disk cache — a\n // different user must not see the prior user's cards.\n clear: () => {\n set({ actions: [], hydrated: false, loading: false });\n getAcsCacheStorage()?.setItem(CACHE_KEY, '[]').catch(() => {});\n },\n\n dismiss: async (actionId) => {\n const before = get().actions;\n const next = before.filter(a => a.actionId !== actionId);\n set({ actions: next });\n persist(next);\n const r = await patchHomeAction(actionId, { dismissed: true }, 'user_dismiss');\n if (r.ok) return { ok: true };\n set({ actions: before });\n persist(before);\n return { ok: false, error: r.error || `HTTP ${r.status}` };\n },\n\n setPinned: async (actionId, pinned) => {\n const before = get().actions;\n const next = before.map(a => (a.actionId === actionId ? { ...a, pinned } : a)).sort(sortByPin);\n set({ actions: next });\n persist(next);\n const r = await patchHomeAction(actionId, { pinned }, pinned ? 'user_pin' : 'user_unpin');\n if (r.ok) {\n if (r.action) {\n const merged = get().actions.map(a => (a.actionId === actionId ? { ...a, ...r.action! } : a)).sort(sortByPin);\n set({ actions: merged });\n persist(merged);\n }\n return { ok: true };\n }\n set({ actions: before });\n persist(before);\n return { ok: false, error: r.error || `HTTP ${r.status}` };\n },\n\n reorder: async (orderedIds) => {\n const before = get().actions;\n const idx = new Map(orderedIds.map((id, i) => [id, i] as const));\n const next = before.map(a => {\n const o = idx.get(a.actionId);\n return o == null ? a : { ...a, pinOrder: o };\n }).sort(sortByPin);\n set({ actions: next });\n persist(next);\n const r = await setHomeActionOrder(orderedIds);\n if (r.ok) return { ok: true };\n set({ actions: before });\n persist(before);\n return { ok: false, error: r.error || `HTTP ${r.status}` };\n },\n\n applyServerUpsert: (action) => {\n const rest = get().actions.filter(a => a.actionId !== action.actionId);\n const next = [...rest, action].sort(sortByPin);\n set({ actions: next });\n persist(next);\n },\n applyServerRemove: (actionId) => {\n const next = get().actions.filter(a => a.actionId !== actionId);\n set({ actions: next });\n persist(next);\n },\n applyServerOrder: (orderedIds) => {\n const idx = new Map(orderedIds.map((id, i) => [id, i] as const));\n const next = get().actions.map(a => {\n const o = idx.get(a.actionId);\n return o == null ? a : { ...a, pinOrder: o };\n }).sort(sortByPin);\n set({ actions: next });\n persist(next);\n },\n}));\n","/**\n * Long-lived WebSocket subscription to the `acs.artifacts` and\n * `acs.home-actions` topics. Protocol (backend websocket.js):\n *\n * client → server : { type: \"subscribe\", topic: \"acs.artifacts\" }\n * server → client : { event: \"subscribed\", data: { topic: \"acs.artifacts\" } }\n * server → client : { topic: \"acs.artifacts\", event: \"updated\", artifact: {...} }\n * server → client : { topic: \"acs.artifacts\", event: \"retired\", artifactId: \"...\" }\n * server → client : { topic: \"acs.artifacts\", event: \"order\", pinned: [...] }\n *\n * Events flow into the stores' applyServer* methods so we don't loop (those\n * methods mutate local state only — no API calls).\n *\n * Lifecycle:\n * connectAcsWs(token, tenant) starts a single connection + subscribe.\n * On disconnect, exponential-backoff reconnect (capped). Call\n * notifyAcsWsForeground() from the host's AppState listener to\n * force-reconnect when the socket dropped while backgrounded (this module\n * is host-agnostic — no react-native import — so the host owns AppState).\n * On signout, disconnectAcsWs() closes the socket and stops reconnects.\n *\n * Backend's spec note: \"WS is best-effort; the GET is the source of truth\"\n * — so widgetsStore.refresh() on foreground stays the safety net even when\n * the WS is healthy. This file is the realtime cherry on top.\n */\n\nimport { getCoreWsUrl } from '../config';\nimport { useWidgetsStore } from './widgetsStore';\nimport { useHomeActionsStore } from './homeActionsStore';\n\nlet _ws: WebSocket | null = null;\nlet _wantOpen = false;\nlet _retryAttempt = 0;\nlet _retryTimer: ReturnType<typeof setTimeout> | null = null;\nlet _token: string | null = null;\nlet _tenant: string | null = null;\n\nconst TOPIC = 'acs.artifacts';\nconst TOPIC_HOME = 'acs.home-actions';\nconst MAX_BACKOFF_MS = 30_000;\n\n// Application close codes the SERVER means as PERMANENT — reconnecting with the\n// same credentials will just be refused again, so we stop the retry loop instead\n// of hammering the socket every 30s forever (silently, on the user's mobile\n// data). We do NOT interpret what the code MEANS for the UI — that is the\n// server's job via GET /api/session; here we only decide \"retrying is pointless.\"\n// Extend as the backend documents more terminal codes.\n// 4003 = invalid/inactive tenant (e.g. an expired trial workspace).\nconst TERMINAL_CLOSE_CODES = new Set<number>([4003]);\n\n// Set when a terminal close arrives; suppresses BOTH the backoff loop and the\n// foreground re-poke until an explicit connectAcsWs() (new creds / reactivation).\nlet _terminated = false;\n\nfunction backoffMs(attempt: number): number {\n // 0.5s, 1s, 2s, 4s, ... capped at 30s. Tiny jitter to avoid thundering herd.\n const base = Math.min(MAX_BACKOFF_MS, 500 * Math.pow(2, attempt));\n const jitter = Math.random() * 250;\n return base + jitter;\n}\n\nfunction send(obj: any) {\n if (_ws && _ws.readyState === 1) {\n try { _ws.send(JSON.stringify(obj)); } catch (e) { console.warn('[acsWs] send failed', e); }\n }\n}\n\nfunction handleEvent(msg: any) {\n // The subscribe confirmation comes as { event: \"subscribed\", data: {...} }\n // (no `topic` field). Real events come as { topic, event, ...payload }.\n if (msg?.event === 'subscribed') {\n console.log('[acsWs] subscribed to', msg?.data?.topic);\n return;\n }\n\n // Home-actions feed events route to its own store.\n if (msg?.topic === TOPIC_HOME) {\n const home = useHomeActionsStore.getState();\n switch (msg.event) {\n case 'updated':\n if (msg.action && typeof msg.action.actionId === 'string') home.applyServerUpsert(msg.action);\n break;\n case 'removed':\n if (typeof msg.actionId === 'string') home.applyServerRemove(msg.actionId);\n break;\n case 'order':\n if (Array.isArray(msg.order)) home.applyServerOrder(msg.order.filter((id: any) => typeof id === 'string'));\n break;\n case 'refreshed':\n home.refresh();\n break;\n default:\n break;\n }\n return;\n }\n\n if (msg?.topic !== TOPIC) return;\n\n const store = useWidgetsStore.getState();\n switch (msg.event) {\n case 'updated':\n if (msg.artifact && typeof msg.artifact.id === 'string') {\n store.applyServerUpdate(msg.artifact);\n }\n break;\n case 'retired':\n if (typeof msg.artifactId === 'string') {\n store.applyServerRetire(msg.artifactId);\n }\n break;\n case 'order':\n if (Array.isArray(msg.pinned)) {\n store.applyServerOrder(msg.pinned.filter((id: any) => typeof id === 'string'));\n }\n break;\n case 'created':\n // Backend spec lists `created` as TODO. Until it ships, host foreground\n // refresh catches new widgets. When it lands the WS will also fire\n // `updated` for the new tile, which is already handled. For now, treat\n // any inbound created the same as updated.\n if (msg.artifact && typeof msg.artifact.id === 'string') {\n store.applyServerUpdate(msg.artifact);\n }\n break;\n default:\n // Unknown event — ignore so we stay forward-compatible with new types.\n break;\n }\n}\n\nfunction openSocket() {\n if (_ws) return;\n if (!_token || !_tenant) return;\n\n const url = `${getCoreWsUrl()}/ws?token=${encodeURIComponent(_token)}&tenant=${encodeURIComponent(_tenant)}`;\n console.log(`[acsWs] opening`);\n const ws = new WebSocket(url);\n _ws = ws;\n\n // IDENTITY GUARD. `connectAcsWs()` (tenant switch / re-auth) and reconnects\n // replace `_ws` while an OLD socket may still be mid-teardown. A late callback\n // from that old socket must NOT touch the singleton state of the CURRENT one:\n // its `onclose` could null out the live `_ws`, schedule a spurious reconnect,\n // or poison `_terminated` for a connection that is perfectly healthy. Every\n // callback proves it is still the current socket before doing anything — the\n // captured `ws` vs the live `_ws`. A superseded socket's events are inert.\n const isCurrent = () => ws === _ws;\n\n ws.onopen = () => {\n if (!isCurrent()) return;\n _retryAttempt = 0;\n send({ type: 'subscribe', topic: TOPIC });\n send({ type: 'subscribe', topic: TOPIC_HOME });\n };\n ws.onmessage = (e) => {\n if (!isCurrent()) return;\n try {\n const msg = JSON.parse(typeof e.data === 'string' ? e.data : '');\n handleEvent(msg);\n } catch (err) {\n // Non-JSON or malformed frame — ignore, log for debugging.\n console.warn('[acsWs] parse failed', err);\n }\n };\n ws.onerror = (e: any) => {\n if (!isCurrent()) return;\n console.warn('[acsWs] error', e?.message || e);\n };\n ws.onclose = (ev: any) => {\n // A superseded socket closing is expected and inert — do not clear the live\n // `_ws`, schedule a retry, or set `_terminated`. Only the CURRENT socket's\n // close drives the reconnect/terminate state machine.\n if (!isCurrent()) return;\n _ws = null;\n if (!_wantOpen) return;\n const code = typeof ev?.code === 'number' ? ev.code : 0;\n if (TERMINAL_CLOSE_CODES.has(code)) {\n // Permanent refusal — stop. Only an explicit connectAcsWs() (re-auth or\n // tenant change) clears this; the automatic loop and foreground re-poke\n // both stay off so we never loop against a socket that will keep refusing.\n _terminated = true;\n if (_retryTimer) { clearTimeout(_retryTimer); _retryTimer = null; }\n console.warn(`[acsWs] server refused permanently (close ${code}); stopping reconnects until re-auth/tenant change`);\n return;\n }\n const delay = backoffMs(_retryAttempt++);\n console.log(`[acsWs] closed (${code}); reconnecting in ${Math.round(delay)}ms (attempt ${_retryAttempt})`);\n if (_retryTimer) clearTimeout(_retryTimer);\n _retryTimer = setTimeout(openSocket, delay);\n };\n}\n\n/**\n * Call from the host's AppState listener when the app returns to the\n * foreground — if the socket dropped while backgrounded, reconnect\n * immediately rather than waiting out the backoff timer.\n */\nexport function notifyAcsWsForeground() {\n if (_terminated) return; // permanent refusal — don't re-poke a socket that keeps refusing\n if (_wantOpen && !_ws) {\n if (_retryTimer) { clearTimeout(_retryTimer); _retryTimer = null; }\n _retryAttempt = 0;\n openSocket();\n }\n}\n\n/**\n * Start the ACS WS subscription. Idempotent — calling twice with the same\n * token/tenant is a no-op. Call once the host says the user is signed in\n * (JWT available).\n */\nexport function connectAcsWs(token: string, tenant: string) {\n // Explicit connect (sign-in, tenant switch, reactivation) always gets a fresh\n // chance — clear any terminal state a prior refusal left behind.\n _terminated = false;\n if (_ws && _wantOpen && _token === token && _tenant === tenant) return;\n // Token or tenant changed → tear down + reopen.\n if (_ws) {\n try { _ws.close(); } catch {}\n _ws = null;\n }\n _token = token;\n _tenant = tenant;\n _wantOpen = true;\n openSocket();\n}\n\n/**\n * Stop the ACS WS subscription. Call on signout / env switch.\n */\nexport function disconnectAcsWs() {\n _wantOpen = false;\n _terminated = false;\n _token = null;\n _tenant = null;\n if (_retryTimer) { clearTimeout(_retryTimer); _retryTimer = null; }\n if (_ws) {\n try { _ws.close(); } catch {}\n _ws = null;\n }\n}\n","/**\n * SDK-owned app-lifecycle coordinator (R7).\n *\n * The OS foreground/background EVENT is a native concern — the host owns the one\n * `AppState` listener (core cannot import react-native). But the REACTIONS to it\n * were scattered across three separate app listeners (ACS WS reconnect, a\n * battery refresh, an ACS GET safety-net), each firing independently on\n * `active`. R7 consolidates the reactions here: the host pokes `notifyAppForeground()`\n * from its single listener, and the SDK runs them in a deliberate order —\n * reconnect the ACS socket FIRST (so a resumed session is live before the GET\n * safety-net runs), then every registered host callback.\n *\n * No react-native import — the same seam works for any host.\n */\nimport { notifyAcsWsForeground } from './acs/ws';\n\nconst foregroundCbs = new Set<() => void>();\n\n/**\n * Register a reaction to run when the app returns to the foreground (e.g. the\n * host's battery refresh, or an ACS GET safety-net). Returns an unsubscribe.\n */\nexport function onAppForeground(cb: () => void): () => void {\n foregroundCbs.add(cb);\n return () => { foregroundCbs.delete(cb); };\n}\n\n/**\n * The host calls this from its single AppState listener when the app becomes\n * `active`. Order is owned here: force the ACS WS to reconnect first, then run\n * the registered host reactions. Each step is guarded so one failure can't stop\n * the rest.\n */\nexport function notifyAppForeground(): void {\n try { notifyAcsWsForeground(); } catch (e: any) {\n console.warn('[adas/lifecycle] acs foreground reconnect failed:', e?.message || e);\n }\n for (const cb of foregroundCbs) {\n try { cb(); } catch (e: any) {\n console.warn('[adas/lifecycle] foreground callback failed:', e?.message || e);\n }\n }\n}\n","/**\n * SDK-owned app-gate cascade (R9).\n *\n * The host's root layout used to inline the \"which screen do we show right now\"\n * decision — a linear cascade (loading → onboarding → auth → tenant →\n * capabilities → app) with a pre-auth PRIORITY (a web→app handoff beats an\n * invite, which beats the default login). That decision is generic navigation\n * orchestration: every host makes the same one. R9 moves the DECISION here as a\n * pure function; the host keeps the presentation (its branded phase screens) and\n * the Expo Router `<Stack>` that owns route registration.\n *\n * No react-native import — a pure function of the flags the host already tracks\n * (auth comes from the SDK session; onboarding/capabilities/invite/handoff are\n * host state), so it is trivially unit-testable and host-agnostic.\n */\nexport type AdasPhase =\n | 'loading'\n | 'onboarding'\n | 'handoff'\n | 'invite'\n | 'login'\n | 'tenant'\n | 'capabilities'\n | 'app';\n\nexport interface AdasGateFlags {\n /** Host config/service-URL hydration finished. */\n configHydrated: boolean;\n /** The SDK session is still restoring (status === 'loading'). */\n authLoading: boolean;\n /** First-launch onboarding: true = done, false = show it, undefined = still checking. */\n onboarded: boolean | undefined;\n /** The SDK session is authenticated. */\n isAuthenticated: boolean;\n /** Host capability init still running. */\n capabilitiesLoading: boolean;\n /** A pending web→app handoff (one-time code → session) — the strongest pre-auth signal. */\n handoffPending: boolean;\n /** A pending invite (tenant join → OAuth). */\n invitePending: boolean;\n /** Whether the host requires an explicit tenant pick before entering the app\n * (multi-tenant). A single-tenant host passes false and skips the phase. */\n requireTenantPick: boolean;\n /** The user has confirmed/selected a tenant (auto-true in single-tenant). */\n tenantConfirmed: boolean;\n /** Capability onboarding done. */\n hasCompletedSetup: boolean;\n}\n\n/**\n * Resolve the single screen phase to render, in the exact order the host used:\n * a still-hydrating app shows `loading`; then first-launch `onboarding`; then,\n * if signed out, the pre-auth priority handoff > invite > login; then (only when\n * the host requires it) the tenant pick; then capability setup; then the app.\n */\nexport function resolveAdasPhase(f: AdasGateFlags): AdasPhase {\n if (!f.configHydrated || f.authLoading || f.capabilitiesLoading || f.onboarded === undefined) {\n return 'loading';\n }\n if (!f.onboarded) return 'onboarding';\n if (!f.isAuthenticated) {\n return f.handoffPending ? 'handoff' : f.invitePending ? 'invite' : 'login';\n }\n if (f.requireTenantPick && !f.tenantConfirmed) return 'tenant';\n if (!f.hasCompletedSetup) return 'capabilities';\n return 'app';\n}\n","/**\n * Consumer-auth service client (R9 — moved from the app).\n *\n * Wraps the public consumer-auth service: config fetch, Google/Apple sign-in,\n * the web→app handoff exchange, the invite/open-join flows, and channel-link OTP.\n * Generic auth-flow mechanism — the host's native layer acquires the provider\n * id_token; this trades it for a tenant-scoped session JWT. It reads only the\n * SDK config getters (consumer-auth URL + the resolved tenant, which pre-auth\n * resolves to the host's active pick), so any host reuses it unchanged.\n */\nimport { getConsumerAuthApiUrl, getTenant } from './config';\n\nexport interface ConsumerProviderConfig {\n google?: {\n // Platform-specific client IDs. Each is its own Google OAuth client\n // (Web / iOS / Android — registered separately in Google Cloud Console).\n // The id_token's `aud` claim is whichever client minted it, so the\n // backend accepts any of them as a valid audience. The host picks the\n // right one for the current Platform.OS.\n web_client_id?: string;\n ios_client_id?: string;\n android_client_id?: string;\n // Legacy single field — kept for backward compat with older configs.\n client_id?: string;\n };\n apple?: { service_id: string };\n}\n\nexport interface ConsumerCapabilityConfig {\n ok: boolean;\n tenant: string;\n providers: ConsumerProviderConfig;\n linked_channels: string[];\n}\n\nexport interface ConsumerSignInResult {\n ok: boolean;\n jwt: string;\n actor: { id: string; email: string; role: string };\n}\n\nexport interface OtpStartResult {\n ok: boolean;\n challenge_id: string;\n sent_to: string;\n provider: string;\n debug_code?: string; // present only in stub mode\n}\n\n/**\n * Build a user-facing error message that NEVER includes raw HTML response\n * bodies. Cloudflare's 502 page is several KB of `<html>...` markup; bubbling\n * it through `Error.message` dumps it onto the login screen as raw text. We\n * trim aggressively and detect HTML so the user sees something useful.\n */\nfunction cleanErrorMessage(status: number, rawText: string, fallback: string): string {\n const trimmed = (rawText || '').trim();\n // Cloudflare / nginx / generic web-server error pages all start with `<`.\n if (trimmed.startsWith('<')) {\n return `${fallback} (${status}) — backend unreachable`;\n }\n if (!trimmed) return `${fallback} (${status})`;\n // Cap to 200 chars so a verbose JSON error doesn't still dump a wall of text.\n const short = trimmed.length > 200 ? `${trimmed.slice(0, 200)}…` : trimmed;\n return `${fallback} (${status}): ${short}`;\n}\n\nasync function postJson<T>(path: string, body: unknown, jwt?: string): Promise<T> {\n const res = await fetch(`${getConsumerAuthApiUrl()}${path}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(jwt ? { Authorization: `Bearer ${jwt}` } : {}),\n },\n body: JSON.stringify(body),\n });\n const text = await res.text();\n let parsed: any = null;\n try { parsed = JSON.parse(text); } catch { /* leave as null — text isn't JSON */ }\n if (!res.ok || parsed?.ok === false) {\n if (parsed?.error) throw new Error(parsed.error);\n throw new Error(cleanErrorMessage(res.status, text, `consumer-auth ${path} failed`));\n }\n return parsed as T;\n}\n\n/**\n * Fetch the public consumer config for the active tenant. Returns the public\n * OAuth client IDs needed to render Google/Apple sign-in buttons.\n */\nexport async function fetchConsumerConfig(): Promise<ConsumerCapabilityConfig> {\n const url = `${getConsumerAuthApiUrl()}/platform/consumer/config?tenant=${encodeURIComponent(getTenant())}`;\n const res = await fetch(url);\n // Read as text first so a Cloudflare 502 HTML page doesn't crash res.json()\n // with a multi-KB error message that ends up rendered on the login screen.\n const text = await res.text();\n let data: any = null;\n try { data = JSON.parse(text); } catch { /* not JSON — leave null, fall through */ }\n if (!res.ok || !data?.ok) {\n if (data?.error) throw new Error(data.error);\n throw new Error(cleanErrorMessage(res.status, text, 'Failed to load sign-in config'));\n }\n return data;\n}\n\n/**\n * Exchange a Google id_token for a tenant-scoped consumer JWT. The id_token\n * comes from the host's native Google sign-in flow.\n */\nexport async function signInWithGoogle(idToken: string): Promise<ConsumerSignInResult> {\n return postJson<ConsumerSignInResult>('/platform/consumer/oauth/google', {\n tenant: getTenant(),\n id_token: idToken,\n });\n}\n\n/** Exchange an Apple id_token for a tenant-scoped consumer JWT. */\nexport async function signInWithApple(idToken: string): Promise<ConsumerSignInResult> {\n return postJson<ConsumerSignInResult>('/platform/consumer/oauth/apple', {\n tenant: getTenant(),\n id_token: idToken,\n });\n}\n\n// ─── Web → app sign-in handoff ─────────────────────────────────────────\n// An already-authenticated WEB session mints a short-lived, single-use code\n// (QR / deep link: <scheme>://handoff?handoff=<code>). The app trades that code\n// for a normal session JWT here — NO auth header: the code IS the credential.\n// The code is single-use and 60s-lived; the returned JWT is a normal session\n// token. Second use / expired → the server 410s (surface as a friendly retry).\n//\n// `tenant` echoes the tenant the code was bound to. The session sets the active\n// tenant from the host, NOT from the JWT, so the caller must adopt this tenant\n// before login() or a cross-tenant handoff would mislabel the session.\nexport interface HandoffExchangeResult extends ConsumerSignInResult {\n tenant?: string;\n}\nexport async function exchangeHandoff(code: string): Promise<HandoffExchangeResult> {\n return postJson<HandoffExchangeResult>('/platform/consumer/handoff/exchange', { code });\n}\n\n// ─── Invite join flow ──────────────────────────────────────────────────\n// A tenant admin issues an invite; the code IS the tenant proof (no slug is\n// ever sent).\n\nexport interface InvitePreview {\n ok: boolean;\n org: string; // display name only — never the tenant slug\n role: string | null;\n}\n\n/**\n * Preview an invite (no auth) to render the \"Join <Org> as <role>?\" screen.\n * Throws with a friendly message on 404 (invalid/expired).\n */\nexport async function previewInvite(code: string): Promise<InvitePreview> {\n const url = `${getConsumerAuthApiUrl()}/platform/consumer/invite/${encodeURIComponent(code)}`;\n const res = await fetch(url);\n const text = await res.text();\n let data: any = null;\n try { data = JSON.parse(text); } catch { /* not JSON */ }\n if (res.status === 404) throw new Error('This invite is invalid or has expired.');\n if (!res.ok || !data?.ok) {\n if (data?.error) throw new Error(data.error);\n throw new Error(cleanErrorMessage(res.status, text, 'Could not load invite'));\n }\n return data;\n}\n\n/**\n * Exchange an invite + Google id_token for a tenant-scoped consumer JWT.\n * NOTE: no `tenant` field — the invite carries the tenant. Sending `tenant`\n * instead would be the open-signup path (403 on invite-only tenants).\n */\nexport async function joinWithGoogle(invite: string, idToken: string): Promise<ConsumerSignInResult> {\n return postJson<ConsumerSignInResult>('/platform/consumer/oauth/google', { invite, id_token: idToken });\n}\n\n/** Exchange an invite + Apple id_token for a tenant-scoped consumer JWT. */\nexport async function joinWithApple(invite: string, idToken: string): Promise<ConsumerSignInResult> {\n return postJson<ConsumerSignInResult>('/platform/consumer/oauth/apple', { invite, id_token: idToken });\n}\n\n// ─── Open self-signup (no invite) ──────────────────────────────────────\n// For tenants the admin marked FREE-TO-JOIN. The user names the team (slug),\n// we send { tenant, id_token } with no invite. The server accepts only if that\n// tenant allows open signup, else 403 — the client just surfaces the message.\n\nexport async function openJoinWithGoogle(tenant: string, idToken: string): Promise<ConsumerSignInResult> {\n return postJson<ConsumerSignInResult>('/platform/consumer/oauth/google', { tenant, id_token: idToken });\n}\n\nexport async function openJoinWithApple(tenant: string, idToken: string): Promise<ConsumerSignInResult> {\n return postJson<ConsumerSignInResult>('/platform/consumer/oauth/apple', { tenant, id_token: idToken });\n}\n\n/**\n * Start a channel-link OTP challenge. The consumer must be authenticated\n * (their JWT is passed via the Authorization header).\n */\nexport async function linkChannelStart(\n jwt: string,\n channel: string,\n handle: string,\n): Promise<OtpStartResult> {\n return postJson<OtpStartResult>('/platform/consumer/link-channel/start', { channel, handle }, jwt);\n}\n\n/** Verify the OTP and complete the channel link. */\nexport async function linkChannelVerify(\n jwt: string,\n challengeId: string,\n code: string,\n): Promise<{ ok: boolean; channel: string; handle: string; actorId: string; transferred: boolean }> {\n return postJson('/platform/consumer/link-channel/verify', { challenge_id: challengeId, code }, jwt);\n}\n","/**\n * Deep-link parsing (R9) — the generic navigation-orchestration half of the\n * host's invite/handoff capture. Extracting a code from a link's query string\n * is the same for any host; only the URL SCHEME (adas://, universal-link\n * domains) is host-specific and stays in the host's Expo Router config.\n */\n\n/**\n * Pull a query param's value out of a deep link, URL-decoded. Returns null when\n * the link is empty, the param is absent, or the value is empty. Tolerant of a\n * malformed encoding (falls back to the raw match) so a valid code is never\n * dropped on a stray `%`.\n */\nexport function parseDeepLinkParam(url: string | null | undefined, param: string): string | null {\n if (!url) return null;\n const re = new RegExp(`[?&]${param}=([^&#\\\\s]+)`, 'i');\n const m = url.match(re);\n if (!m) return null;\n try {\n const v = decodeURIComponent(m[1]);\n return v.length > 0 ? v : null;\n } catch {\n return m[1].length > 0 ? m[1] : null;\n }\n}\n\n/** A tenant-invite code from `?invite=<code>`. */\nexport function parseInviteCode(url: string | null | undefined): string | null {\n return parseDeepLinkParam(url, 'invite');\n}\n\n/**\n * A web→app handoff code from `?handoff=<code>`. Deliberately keyed on `handoff=`\n * and NOT `code=`, so it never collides with an OAuth `?code=` redirect.\n */\nexport function parseHandoffCode(url: string | null | undefined): string | null {\n return parseDeepLinkParam(url, 'handoff');\n}\n","// packages/core/src/workspace.ts\n//\n// Workspace subscription state — resolved ENTIRELY by the server and surfaced,\n// never interpreted by the client. The app holds NO subscription rules (no trial\n// dates, plan names, or close-code meaning); it renders what the server says.\n// That is the whole point of the design: a new plan needs no mobile release.\n//\n// Source: GET /api/session (authenticated, below attachActor). It answers even\n// when the realtime socket is refused (an expired tenant gets WS close 4003), so\n// it — not a WS frame — is the startup carrier for this state. A future ACS WS\n// delta push will update it in place; that path is not built yet, so no\n// push-apply API is exposed until the delta contract exists (avoids freezing an\n// unused contract early).\n\nimport { create } from 'zustand';\nimport { apiFetch } from './api/client';\n\nexport type WorkspaceState = 'active' | 'expired' | 'suspended' | 'unknown';\n\n/** Server-granted capabilities. Open-ended on purpose: a plan can introduce a\n * new capability name and old app versions simply won't grant it. */\nexport interface WorkspaceCapabilities {\n chat?: boolean;\n live_updates?: boolean;\n ui_commands?: boolean;\n [capability: string]: boolean | undefined;\n}\n\nexport interface WorkspaceSnapshot {\n state: WorkspaceState;\n reason: string | null;\n trialEndsAt: string | null;\n expiresInDays: number | null;\n capabilities: WorkspaceCapabilities;\n /** Server-authored copy. Rendered VERBATIM — the client never composes it. */\n message: string | null;\n}\n\n/** GET /api/session response envelope (only `workspace` is read here). */\ninterface SessionResponse {\n ok?: boolean;\n actor?: unknown;\n tenant?: string;\n workspace?: Partial<WorkspaceSnapshot> | null;\n}\n\nfunction normalize(w: Partial<WorkspaceSnapshot> | null | undefined): WorkspaceSnapshot | null {\n if (!w || typeof w !== 'object') return null;\n const state: WorkspaceState =\n w.state === 'active' || w.state === 'expired' || w.state === 'suspended' ? w.state : 'unknown';\n return {\n state,\n reason: typeof w.reason === 'string' ? w.reason : null,\n trialEndsAt: typeof w.trialEndsAt === 'string' ? w.trialEndsAt : null,\n expiresInDays: typeof w.expiresInDays === 'number' ? w.expiresInDays : null,\n capabilities:\n w.capabilities && typeof w.capabilities === 'object' ? (w.capabilities as WorkspaceCapabilities) : {},\n message: typeof w.message === 'string' && w.message.trim() ? w.message : null,\n };\n}\n\n// Monotonic identity generation. Bumped whenever the workspace IDENTITY changes\n// — clear() on tenant switch / signout. A refresh() captures the generation at\n// dispatch and applies its result only if the generation has not moved on, so a\n// slow /api/session response scoped to tenant A can never land on (and mislabel)\n// tenant B after a switch. Without this, a late A response — success OR the\n// snapshot-preserving failure path — could leak A's banner/capabilities into B.\nlet _gen = 0;\n\ninterface WorkspaceStore {\n /** null = not loaded / endpoint absent — treat the app as fully capable. */\n snapshot: WorkspaceSnapshot | null;\n loaded: boolean;\n /** Fetch GET /api/session and store the workspace snapshot (identity-guarded). */\n refresh: () => Promise<void>;\n /** Reset AND invalidate any in-flight refresh. Call on signout / tenant switch\n * (the host must clear before refreshing a new identity). */\n clear: () => void;\n}\n\nexport const useWorkspaceStore = create<WorkspaceStore>((set) => ({\n snapshot: null,\n loaded: false,\n refresh: async () => {\n const gen = _gen; // this refresh belongs to the CURRENT identity\n try {\n const res = await apiFetch<SessionResponse>('/api/session');\n if (gen !== _gen) return; // superseded by a tenant switch / signout — drop the stale response\n set({ snapshot: normalize(res?.workspace), loaded: true });\n } catch {\n // Superseded → do not touch the new identity's state.\n if (gen !== _gen) return;\n // Same identity, transient failure (or pre-promotion 404): DO NOT grey\n // anything out. Keep the last-known snapshot (null on first load), so the\n // app stays fully capable and the banner only ever shows from a real\n // snapshot, never from the absence of one.\n set({ loaded: true });\n }\n },\n clear: () => {\n _gen++; // invalidate any in-flight refresh from the previous identity\n set({ snapshot: null, loaded: false });\n },\n}));\n\n/**\n * Is a capability granted? The SERVER decides; the client never infers it from\n * a local rule. Two deliberate, asymmetric defaults:\n * - No snapshot loaded (endpoint absent, or not fetched yet) → GRANTED, so the\n * app behaves exactly as it did before this layer existed.\n * - Snapshot loaded but this capability missing/unknown → NOT granted, so a new\n * plan can restrict an old app version and an unrecognised name never leaks\n * access.\n */\nexport function capabilityEnabled(name: string): boolean {\n const { snapshot } = useWorkspaceStore.getState();\n if (!snapshot) return true;\n return snapshot.capabilities?.[name] === true;\n}\n\n/** Reactive form of capabilityEnabled for components (re-renders on change). */\nexport function useCapability(name: string): boolean {\n return useWorkspaceStore((s) => {\n if (!s.snapshot) return true;\n return s.snapshot.capabilities?.[name] === true;\n });\n}\n","/**\n * Chat store — talks to the ADAS Core API.\n *\n * Flow:\n * 1. loadConversation() → GET /api/conversation → display history\n * 2. sendMessage(text) → POST /api/chat { goal } → returns jobId\n * 3. WebSocket (poll fallback) → real-time progress\n * 4. When done → GET /api/conversation → formatted response\n *\n * Platform notes: no Expo/AsyncStorage imports. Attachment persistence is\n * delegated to the host via config.attachmentCache; identity and tokens come\n * from the host via initAdas().\n */\nimport { create } from 'zustand';\nimport { getCoreApiUrl, getCoreWsUrl, getToken, getTenant, getUser, getConfig } from '../config';\nimport { pluginCommandEmitter } from '../utils/emitter';\nimport type { OutgoingAttachment } from '../storage/types';\nimport type { Message, MessageAttachment, JobProgress, UIAction } from './types';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface ChatState {\n messages: Message[];\n loading: boolean;\n error: string | null;\n activeJobId: string | null;\n conversationLoaded: boolean;\n /** Load conversation history from Core */\n loadConversation: () => Promise<void>;\n /** Send a message via POST /api/chat — creates a job, streams progress */\n sendMessage: (text: string, skillSlug?: string, attachments?: OutgoingAttachment[]) => Promise<void>;\n /** Respond to a paused sys.askUser question */\n respondToQuestion: (jobId: string, inputId: string, answer: string) => Promise<void>;\n /** Update in-flight job progress (called by the stream handler) */\n updateJobProgress: (jobId: string, progress: Partial<JobProgress>) => void;\n /** Reset chat (also re-fetches history on next mount — used on auth switch) */\n clearChat: () => void;\n /** Start a fresh empty chat without re-fetching history */\n newChat: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction coreHeaders(): Record<string, string> {\n const token = getToken();\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (token) headers['Authorization'] = `Bearer ${token}`;\n return headers;\n}\n\nfunction isTerminalStatus(status?: unknown): boolean {\n const s = typeof status === 'string' ? status.toLowerCase() : '';\n return s === 'done' || s === 'error' || s === 'failed' || s === 'completed';\n}\n\nfunction normalizeJobStatus(snap: any): JobProgress['status'] {\n const status = typeof snap?.status === 'string' ? snap.status.toLowerCase() : '';\n if (status === 'done' || status === 'completed') return 'done';\n if (status === 'error' || status === 'failed') return 'error';\n if (status === 'paused' || status === 'waiting') return 'paused';\n if (status === 'unknown') return 'unknown';\n return 'running';\n}\n\nfunction extractPendingQuestion(snap: any): any {\n return snap?.pendingQuestion || snap?.state?.pendingQuestion || snap?.state?.waiting || null;\n}\n\nfunction extractActorId(snap: any): string | undefined {\n const actorId =\n snap?.actorId ||\n snap?.actor?.actorId ||\n snap?.actor?.id ||\n snap?.state?.actorId ||\n snap?.state?.actor?.actorId ||\n snap?.state?.actor?.id;\n return typeof actorId === 'string' && actorId.length > 0 ? actorId : undefined;\n}\n\nfunction extractLiveProgress(snap: any): JobProgress['liveProgress'] | undefined {\n const lp = snap?.liveProgress || snap?.state?.__live_progress;\n const latest = Array.isArray(lp) ? lp[lp.length - 1] : lp;\n if (!latest?.message) return undefined;\n return {\n message: String(latest.message || ''),\n ...(latest.step != null ? { step: latest.step } : {}),\n ...(latest.total != null ? { total: latest.total } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Extract the most-recently-called tool from job exec steps.\n// snap.state.steps.__exec = { iter_0: [{tool, ok, reason}], iter_1: [...] }\n// ---------------------------------------------------------------------------\nfunction extractCurrentTool(state: any): string | undefined {\n const exec = state?.steps?.__exec;\n if (!exec || typeof exec !== 'object') return undefined;\n const latest = Object.keys(exec)\n .map(k => ({ k, n: parseInt(k.replace('iter_', ''), 10) }))\n .filter(({ n }) => !isNaN(n))\n .sort((a, b) => b.n - a.n)[0];\n if (!latest) return undefined;\n const steps = exec[latest.k];\n if (!Array.isArray(steps) || !steps.length) return undefined;\n const last = steps[steps.length - 1];\n return (last?.tool || last?.phase) as string | undefined;\n}\n\nfunction snapshotToProgress(snap: any, rootJobId: string): Partial<JobProgress> {\n const pq = extractPendingQuestion(snap);\n const progress: Partial<JobProgress> = {\n jobId: rootJobId,\n actorId: extractActorId(snap),\n status: pq ? 'paused' : normalizeJobStatus(snap),\n subStatus: snap?.subStatus || snap?.phase || snap?.state?.visual?.phase,\n phase: snap?.phase || snap?.state?.visual?.phase,\n iteration: snap?.iteration || snap?.state?.plannerIter,\n skillSlug: snap?.skillSlug || snap?.activeSkill,\n currentTool: snap?.currentTool || snap?.lastTool || extractCurrentTool(snap?.state),\n };\n\n const liveProgress = extractLiveProgress(snap);\n if (liveProgress) progress.liveProgress = liveProgress;\n\n // Human-language status (additive; absent when disabled/uncached).\n if (typeof snap?.humanStatus === 'string' && snap.humanStatus) progress.humanStatus = snap.humanStatus;\n if (snap?.humanLabels && typeof snap.humanLabels === 'object') progress.humanLabels = snap.humanLabels;\n\n if (pq) {\n progress.pendingQuestion = {\n inputId: pq.correlation_id || pq.correlationId || pq.inputId || '',\n question: pq.prompt_message || pq.question || 'The assistant needs your input',\n inputType: pq.inputType || 'text',\n options: pq.options || pq.requested_fields,\n placeholder: pq.placeholder,\n };\n }\n\n return progress;\n}\n\nfunction isTerminalSnapshot(snap: any): boolean {\n if (extractPendingQuestion(snap)) return false;\n return snap?.done === true || isTerminalStatus(snap?.status);\n}\n\n/**\n * Sentinel delimiters used to embed structured table data inside a flat\n * message string. UI layers split on these and render the contents with a\n * native table component instead of markdown tables — markdown tables look\n * terrible on narrow phone screens.\n *\n * Unicode Private Use Area chars (U+E100/U+E101) so they never collide with\n * anything the model could legitimately emit.\n */\nexport const TABLE_BLOCK_OPEN = '\\uE100';\nexport const TABLE_BLOCK_CLOSE = '\\uE101';\n\n/**\n * Render a `{ columns: [...], rows: [...] }` payload as a sentinel-wrapped\n * JSON marker. Returns null if the shape doesn't match.\n */\nfunction renderTableShape(content: any): string | null {\n if (!content || typeof content !== 'object') return null;\n const cols = Array.isArray(content.columns) ? content.columns : null;\n const rows = Array.isArray(content.rows) ? content.rows : null;\n if (!cols || !rows) return null;\n // encodeURIComponent so the JSON can never contain newlines or sentinel chars.\n const payload = encodeURIComponent(JSON.stringify({ columns: cols, rows }));\n return `${TABLE_BLOCK_OPEN}${payload}${TABLE_BLOCK_CLOSE}`;\n}\n\n/**\n * Extract display text from a conversation turn's assistant blocks.\n * Handles multiple block kinds (text, code, json, markdown, table, html).\n */\nexport function blocksToText(blocks?: Array<{ kind: string; content: any }>): string {\n if (!blocks || blocks.length === 0) return '';\n // Mirror the web renderer: when a human-readable text/markdown block exists in\n // the same turn, drop internal-state json/code blocks (planning state,\n // askUser payloads) — otherwise they render as a raw ```json blob AFTER the\n // real answer. Standalone json/code (no text block, e.g. a table-only turn)\n // still renders as before.\n const hasTextBlock = blocks.some(\n b => b.kind === 'text' || b.kind === 'markdown' || b.kind === 'description',\n );\n return blocks.map(b => {\n if ((b.kind === 'json' || b.kind === 'code') && hasTextBlock) return '';\n if (b.kind === 'text' || b.kind === 'markdown' || b.kind === 'description') {\n return typeof b.content === 'string' ? b.content : '';\n }\n if (b.kind === 'code') {\n const code = typeof b.content === 'object' ? b.content.content : b.content;\n return `\\`\\`\\`\\n${code}\\n\\`\\`\\``;\n }\n if (b.kind === 'table' && b.content && typeof b.content === 'object') {\n const md = renderTableShape(b.content);\n if (md) return md;\n // Fall through to generic JSON rendering if shape is unexpected.\n }\n if (b.kind === 'json') {\n // Skip pendingQuestion state objects — internal sys.askUser data\n if (b.content && typeof b.content === 'object' && 'pendingQuestion' in b.content) return '';\n // Polisher sometimes wraps tables as kind:'json' when the content has\n // columns/rows — render those as table blocks too.\n const md = renderTableShape(b.content);\n if (md) return md;\n return '```json\\n' + JSON.stringify(b.content, null, 2) + '\\n```';\n }\n if (b.kind === 'html') {\n return typeof b.content === 'string' ? b.content : '';\n }\n return typeof b.content === 'string' ? b.content : JSON.stringify(b.content);\n }).filter(Boolean).join('\\n\\n');\n}\n\n/** Extract image/file attachments from conversation blocks */\nfunction blocksToAttachments(blocks?: Array<{ kind: string; hash?: string; url?: string; mimeType?: string; name?: string }>): MessageAttachment[] | undefined {\n if (!blocks || blocks.length === 0) return undefined;\n const atts: MessageAttachment[] = [];\n for (const b of blocks) {\n if ((b.kind === 'image' || b.kind === 'file') && (b.url || b.hash)) {\n atts.push({\n mimeType: b.mimeType || (b.kind === 'image' ? 'image/jpeg' : 'application/octet-stream'),\n name: b.name || 'attachment',\n previewUri: b.url ? `${getCoreApiUrl()}${b.url}` : undefined,\n });\n }\n }\n return atts.length > 0 ? atts : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Store\n// ---------------------------------------------------------------------------\n\nexport const useChatStore = create<ChatState>((set, get) => ({\n messages: [],\n loading: false,\n error: null,\n activeJobId: null,\n conversationLoaded: false,\n\n // -----------------------------------------------------------------------\n // Load conversation history from Core\n // GET /api/conversation → array of turns [{user, assistant, jobId, ...}]\n // -----------------------------------------------------------------------\n loadConversation: async () => {\n try {\n const attachmentCache = getConfig().attachmentCache;\n await attachmentCache?.ready();\n\n const res = await fetch(`${getCoreApiUrl()}/api/conversation?limit=20`, {\n headers: coreHeaders(),\n });\n if (!res.ok) {\n console.warn('[adas-chat] Failed to load conversation:', res.status);\n set({ conversationLoaded: true });\n return;\n }\n\n const data = await res.json();\n if (!data.ok || !data.items) {\n set({ conversationLoaded: true });\n return;\n }\n\n // Persisted turns from /api/conversation are the SOLE source of history.\n // Live job state is mirrored only via WS into the placeholder bubble\n // created by sendMessage; in-flight local state is preserved below.\n const items: any[] = [...data.items].reverse();\n const persistedMessages: Message[] = [];\n const seenJobIds = new Set<string>();\n for (const turn of items) {\n // Deduplicate by jobId (chain jobs can produce duplicate turns)\n if (turn.jobId && seenJobIds.has(turn.jobId)) continue;\n if (turn.jobId) seenJobIds.add(turn.jobId);\n\n // User message\n const userText = blocksToText(turn.user?.blocks);\n let userAtts = blocksToAttachments(turn.user?.blocks);\n // Merge locally cached attachments if API doesn't have them\n if (!userAtts && turn.jobId) {\n userAtts = attachmentCache?.get(turn.jobId);\n }\n const turnChainId = typeof turn.chainId === 'string' ? turn.chainId : undefined;\n if (userText || userAtts) {\n persistedMessages.push({\n id: `user-${turn.jobId}`,\n role: 'user',\n content: userText || '',\n timestamp: new Date(turn.startedAt || Date.now()),\n skillSlug: turn.skillSlug,\n ...(userAtts ? { attachments: userAtts } : {}),\n ...(turnChainId ? { chainId: turnChainId } : {}),\n });\n }\n // Assistant response\n const assistantText = blocksToText(turn.assistant?.blocks);\n const assistantAtts = blocksToAttachments(turn.assistant?.blocks);\n const assistantActions = Array.isArray(turn.assistant?.actions) ? turn.assistant.actions : undefined;\n const turnKind = typeof turn.kind === 'string' ? turn.kind : undefined;\n if (assistantText || assistantAtts || assistantActions?.length) {\n persistedMessages.push({\n id: `bot-${turn.jobId}`,\n role: 'assistant',\n content: assistantText || '',\n timestamp: new Date(turn.finishedAt || turn.startedAt || Date.now()),\n skillSlug: turn.skillSlug,\n ...(assistantAtts ? { attachments: assistantAtts } : {}),\n ...(assistantActions?.length ? { actions: assistantActions } : {}),\n ...(turnChainId ? { chainId: turnChainId } : {}),\n ...(turnKind ? { kind: turnKind } : {}),\n });\n }\n }\n\n // Preserve in-flight LOCAL state across pull-to-refresh. /api/conversation\n // is the truth for HISTORY; anything mid-flight lives only as a local\n // WS-driven placeholder created by sendMessage.\n const current = get().messages;\n const keptPlaceholders: Message[] = [];\n for (const m of current) {\n if (!m.id.startsWith('job-') || !m.jobProgress || !m.chainId) continue;\n const matchIdx = persistedMessages.findIndex(pm =>\n pm.role === 'assistant' && pm.chainId === m.chainId\n );\n if (matchIdx >= 0) {\n // The persisted turn for this chain has arrived. Move the live\n // progress overlay (pill) onto it — but only while still in flight.\n const st = m.jobProgress.status;\n if (st === 'running' || st === 'paused') {\n persistedMessages[matchIdx] = { ...persistedMessages[matchIdx], jobProgress: m.jobProgress };\n }\n // Drop the placeholder either way.\n } else {\n // No persisted turn yet — chain still in flight. Keep it.\n keptPlaceholders.push(m);\n }\n }\n\n // Optimistic user bubbles (id=`user-<ms>` from sendMessage). Keep until\n // a persisted user turn arrives. Dedup by chainId — content match has\n // false positives (\"Yes\", \"Continue\", etc.).\n const isLocalUserId = (id: string) => /^user-\\d+$/.test(id);\n const keptUserBubbles = current.filter(m => {\n if (m.role !== 'user' || !isLocalUserId(m.id)) return false;\n if (!m.chainId) return true; // POST in flight — can't dedup yet\n return !persistedMessages.some(pm =>\n pm.role === 'user' && pm.chainId === m.chainId\n );\n });\n\n const finalMessages = [...persistedMessages, ...keptUserBubbles, ...keptPlaceholders];\n finalMessages.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());\n\n let firstRunning: Message | undefined = keptPlaceholders.find(m => m.jobProgress?.status === 'running');\n\n // Cold-start / post-refresh reattach: WS subscriptions die on app\n // restart. Fetch ALL active chains for this actor (chain-native SlimChain\n // API) and reattach EACH — multiple concurrent chains must all come back\n // in progress, not just the most-recently-updated one. Falls back to the\n // legacy single-chain /api/chain/current on older backends.\n try {\n let activeChains: any[] = [];\n const chainsRes = await fetch(`${getCoreApiUrl()}/api/chains/active`, {\n headers: coreHeaders(),\n });\n if (chainsRes.ok) {\n const body = await chainsRes.json();\n activeChains = Array.isArray(body?.chains) ? body.chains : [];\n } else {\n // Legacy backend without the plural endpoint — single most-recent chain.\n const legacy = await fetch(`${getCoreApiUrl()}/api/chain/current`, {\n headers: coreHeaders(),\n });\n if (legacy.ok) {\n const lb = await legacy.json();\n if (lb?.data?.id) activeChains = [lb.data];\n }\n }\n\n for (const slim of activeChains) {\n if (!slim?.id) continue;\n const chainId = String(slim.id);\n const alreadyTracked = finalMessages.some(m =>\n m.role === 'assistant' && m.jobProgress?.jobId === chainId\n );\n if (alreadyTracked) continue;\n\n // Chain-native status: prefer the whole-chain aggregate (chainStatus)\n // over the active job's per-job status, so e.g. a chain still running\n // across a handoff isn't shown as \"done\". A pending question still\n // overrides to \"paused\" inside snapshotToProgress.\n const progress = snapshotToProgress(\n { ...slim, status: slim.chainStatus || slim.status },\n chainId,\n );\n const chainCreatedAt = typeof slim.createdAt === 'number'\n ? slim.createdAt\n : (typeof slim.lastUpdate === 'number' ? slim.lastUpdate : Date.now());\n const chainLastUpdate = typeof slim.lastUpdate === 'number' ? slim.lastUpdate : chainCreatedAt;\n const jobProgress = {\n jobId: chainId,\n actorId: extractActorId(slim),\n status: 'running',\n chainStartedAt: chainCreatedAt,\n ...progress,\n } as JobProgress;\n\n // Synthesize the user bubble if a hard refresh wiped the optimistic\n // one and the chain hasn't persisted its turn yet.\n const goal = typeof slim.goal === 'string' ? slim.goal : '';\n const userExists = finalMessages.some(m =>\n m.role === 'user' && (m.chainId === chainId || (goal && m.content === goal))\n );\n if (goal && !userExists) {\n finalMessages.push({\n id: `user-${chainId}`,\n role: 'user',\n content: goal,\n timestamp: new Date(chainCreatedAt),\n chainId,\n });\n }\n\n // Assistant side: attach to matching persisted turn if one exists;\n // otherwise push a placeholder bubble.\n let matchIdx = -1;\n for (let i = finalMessages.length - 1; i >= 0; i--) {\n const m = finalMessages[i];\n if (m.role === 'assistant' && m.chainId === chainId) {\n matchIdx = i;\n break;\n }\n }\n if (matchIdx >= 0) {\n finalMessages[matchIdx] = { ...finalMessages[matchIdx], jobProgress };\n if (jobProgress.status === 'running' && !firstRunning) firstRunning = finalMessages[matchIdx];\n } else {\n const reattachBubble: Message = {\n id: `job-${chainId}`,\n role: 'assistant',\n content: 'Working on it...',\n timestamp: new Date(chainLastUpdate),\n jobProgress,\n chainId,\n };\n finalMessages.push(reattachBubble);\n if (jobProgress.status === 'running' && !firstRunning) firstRunning = reattachBubble;\n }\n\n // Re-open the live subscription for this chain.\n streamJob(chainId, getTenant(), getToken());\n }\n\n finalMessages.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());\n } catch {}\n\n set({\n messages: finalMessages,\n conversationLoaded: true,\n error: null,\n activeJobId: firstRunning?.jobProgress?.jobId || null,\n loading: !!firstRunning,\n });\n } catch (err: any) {\n console.warn('[adas-chat] Error loading conversation:', err.message);\n set({ conversationLoaded: true });\n }\n },\n\n // -----------------------------------------------------------------------\n // Send message via Core API\n // POST /api/chat { goal } → { jobId } → stream → fetch turn result\n // -----------------------------------------------------------------------\n sendMessage: async (text: string, skillSlug?: string, attachments?: OutgoingAttachment[]) => {\n // No `if (loading) return` guard: Chain Additions needs the user to send\n // WHILE a chain is running. The backend router decides attach/cancel/new.\n\n const userMsg: Message = {\n id: `user-${Date.now()}`,\n role: 'user',\n content: text,\n timestamp: new Date(),\n ...(attachments?.length ? {\n attachments: attachments.map(a => ({\n mimeType: a.mimeType,\n name: a.name,\n previewUri: a.mimeType.startsWith('image/') ? `data:${a.mimeType};base64,${a.data}` : undefined,\n }))\n } : {}),\n };\n\n set(s => ({ messages: [...s.messages, userMsg], loading: true, error: null }));\n\n try {\n // Backend auto-resolves the skill via conversation continuity → channel\n // routing → default. triggerContext.channel + replyContext.chatId are\n // required by handoff-controller-mcp's schema — without them handoffs\n // fail silently.\n const actor = getUser();\n const body: Record<string, unknown> = {\n goal: text,\n // Opt in to the Chain Additions router (attach/cancel/new while a\n // chain runs). Backend ignores it where not applicable — safe to send.\n chainRouting: true,\n triggerContext: {\n channel: 'mobile',\n replyContext: { chatId: actor?.actorId || 'unknown' },\n },\n };\n if (skillSlug) body.skillSlug = skillSlug;\n if (attachments?.length) body.attachments = attachments;\n\n const res = await fetch(`${getCoreApiUrl()}/api/chat`, {\n method: 'POST',\n headers: coreHeaders(),\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n let errMsg = `Server error (${res.status})`;\n try {\n const text = await res.text();\n // Don't show raw HTML — extract a short message\n if (text.startsWith('<') || text.startsWith('<!')) {\n errMsg = res.status === 502 ? 'Server is temporarily unavailable. Please try again.' : `Server error (${res.status})`;\n } else {\n const parsed = JSON.parse(text);\n errMsg = parsed.error || errMsg;\n }\n } catch {}\n throw new Error(errMsg);\n }\n\n const data = await res.json();\n\n if (!data.ok) {\n throw new Error(data.error || 'Failed to send message');\n }\n\n // Chain Additions: the router may ATTACH this message to the running\n // chain or CANCEL it. These replies carry NO jobId — handle them and\n // don't start a second stream.\n if (data.mode === 'chain_addition' || data.mode === 'chain_cancel') {\n const ack = data.mode === 'chain_addition'\n ? (data.ack || 'Got it — added to the running task.')\n : 'Stopping the task…';\n set(s => ({\n messages: s.messages\n .map(m => (m.id === userMsg.id && data.chainId) ? { ...m, chainId: data.chainId } : m)\n .concat({\n id: `ack-${Date.now()}`,\n role: 'assistant',\n content: ack,\n timestamp: new Date(),\n ...(data.chainId ? { chainId: data.chainId } : {}),\n }),\n }));\n return;\n }\n\n const jobId = data.id || data.jobId;\n if (!jobId) {\n throw new Error('No job ID returned');\n }\n\n // Cache attachments so they survive loadConversation reload. Awaited\n // intentionally — fire-and-forget can lose the write on quick reload.\n if (attachments?.length) {\n await getConfig().attachmentCache?.save(jobId, attachments);\n }\n\n // Placeholder assistant bubble = live mirror of the active chain.\n // Carries chainId so a subsequent loadConversation refresh can adopt\n // this placeholder's progress. For the root job, chainId == jobId.\n const progressMsg: Message = {\n id: `job-${jobId}`,\n role: 'assistant',\n content: 'Working on it...',\n timestamp: new Date(),\n jobProgress: {\n jobId,\n actorId: actor?.actorId,\n status: 'running',\n subStatus: 'Starting...',\n chainStartedAt: Date.now(),\n },\n chainId: jobId,\n };\n\n // Attach chainId to the optimistic user bubble so loadConversation can\n // dedup against persisted user turns by chainId.\n set(s => ({\n messages: s.messages.map(m =>\n m.id === userMsg.id ? { ...m, chainId: jobId } : m\n ).concat(progressMsg),\n activeJobId: jobId,\n }));\n\n // Start streaming for real-time progress\n streamJob(jobId, getTenant(), getToken());\n } catch (err: any) {\n set(s => ({\n messages: [...s.messages, {\n id: `error-${Date.now()}`,\n role: 'system' as const,\n content: `Error: ${err.message}`,\n timestamp: new Date(),\n }],\n loading: false,\n }));\n }\n },\n\n // -----------------------------------------------------------------------\n // Respond to sys.askUser paused question\n // -----------------------------------------------------------------------\n respondToQuestion: async (jobId: string, inputId: string, answer: string) => {\n try {\n const res = await fetch(`${getCoreApiUrl()}/api/job/${jobId}/respond`, {\n method: 'POST',\n headers: coreHeaders(),\n body: JSON.stringify({ inputId, answer }),\n });\n const data = await res.json();\n if (!data.ok) {\n console.warn('[adas-chat] Failed to respond to question:', data.error);\n }\n } catch (err: any) {\n console.warn('[adas-chat] Error responding to question:', err.message);\n }\n },\n\n // -----------------------------------------------------------------------\n // Update job progress (called by the stream handler)\n // -----------------------------------------------------------------------\n updateJobProgress: (jobId: string, progress: Partial<JobProgress>) => {\n set(s => ({\n messages: s.messages.map(m => {\n if (m.jobProgress?.jobId === jobId) {\n const updated = { ...m.jobProgress, ...progress };\n // Clear pendingQuestion when status is no longer paused\n if (progress.status && progress.status !== 'paused') {\n updated.pendingQuestion = undefined;\n }\n // Persisted turns (id `bot-…`) own their content/actions from\n // /api/conversation — the live mirror must never overwrite them.\n const isPersisted = m.id.startsWith('bot-');\n const content = isPersisted\n ? m.content\n : (progress.resultMessage\n || progress.pendingQuestion?.question\n || m.content);\n const actions = isPersisted\n ? m.actions\n : (progress.resultActions?.length ? progress.resultActions : m.actions);\n return {\n ...m,\n jobProgress: updated as JobProgress,\n content,\n ...(actions ? { actions } : {}),\n };\n }\n return m;\n }),\n loading: (progress.status === 'done' || progress.status === 'error' || progress.status === 'paused')\n ? false\n : s.loading,\n activeJobId: (progress.status === 'done' || progress.status === 'error' || progress.status === 'paused')\n ? null\n : s.activeJobId,\n }));\n },\n\n clearChat: () => {\n set({\n messages: [],\n loading: false,\n error: null,\n activeJobId: null,\n conversationLoaded: false,\n });\n },\n newChat: () => {\n set({\n messages: [],\n loading: false,\n error: null,\n activeJobId: null,\n conversationLoaded: true,\n });\n },\n}));\n\n// ---------------------------------------------------------------------------\n// WebSocket Streaming — persistent connection, reused across jobs\n// ---------------------------------------------------------------------------\n\n// Track processed plugin command correlationIds to avoid duplicates\nconst _processedPluginCmds = new Set<string>();\n\nfunction dispatchPluginCommands(cmds: any[]) {\n for (const cmd of cmds) {\n const corrId = cmd?.correlationId || cmd?.correlation_id;\n if (corrId && _processedPluginCmds.has(corrId)) continue;\n if (corrId) _processedPluginCmds.add(corrId);\n\n pluginCommandEmitter.emit('plugin-command', cmd);\n const pluginId = cmd?.pluginId || cmd?.plugin_id || cmd?.args?.plugin_id;\n if (pluginId) {\n const props = cmd?.props ?? cmd?.args?.props ?? undefined;\n pluginCommandEmitter.emit('focus-plugin', { pluginId, props });\n }\n }\n}\n\n// Persistent WS connection — stays open across jobs\nlet _ws: WebSocket | null = null;\nlet _wsReady = false;\n\n/** What this client can execute, supplied by the host (see setClientDeclarationProvider). */\ntype ClientDeclaration = { device_host: boolean; tools: Array<{ name: string }> };\nlet _clientDeclarationProvider: (() => ClientDeclaration | null) | null = null;\n\n/**\n * Host DI: what to tell the server about this client on connect.\n *\n * Kept as a provider rather than a value because the answer changes — plugins\n * load after boot, so the tool list is not known when the socket first opens.\n */\nexport function setClientDeclarationProvider(fn: (() => ClientDeclaration | null) | null): void {\n _clientDeclarationProvider = fn;\n}\nlet _currentJobId: string | null = null;\n\nfunction ensureWs(tenant: string, token: string | null): WebSocket {\n if (_ws && _wsReady) return _ws;\n\n const wsUrl = `${getCoreWsUrl()}/ws?token=${encodeURIComponent(token || '')}&tenant=${encodeURIComponent(tenant)}`;\n const ws = new WebSocket(wsUrl);\n _ws = ws;\n _wsReady = false;\n\n ws.onopen = () => {\n _wsReady = true;\n\n // Tell the server what this client IS. A device tool executes here, so the\n // server has to reach the right connection: the same person may also have a\n // browser tab on this stream, and \"read the GPS\" has one correct answer.\n // Without this the server can only broadcast and hope.\n try {\n const declared = _clientDeclarationProvider?.();\n if (declared) ws.send(JSON.stringify({ type: 'declare_client', ...declared }));\n } catch {\n // Never let a declaration failure cost us the connection.\n }\n\n // If a job is waiting for the connection, subscribe now\n if (_currentJobId) {\n ws.send(JSON.stringify({ type: 'subscribe', jobId: _currentJobId }));\n }\n };\n\n ws.onmessage = (ev) => {\n try {\n const msg = JSON.parse(typeof ev.data === 'string' ? ev.data : '');\n const { event, data } = msg;\n const { updateJobProgress } = useChatStore.getState();\n // Route by the event's OWN chain id — the server streams ALL of the\n // user's active chains down one connection, so each event updates its\n // own bubble. Fall back to the last-subscribed job for older backends.\n const jobId = (data && (data.id || data.chainId)) || _currentJobId;\n\n // Always process plugin_command events even when _currentJobId is null\n // (e.g., during handoff relay)\n if (event === 'plugin_command') {\n dispatchPluginCommands([data]);\n return;\n }\n if (event === 'plugin-command-ack' || event === 'pong') return;\n\n if (!jobId) return;\n\n switch (event) {\n case 'init':\n case 'job':\n case 'update': {\n const snap = data;\n const progress = snapshotToProgress(snap, jobId);\n\n if (isTerminalSnapshot(snap)) {\n // Handoff chain: root job finished but relay job is still running.\n // The backend broadcasts relay progress as synthetic updates on\n // this same subscription — stay subscribed and show running.\n if (snap.status === 'done' && (snap.isHandoff || snap.state?.handoffTransfer)) {\n updateJobProgress(jobId, { status: 'running' });\n return;\n }\n _currentJobId = null;\n if (progress.status === 'paused' && progress.pendingQuestion) {\n updateJobProgress(jobId, progress);\n } else {\n fetchTurnResult(jobId, snap);\n }\n return;\n }\n\n updateJobProgress(jobId, progress);\n break;\n }\n case 'chain_final':\n // Authoritative chain-termination event (fires once when the WHOLE\n // chain is done — handoff-forgiven). Preferred over per-job `final`.\n _currentJobId = null;\n fetchTurnResult(jobId, data);\n break;\n case 'final':\n _currentJobId = null;\n fetchTurnResult(jobId, data);\n break;\n }\n } catch (err) {\n console.warn('[adas-chat] WS parse error:', err);\n }\n };\n\n ws.onerror = () => {\n _ws = null;\n _wsReady = false;\n // Fall back to polling for current job\n if (_currentJobId) {\n const jid = _currentJobId;\n _currentJobId = null;\n pollJob(jid, token);\n }\n };\n\n ws.onclose = () => {\n _ws = null;\n _wsReady = false;\n if (_currentJobId) {\n const jid = _currentJobId;\n _currentJobId = null;\n pollJob(jid, token);\n }\n };\n\n return ws;\n}\n\nfunction streamJob(jobId: string, tenant: string, token: string | null) {\n _currentJobId = jobId;\n const ws = ensureWs(tenant, token);\n\n // If WS is already open, subscribe immediately; otherwise onopen handles it\n if (_wsReady) {\n ws.send(JSON.stringify({ type: 'subscribe', jobId }));\n }\n}\n\n/** Tear down the live connection (env/auth switch, SDK unmount). */\nexport function closeChatStream(): void {\n _currentJobId = null;\n if (_ws) {\n try { _ws.close(); } catch {}\n }\n _ws = null;\n _wsReady = false;\n}\n\n/**\n * When a job completes, fetch the conversation turn to get the formatted\n * response. Falls back to extracting from the job snapshot.\n */\nasync function fetchTurnResult(jobId: string, jobSnap: any) {\n // Clean up dedup set (cap to prevent unbounded growth)\n if (_processedPluginCmds.size > 200) _processedPluginCmds.clear();\n const { updateJobProgress } = useChatStore.getState();\n const status = normalizeJobStatus(jobSnap) === 'error' ? 'error' : 'done';\n\n // Handoff chain: if this job handed off, wait for the chain to settle by\n // polling job status — done:true only when the relay job completes.\n if (jobSnap.isHandoff || jobSnap.state?.handoffTransfer || jobSnap.result?.contentType === 'handoff') {\n try {\n for (let i = 0; i < 40; i++) { // up to ~60s\n const statusRes = await fetch(`${getCoreApiUrl()}/api/job/${jobId}/status`, {\n headers: coreHeaders(),\n });\n if (statusRes.ok) {\n const statusData = await statusRes.json();\n if (statusData.done && !statusData.isHandoff) {\n break;\n }\n updateJobProgress(jobId, { status: 'running' });\n }\n await new Promise(r => setTimeout(r, 1500));\n }\n } catch {}\n }\n\n // Fetch the most recent conversation turn. By actor rather than by jobId,\n // because handoff chains create child jobs — the real reply lives on the\n // LAST child, not on the root job we submitted.\n for (let attempt = 0; attempt < 5; attempt++) {\n try {\n const res = await fetch(`${getCoreApiUrl()}/api/conversation?limit=1`, {\n headers: coreHeaders(),\n });\n\n if (res.ok) {\n const data = await res.json();\n const items = Array.isArray(data.items) ? data.items : [];\n const turn = items[0];\n if (turn?.assistant || turn?.turn?.assistant) {\n const assistant = turn.assistant || turn.turn?.assistant;\n const text = blocksToText(assistant.blocks);\n const actions: UIAction[] | undefined =\n Array.isArray(assistant.actions) && assistant.actions.length\n ? assistant.actions\n : undefined;\n if (text) {\n updateJobProgress(jobId, {\n status,\n resultMessage: text,\n ...(actions ? { resultActions: actions } : {}),\n });\n return;\n }\n }\n }\n } catch {}\n await new Promise(r => setTimeout(r, 800));\n }\n\n // Fallback: extract from job snapshot\n const fallback =\n jobSnap.result?.message ||\n jobSnap.state?.finalReply ||\n (status === 'error' ? 'Something went wrong. Please try again.' : 'Done.');\n\n updateJobProgress(jobId, { status, resultMessage: fallback });\n}\n\n// ---------------------------------------------------------------------------\n// Polling fallback (when WS is unavailable)\n// ---------------------------------------------------------------------------\n\nasync function pollJob(jobId: string, token: string | null) {\n const { updateJobProgress } = useChatStore.getState();\n\n for (let i = 0; i < 120; i++) {\n try {\n const res = await fetch(`${getCoreApiUrl()}/api/job/${jobId}/status`, {\n headers: {\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n },\n });\n\n if (!res.ok) {\n if (res.status === 404) return;\n continue;\n }\n\n const snap = await res.json();\n const progress = snapshotToProgress(snap, jobId);\n\n // sys.askUser — same logic as the WS handler\n if (progress.status === 'paused' && progress.pendingQuestion) {\n updateJobProgress(jobId, progress);\n return; // Stop polling — waiting for user input\n }\n\n updateJobProgress(jobId, progress);\n\n if (isTerminalSnapshot(snap)) {\n fetchTurnResult(jobId, snap);\n return;\n }\n } catch {}\n\n await new Promise(r => setTimeout(r, 1500));\n }\n\n // Timeout\n updateJobProgress(jobId, {\n status: 'error',\n resultMessage: 'Job timed out. Please try again.',\n });\n}\n","/**\n * Lightweight event emitter bridging the chat job stream → plugin bridges\n * (React Native has no Node 'events' module).\n *\n * Events: 'plugin-command' (raw command), 'focus-plugin' ({ pluginId, props }),\n * 'plugin-close' ({ pluginId }), 'prepare-chat-message' ({ text }).\n */\n\ntype Listener = (data: any) => void;\n\nexport interface BufferedCommand {\n pluginId: string;\n command: string;\n args?: any;\n correlationId?: string;\n _bufferedAt: number;\n}\n\nconst RECENT_TTL_MS = 10000;\nconst RECENT_MAX = 20;\n\nexport class PluginCommandEmitter {\n private listeners = new Map<string, Set<Listener>>();\n // Ring buffer of plugin commands seen recently. Lets a plugin that mounts\n // AFTER its first command was emitted (e.g. a surface mounted by the\n // dispatcher in response to focus-plugin) still pick that command up via\n // `drainPluginCommands(pluginId)`. Self-cleans on TTL + size cap.\n private recent: BufferedCommand[] = [];\n\n on(event: string, fn: Listener): () => void {\n if (!this.listeners.has(event)) this.listeners.set(event, new Set());\n this.listeners.get(event)!.add(fn);\n return () => this.off(event, fn);\n }\n\n off(event: string, fn: Listener): void {\n this.listeners.get(event)?.delete(fn);\n }\n\n emit(event: string, data: any): void {\n if (event === 'plugin-command' && data && data.pluginId && data.command && data.command !== '__focus') {\n this.recent.push({\n pluginId: data.pluginId,\n command: data.command,\n args: data.args,\n correlationId: data.correlationId,\n _bufferedAt: Date.now(),\n });\n if (this.recent.length > RECENT_MAX) this.recent.shift();\n }\n this.listeners.get(event)?.forEach((fn) => {\n try { fn(data); } catch {}\n });\n }\n\n /** Pop and return any plugin-commands targeted at `pluginId` that were\n * emitted within the last RECENT_TTL_MS. Used by the native bridge on\n * mount so the surface-dispatcher path doesn't drop the first command. */\n drainPluginCommands(pluginId: string): BufferedCommand[] {\n const now = Date.now();\n const out: BufferedCommand[] = [];\n const remaining: BufferedCommand[] = [];\n for (const cmd of this.recent) {\n if (now - cmd._bufferedAt > RECENT_TTL_MS) continue; // expired, drop\n if (cmd.pluginId === pluginId) out.push(cmd);\n else remaining.push(cmd);\n }\n this.recent = remaining;\n return out;\n }\n}\n\n/** App-wide bus for plugin commands arriving over the chat stream. */\nexport const pluginCommandEmitter = new PluginCommandEmitter();\n","import { getConsumerAuthApiUrl, getToken, getConfig, type AdasSession } from '../config';\nimport { useChatStore, closeChatStream } from '../chat/store';\n\nexport interface TenantMembership {\n id: string;\n name: string;\n role: string | null;\n}\n\n/**\n * List the tenants the CURRENT user belongs to.\n *\n * The list is membership-scoped: for consumer JWTs the backend returns ONLY\n * tenants where the user's login identity (google/apple oauth or embed id)\n * has an active actor; builder JWTs get their user-doc memberships. It never\n * leaks the full tenant list. Returns [] when signed out or unknown.\n *\n * IT CALLS CONSUMER-AUTH, NOT CORE. This used to be\n * `apiFetch('/api/tenants/list')` against the Core API, which is the wrong\n * service: the membership a consumer JWT proves is held by consumer-auth, so\n * Core could not answer it for the case that matters — a signed-in consumer\n * asking which teams they are in. Hence the explicit fetch with the bearer\n * token rather than the shared `apiFetch` helper, which is Core-scoped.\n *\n * EVERY FAILURE PATH RETURNS [] deliberately: no token, non-OK response,\n * unparseable body, or a thrown fetch. \"I could not find out\" and \"you belong\n * to nothing\" are genuinely different, but this signature has no room to say\n * so — the caller renders a team list, and an empty one is the safe render.\n * If that distinction ever matters, widen the return type rather than\n * inventing a tenant.\n */\nexport async function listMyTenants(): Promise<TenantMembership[]> {\n const token = getToken();\n if (!token) return [];\n try {\n const res = await fetch(`${getConsumerAuthApiUrl()}/platform/consumer/my-tenants`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n const data = await res.json().catch(() => null);\n if (!res.ok || !data?.ok || !Array.isArray(data.tenants)) return [];\n return data.tenants.map((t: any) => ({\n id: t.id,\n // Fall back to the id rather than to an empty string: a nameless row in\n // a team switcher is unpickable.\n name: t.name || t.id,\n role: t.role ?? null,\n }));\n } catch {\n return [];\n }\n}\n\n/**\n * Re-scope the whole session to another tenant the user already belongs to.\n *\n * The backend treats the JWT tenant claim as the sole source of truth, so\n * switching = minting a fresh JWT for the target tenant's actor. The SDK\n * owns the call (POST consumer-auth /platform/consumer/switch-tenant, authed\n * by the current token); the HOST persists the new session via\n * `config.onSession` (the SDK never stores credentials). The chat stream is\n * then torn down and cleared so it reconnects against the new tenant.\n *\n * Requires `consumerAuthUrl` and `onSession` in the initAdas() config.\n * Throws on any non-OK response (e.g. not a member of the target tenant).\n */\nexport async function switchTenant(targetTenantId: string): Promise<AdasSession> {\n const token = getToken();\n if (!token) throw new Error('[adas] switchTenant: not signed in');\n\n const res = await fetch(`${getConsumerAuthApiUrl()}/platform/consumer/switch-tenant`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({ target_tenant: targetTenantId }),\n });\n\n const data = await res.json().catch(() => null);\n if (!res.ok || !data?.ok || !data.jwt) {\n throw new Error(data?.error || `switchTenant failed (${res.status})`);\n }\n\n const session: AdasSession = {\n jwt: data.jwt,\n tenant: data.tenant,\n actor: data.actor,\n };\n\n // Host persists the new token + active tenant BEFORE we reconnect, so the\n // fresh stream/requests pick up the target-tenant credentials.\n const onSession = getConfig().onSession;\n if (!onSession) {\n throw new Error('[adas] switchTenant: config.onSession is required to persist the new session');\n }\n await onSession(session);\n\n // Tear down the old-tenant stream and history; the chat screen reloads\n // against the new tenant on its next mount / loadConversation().\n closeChatStream();\n useChatStore.getState().clearChat();\n\n return session;\n}\n","// ADAS design tokens — shared by @ateam-ai-mobile/ui and host apps that want to match.\n\nexport const colors = {\n dark: {\n bgPrimary: '#0a0e14',\n bgSecondary: '#111820',\n bgTertiary: '#1a2230',\n bgInput: '#0d1219',\n bgHover: '#1e2a3a',\n border: 'rgba(255, 255, 255, 0.08)',\n borderHover: 'rgba(255, 255, 255, 0.15)',\n textPrimary: '#e8ecf1',\n textSecondary: '#8b95a5',\n textMuted: '#5a6577',\n accent: '#2563eb',\n accentHover: '#3b82f6',\n accentSoft: 'rgba(37, 99, 235, 0.15)',\n green: '#16a34a',\n greenSoft: 'rgba(22, 163, 74, 0.15)',\n red: '#dc2626',\n redSoft: 'rgba(220, 38, 38, 0.15)',\n amber: '#f59e0b',\n amberSoft: 'rgba(245, 158, 11, 0.15)',\n purple: '#8b5cf6',\n purpleSoft: 'rgba(139, 92, 246, 0.15)',\n // Message-specific\n userBubble: '#2563eb',\n botBubble: '#1a2230',\n botBubbleBorder: 'rgba(255, 255, 255, 0.08)',\n },\n light: {\n bgPrimary: '#ffffff',\n bgSecondary: '#f8f9fa',\n bgTertiary: '#f1f3f5',\n bgInput: '#ffffff',\n bgHover: '#e9ecef',\n border: 'rgba(0, 0, 0, 0.08)',\n borderHover: 'rgba(0, 0, 0, 0.15)',\n textPrimary: '#1a1a2e',\n textSecondary: '#6b7280',\n textMuted: '#9ca3af',\n accent: '#2563eb',\n accentHover: '#1d4ed8',\n accentSoft: 'rgba(37, 99, 235, 0.1)',\n green: '#16a34a',\n greenSoft: 'rgba(22, 163, 74, 0.1)',\n red: '#dc2626',\n redSoft: 'rgba(220, 38, 38, 0.1)',\n amber: '#f59e0b',\n amberSoft: 'rgba(245, 158, 11, 0.1)',\n purple: '#8b5cf6',\n purpleSoft: 'rgba(139, 92, 246, 0.1)',\n userBubble: '#2563eb',\n botBubble: '#f1f3f5',\n botBubbleBorder: 'rgba(0, 0, 0, 0.08)',\n },\n} as const;\n\n// Ada palette — warm, paper, personal. Used by the consumer chat experience.\nexport const ada = {\n paper: '#FAF6EE',\n paperSoft: '#F2EADC',\n paperDeep: '#EBE0CC',\n ink: '#1a1410',\n inkSoft: '#5a4d3f',\n inkMuted: '#9a8870',\n border: 'rgba(26, 20, 16, 0.08)',\n borderStrong: 'rgba(26, 20, 16, 0.16)',\n orange: '#FF7A28',\n orangeDeep: '#B8410A',\n orangeSoft: 'rgba(255, 122, 40, 0.14)',\n userBubble: '#1a1410',\n userBubbleText: '#FAF6EE',\n botBubble: '#F2EADC',\n botBubbleBorder: 'rgba(26, 20, 16, 0.06)',\n botBubbleText: '#1a1410',\n} as const;\n\nexport const spacing = {\n xs: 4,\n sm: 8,\n md: 12,\n lg: 16,\n xl: 20,\n xxl: 24,\n xxxl: 32,\n} as const;\n\nexport const fontSize = {\n xs: 11,\n sm: 13,\n md: 15,\n lg: 17,\n xl: 20,\n xxl: 24,\n title: 28,\n} as const;\n\nexport const borderRadius = {\n sm: 8,\n md: 12,\n lg: 16,\n xl: 20,\n full: 9999,\n} as const;\n"],"mappings":"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","names":["import_zustand","import_zustand","import_zustand","text","_ws"]}