@arbidocs/react 0.5.104 → 0.5.105
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/annotations/freeze/index.cjs +14 -14
- package/dist/annotations/freeze/index.js +3 -3
- package/dist/artifacts/index.cjs +34 -34
- package/dist/artifacts/index.js +6 -6
- package/dist/bundle-IC65W2AO.cjs +14 -0
- package/dist/{bundle-DCWPYNGD.cjs.map → bundle-IC65W2AO.cjs.map} +1 -1
- package/dist/bundle-YNOX7VIL.js +5 -0
- package/dist/{bundle-JCKKGEJE.js.map → bundle-YNOX7VIL.js.map} +1 -1
- package/dist/calls/livekit/index.cjs +44 -44
- package/dist/calls/livekit/index.js +5 -5
- package/dist/{chunk-V73GLDHD.js → chunk-4UUZ67JL.js} +3 -3
- package/dist/{chunk-V73GLDHD.js.map → chunk-4UUZ67JL.js.map} +1 -1
- package/dist/{chunk-RC42YRVA.js → chunk-AXKIOI2U.js} +3 -3
- package/dist/{chunk-RC42YRVA.js.map → chunk-AXKIOI2U.js.map} +1 -1
- package/dist/{chunk-YD25DEMR.cjs → chunk-DM4KTNKI.cjs} +24 -24
- package/dist/{chunk-YD25DEMR.cjs.map → chunk-DM4KTNKI.cjs.map} +1 -1
- package/dist/{chunk-7C77IBQZ.cjs → chunk-NDIKMOBJ.cjs} +3 -3
- package/dist/{chunk-7C77IBQZ.cjs.map → chunk-NDIKMOBJ.cjs.map} +1 -1
- package/dist/{chunk-A2XSLTIP.js → chunk-NIKPG4PW.js} +3 -3
- package/dist/{chunk-A2XSLTIP.js.map → chunk-NIKPG4PW.js.map} +1 -1
- package/dist/{chunk-7KZS22EK.js → chunk-OYPKKHXY.js} +4 -4
- package/dist/{chunk-7KZS22EK.js.map → chunk-OYPKKHXY.js.map} +1 -1
- package/dist/{chunk-B6I2HRRF.cjs → chunk-PC4O3JDV.cjs} +3 -3
- package/dist/{chunk-B6I2HRRF.cjs.map → chunk-PC4O3JDV.cjs.map} +1 -1
- package/dist/{chunk-A6HUB6PX.js → chunk-PJMCBB6E.js} +4 -4
- package/dist/{chunk-A6HUB6PX.js.map → chunk-PJMCBB6E.js.map} +1 -1
- package/dist/{chunk-LD2SJBXC.js → chunk-RKTBXAS4.js} +4 -4
- package/dist/{chunk-LD2SJBXC.js.map → chunk-RKTBXAS4.js.map} +1 -1
- package/dist/{chunk-524AFWK7.cjs → chunk-TV4QQVZM.cjs} +6 -6
- package/dist/{chunk-524AFWK7.cjs.map → chunk-TV4QQVZM.cjs.map} +1 -1
- package/dist/{chunk-GR7CZEJJ.js → chunk-UW2SGTI5.js} +3 -3
- package/dist/{chunk-GR7CZEJJ.js.map → chunk-UW2SGTI5.js.map} +1 -1
- package/dist/{chunk-YNKV4MN6.cjs → chunk-XBYVYPU5.cjs} +4 -4
- package/dist/{chunk-YNKV4MN6.cjs.map → chunk-XBYVYPU5.cjs.map} +1 -1
- package/dist/{chunk-6TUNWGRJ.cjs → chunk-XOS7ISIR.cjs} +5 -5
- package/dist/{chunk-6TUNWGRJ.cjs.map → chunk-XOS7ISIR.cjs.map} +1 -1
- package/dist/{chunk-TTHJNRLY.cjs → chunk-Z3W4XFWB.cjs} +5 -5
- package/dist/{chunk-TTHJNRLY.cjs.map → chunk-Z3W4XFWB.cjs.map} +1 -1
- package/dist/document-tools/core.cjs +99 -99
- package/dist/document-tools/core.js +5 -5
- package/dist/document-tools/index.cjs +102 -102
- package/dist/document-tools/index.js +7 -7
- package/dist/index.cjs +1519 -1519
- package/dist/index.js +17 -17
- package/dist/shell/index.cjs +39 -39
- package/dist/shell/index.js +4 -4
- package/package.json +3 -3
- package/dist/bundle-DCWPYNGD.cjs +0 -14
- package/dist/bundle-JCKKGEJE.js +0 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/account/settings.ts","../src/host/features.ts","../src/conversation-list.ts","../src/directmessages/chat-cache.ts","../src/calls/keys.ts","../src/calls/moderation.ts","../src/calls/session.ts","../src/calls/events.ts","../src/mail/offline/crypto.ts","../src/offline-store/store.ts","../src/mail/offline/store.ts","../src/mail/offline/settings.ts","../src/mail/offline/wipe.ts","../src/account/profile.ts","../src/account/client.tsx","../src/account/entitlements.ts","../src/calls/meeting-chat.ts","../src/calls/transcript-format.ts","../src/calls/in-call-chat.ts","../src/calls/recording.ts","../src/calls/recording-playback.ts","../src/calls/transcription.ts","../src/calls/recording-media.ts","../src/calls/hooks.ts","../src/calls/save-to-thread.ts","../src/calls/actions.ts","../src/calls/webinar.ts","../src/calls/read-only.ts","../src/calls/gate.ts","../src/calls/open-meeting-chat.ts"],"names":["store","listeners","notify","update","useSyncExternalStore","state","useQueryClient","useQuery","useMutation","useCallback","useMemo","chats","encryptWithChatKey","generateChatKey","key","calls","partOffsetMs","pending","encoder","decoder","decryptWithChatKey","documents","CallApiError","decryptCallTitle"],"mappings":"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","file":"chunk-A6HUB6PX.js","sourcesContent":["/**\n * The user's settings record (flags, pins, preferences, notification choices) and its writes.\n */\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { components } from '@arbidocs/client'\nimport { useArbi } from '../useArbi'\nimport { arbiQueryKeys } from '../hooks'\n\n/** A PATCH of the user settings: only the fields given change (lists are sent whole). */\nexport type UserSettingsPatch = components['schemas']['UserSettingsUpdate']\n\n/** The user's settings record (flags, pins, notification preferences). */\nexport function useUserSettings(options?: { enabled?: boolean }) {\n const arbi = useArbi()\n return useQuery({\n queryKey: arbiQueryKeys.userSettings(),\n queryFn: () => arbi.settings.get(),\n // Edited about once a month, and every write here patches the cache: no refetch on each mount.\n staleTime: 30 * 60 * 1000,\n enabled: (options?.enabled ?? true) && arbi.isLoggedIn,\n })\n}\n\n/**\n * Write some of the user settings. The cached record takes the patch straight away, so a switch\n * does not wait for the round trip; a failed write rolls it back.\n */\nexport function useUpdateUserSettings() {\n const arbi = useArbi()\n const qc = useQueryClient()\n return useMutation({\n mutationFn: (patch: UserSettingsPatch) => arbi.settings.update(patch),\n onMutate: async (patch) => {\n await qc.cancelQueries({ queryKey: arbiQueryKeys.userSettings() })\n const previous = qc.getQueryData(arbiQueryKeys.userSettings())\n qc.setQueryData(arbiQueryKeys.userSettings(), (old: object | undefined) =>\n old ? { ...old, ...patch } : old\n )\n return { previous }\n },\n onError: (_err, _patch, context) => {\n qc.setQueryData(arbiQueryKeys.userSettings(), context?.previous)\n },\n onSettled: () => qc.invalidateQueries({ queryKey: arbiQueryKeys.userSettings() }),\n })\n}\n","/**\n * Features: the parts of the product a host turns on or off for the user (`<ArbiHost config={{\n * features }}>`), read anywhere with `useFeature(name)`.\n *\n * Each listed feature says where its switch is kept: a boolean field of the user's settings record\n * (`{ userSetting: 'show_skills', default: false }`, so it follows the user to every device), or\n * this device (`{ local: store, default: false }`, for a switch the server has no field for). A\n * feature the host does not list is on, so a site that lists none offers everything.\n */\n\nimport { useCallback, useMemo, useSyncExternalStore } from 'react'\nimport { useUpdateUserSettings, useUserSettings, type UserSettingsPatch } from '../account/settings'\nimport { useArbiHost } from './config'\n\n/** The features the kits ask about; a host may list its own as well. */\nexport type ArbiFeature =\n 'workflows' | 'mail' | 'databases' | 'calls' | 'welcomeJourney' | (string & Record<never, never>)\n\n/** A boolean field of the user's settings record. */\nexport type UserSettingFlag = {\n [K in keyof UserSettingsPatch]-?: NonNullable<UserSettingsPatch[K]> extends boolean ? K : never\n}[keyof UserSettingsPatch]\n\n/** Switches kept on this device (`createLocalFeatureStore`). */\nexport interface LocalFeatureStore {\n /** The saved value; `undefined` while there is none. */\n get(feature: string): boolean | undefined\n set(feature: string, on: boolean): void\n subscribe(listener: () => void): () => void\n /** Whether the saved values have been read (an earlier build's brought in, once). */\n isHydrated(): boolean\n hydrated(): Promise<void>\n}\n\n/** Where a feature's switch is kept, and its value while none is saved. */\nexport type FeatureSource =\n | { userSetting: UserSettingFlag; default: boolean }\n | { local: LocalFeatureStore; default: boolean }\n\n/** The host's features by name. */\nexport type ArbiFeatures = Readonly<Partial<Record<ArbiFeature, FeatureSource>>>\n\n/** A feature's value and how to change it. */\nexport interface FeatureSwitch {\n on: boolean\n /** Turn it on or off (a feature the host does not list cannot be turned off). */\n set: (on: boolean) => void\n}\n\nconst ALWAYS_ON: FeatureSwitch = { on: true, set: () => {} }\n\nfunction useUnlisted(): FeatureSwitch {\n return ALWAYS_ON\n}\n\nfunction useUserSettingSwitch(source: FeatureSource): FeatureSwitch {\n const { userSetting, default: fallback } = source as Extract<\n FeatureSource,\n { userSetting: UserSettingFlag }\n >\n const { data } = useUserSettings()\n const { mutate } = useUpdateUserSettings()\n const saved = (data as Partial<Record<UserSettingFlag, boolean | null>> | undefined)?.[\n userSetting\n ]\n const set = useCallback((on: boolean) => mutate({ [userSetting]: on }), [mutate, userSetting])\n return { on: saved ?? fallback, set }\n}\n\nfunction useLocalSwitch(source: FeatureSource, feature: string): FeatureSwitch {\n const { local, default: fallback } = source as Extract<FeatureSource, { local: unknown }>\n const saved = useSyncExternalStore(local.subscribe, () => local.get(feature))\n const set = useCallback((on: boolean) => local.set(feature, on), [local, feature])\n return { on: saved ?? fallback, set }\n}\n\n/**\n * The switch of one feature. The host's configuration is one object for its life, so the hook a\n * feature's source picks is always the same one.\n */\nexport function useFeatureSwitch(feature: ArbiFeature): FeatureSwitch {\n const source = useArbiHost().features?.[feature]\n const useSource = !source\n ? useUnlisted\n : 'userSetting' in source\n ? useUserSettingSwitch\n : useLocalSwitch\n return useSource(source as FeatureSource, feature)\n}\n\n/** Whether a feature is on for the user (on when the host does not list it). */\nexport function useFeature(feature: ArbiFeature): boolean {\n return useFeatureSwitch(feature).on\n}\n\nfunction useNoSettings(): { data: undefined } {\n return { data: undefined }\n}\n\n/**\n * Whether any feature is on, for a list whose features are only known at render (a host's\n * navigation config): one hook for all of them, re-rendering when a setting or a device switch\n * changes. The same answer as `useFeature` for each.\n */\nexport function useFeatureCheck(): (feature: ArbiFeature) => boolean {\n const features = useArbiHost().features\n // Like `useFeatureSwitch`: the settings are read only for a host that keeps a feature there, and\n // its configuration is one object for its life.\n const useSettings = Object.values(features ?? {}).some(\n (source) => !!source && 'userSetting' in source\n )\n ? useUserSettings\n : useNoSettings\n const { data } = useSettings()\n const locals = useMemo(() => {\n const entries = Object.entries(features ?? {}).filter(\n (entry): entry is [string, Extract<FeatureSource, { local: LocalFeatureStore }>] =>\n !!entry[1] && 'local' in entry[1]\n )\n return {\n subscribe: (listener: () => void) => {\n const stops = [...new Set(entries.map(([, source]) => source.local))].map((store) =>\n store.subscribe(listener)\n )\n return () => stops.forEach((stop) => stop())\n },\n // A primitive snapshot, so an unrelated store write does not re-render.\n snapshot: () => entries.map(([name, source]) => `${source.local.get(name)}`).join(','),\n }\n }, [features])\n // Subscribed so a device switch re-renders; the check below reads the stores themselves.\n useSyncExternalStore(locals.subscribe, locals.snapshot, locals.snapshot)\n return (feature) => {\n const source = features?.[feature]\n if (!source) return true\n if ('local' in source) return source.local.get(feature) ?? source.default\n const saved = (data as Partial<Record<UserSettingFlag, boolean | null>> | undefined)?.[\n source.userSetting\n ]\n return saved ?? source.default\n }\n}\n\n/** A local feature's value outside React (its default while none is saved). */\nexport function readLocalFeature(features: ArbiFeatures, feature: ArbiFeature): boolean {\n const source = features[feature]\n if (!source) return true\n if ('local' in source) return source.local.get(feature) ?? source.default\n return source.default\n}\n\nexport interface LocalFeatureStoreOptions {\n /** The storage key. */\n key: string\n /** Where the values are kept. Default: `localStorage` (memory only where it cannot be used). */\n storage?: Pick<Storage, 'getItem' | 'setItem'> | null\n /**\n * Values an earlier build kept elsewhere, brought in once: on the first load that finds nothing\n * saved under `key`. The store is hydrated once they are in.\n */\n migrate?: () => Promise<Record<string, boolean> | null>\n}\n\nfunction defaultStorage(): Pick<Storage, 'getItem' | 'setItem'> | null {\n try {\n return typeof localStorage === 'undefined' ? null : localStorage\n } catch {\n return null\n }\n}\n\nfunction parseValues(raw: string | null): Record<string, boolean> | null {\n if (raw === null) return null\n try {\n const parsed: unknown = JSON.parse(raw)\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return {}\n return Object.fromEntries(\n Object.entries(parsed).filter(([, value]) => typeof value === 'boolean')\n ) as Record<string, boolean>\n } catch {\n return {}\n }\n}\n\n/** Feature switches kept on this device, read synchronously once saved. */\nexport function createLocalFeatureStore({\n key,\n storage = defaultStorage(),\n migrate,\n}: LocalFeatureStoreOptions): LocalFeatureStore {\n const listeners = new Set<() => void>()\n const read = (): string | null => {\n try {\n return storage?.getItem(key) ?? null\n } catch {\n return null\n }\n }\n const write = () => {\n try {\n storage?.setItem(key, JSON.stringify(values))\n } catch {\n // Memory only: private windows, a full quota.\n }\n }\n const notify = () => listeners.forEach((listener) => listener())\n\n const saved = parseValues(read())\n let values: Record<string, boolean> = saved ?? {}\n let hydrated = saved !== null || !migrate\n const ready: Promise<void> = hydrated\n ? Promise.resolve()\n : migrate!()\n .catch((error: unknown) => {\n console.warn(`[features] The saved ${key} could not be brought in`, error)\n return null\n })\n .then((earlier) => {\n // A switch flipped while the earlier values were read wins.\n values = { ...(earlier ?? {}), ...values }\n write()\n hydrated = true\n notify()\n })\n\n return {\n get: (feature) => values[feature],\n set(feature, on) {\n if (values[feature] === on) return\n values = { ...values, [feature]: on }\n write()\n notify()\n },\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n isHydrated: () => hydrated,\n hydrated: () => ready,\n }\n}\n","/**\n * The workspace's conversation list in the query cache (`arbiQueryKeys.conversations(ws)`).\n *\n * The list is kept current by its writers, not by re-reading it: the workspace open seeds it, every\n * write patches the rows it changed (with the server's version when the server returns one), and\n * the workspace's events invalidate it. These are the writers, for every host and kit, so that one\n * list serves the whole app.\n *\n * Conversation ids are unique across workspaces, so a row is patched (or looked up) by its id in\n * every cached list; only adding a row needs to name its workspace.\n */\n\nimport type { QueryClient, QueryKey } from '@tanstack/react-query'\nimport type { ConversationResponse } from '@arbidocs/sdk/browser'\nimport { arbiQueryKeys } from './hooks'\n\n/** Whether a query key is a workspace's conversation list (not its threads, activity or usage). */\nexport function isConversationListKey(queryKey: QueryKey): boolean {\n return queryKey.length === 3 && queryKey[0] === 'arbi' && queryKey[1] === 'conversations'\n}\n\nconst LISTS = {\n predicate: (query: { queryKey: QueryKey }) => isConversationListKey(query.queryKey),\n}\n\n/** Apply `reducer` to a workspace's cached list (an absent list counts as empty). */\nexport function patchConversationList(\n queryClient: QueryClient,\n workspaceId: string | null | undefined,\n reducer: (old: ConversationResponse[]) => ConversationResponse[]\n): void {\n if (!workspaceId) return\n const key = arbiQueryKeys.conversations(workspaceId)\n const current = queryClient.getQueryData<ConversationResponse[]>(key) ?? []\n const next = reducer(current)\n if (next !== current) queryClient.setQueryData(key, next)\n}\n\n/** Put a new conversation at the top of its workspace's list (replacing an earlier copy). */\nexport function addConversationToList(\n queryClient: QueryClient,\n workspaceId: string | null | undefined,\n conversation: ConversationResponse\n): void {\n patchConversationList(queryClient, workspaceId, (old) => [\n conversation,\n ...old.filter((c) => c.external_id !== conversation.external_id),\n ])\n}\n\n/** Change one conversation's row, wherever it is listed. */\nexport function patchConversation(\n queryClient: QueryClient,\n conversationId: string,\n update: Partial<ConversationResponse> | ((row: ConversationResponse) => ConversationResponse)\n): void {\n queryClient.setQueriesData<ConversationResponse[]>(LISTS, (old) => {\n if (!old?.some((c) => c.external_id === conversationId)) return old\n return old.map((c) =>\n c.external_id === conversationId\n ? typeof update === 'function'\n ? update(c)\n : { ...c, ...update }\n : c\n )\n })\n}\n\n/** Take a conversation out of every list. */\nexport function removeConversationFromLists(\n queryClient: QueryClient,\n conversationId: string\n): void {\n queryClient.setQueriesData<ConversationResponse[]>(LISTS, (old) =>\n old?.some((c) => c.external_id === conversationId)\n ? old.filter((c) => c.external_id !== conversationId)\n : old\n )\n}\n\n/** Read the lists again: a change the cache cannot know (the server shared a thread, a run ended). */\nexport function invalidateConversationLists(queryClient: QueryClient): Promise<void> {\n return queryClient.invalidateQueries(LISTS)\n}\n\n/** A conversation's row, from whichever cached list holds it. */\nexport function findConversation(\n queryClient: QueryClient,\n conversationId: string | null | undefined\n): ConversationResponse | undefined {\n if (!conversationId) return undefined\n for (const [, list] of queryClient.getQueriesData<ConversationResponse[]>(LISTS)) {\n const row = list?.find((c) => c.external_id === conversationId)\n if (row) return row\n }\n return undefined\n}\n\n/** Whether the conversation is shared with the workspace, from the cached lists (false when unknown). */\nexport function isConversationShared(\n queryClient: QueryClient,\n conversationId: string | null | undefined\n): boolean {\n return findConversation(queryClient, conversationId)?.is_shared ?? false\n}\n\n/**\n * Refresh what hangs off one conversation (its activity, usage, pending access): not the lists, and\n * not its threads, which the chat engine and the conversation's events keep current.\n */\nexport function invalidateConversationDetails(\n queryClient: QueryClient,\n conversationId: string\n): Promise<void> {\n return queryClient.invalidateQueries({\n predicate: ({ queryKey }) =>\n queryKey[0] === 'arbi' &&\n queryKey[1] === 'conversations' &&\n queryKey[2] === conversationId &&\n queryKey[3] !== 'threads',\n })\n}\n","/**\n * The one chat cache: my chat list (`arbiQueryKeys.chatList()`, raw — titles and last messages\n * stay ciphertext) and each open thread (`arbiQueryKeys.chatThread(id)`, decrypted). Every reader\n * (the messenger, unread badges, the DM kit, a call's chat) and every writer (a send, a delete,\n * a WebSocket event) goes through here, so one change shows everywhere at once.\n *\n * Live events: `<ArbiWebSocketProvider>` applies them itself; a host with its own socket passes each\n * chat event to `applyChatEvent`.\n */\n\nimport type { QueryClient } from '@tanstack/react-query'\nimport type { WsAgentDmStatusMessage } from '@arbidocs/client'\nimport { chats, type Arbi } from '@arbidocs/sdk/browser'\nimport { arbiQueryKeys } from '../hooks'\n\nexport type ChatResponse = chats.ChatResponse\nexport type ChatMessageResponse = chats.ChatMessageResponse\nexport type DecryptedChatMessage = chats.DecryptedChatMessage\nexport type ChatAttachmentFile = chats.ChatAttachmentFile\nexport type ChatMemberKey = chats.ChatMemberKey\n\n/** A message in an open thread; an optimistic row carries a local id until the server echoes it. */\nexport type ChatThreadMessage = DecryptedChatMessage & { optimistic?: boolean }\n\n/** An open thread's cache entry: decrypted messages, oldest first. */\nexport interface ChatThreadData {\n messages: ChatThreadMessage[]\n /** False once the oldest page has been loaded. */\n hasMore: boolean\n}\n\n/** The signed-in user's external id, or null when signed out. */\nexport function myChatId(arbi: Arbi): string | null {\n try {\n return arbi.user.identity()?.externalId ?? null\n } catch {\n return null\n }\n}\n\n// ── The chat open in a messenger ────────────────────────────────────────────\n\n/** The chat open in a messenger, if any: its new messages do not count as unread. */\nlet openChatExtId: string | null = null\nexport function setOpenChat(chatExtId: string | null): void {\n openChatExtId = chatExtId\n}\nexport function getOpenChat(): string | null {\n return openChatExtId\n}\n\n// ── Writers ─────────────────────────────────────────────────────────────────\n\nfunction byActivity(a: ChatResponse, b: ChatResponse): number {\n const at = Date.parse(a.last_message_at ?? a.created_at)\n const bt = Date.parse(b.last_message_at ?? b.created_at)\n return bt - at\n}\n\n/** My chats as the cache holds them (empty until the list has loaded). */\nexport function cachedChats(queryClient: QueryClient): ChatResponse[] {\n return queryClient.getQueryData<ChatResponse[]>(arbiQueryKeys.chatList()) ?? []\n}\n\n/** One of my chats from the cache, if the list has it. */\nexport function cachedChat(queryClient: QueryClient, chatExtId: string): ChatResponse | undefined {\n return cachedChats(queryClient).find((c) => c.external_id === chatExtId)\n}\n\n/** Put a chat (new or updated) into the list, keeping the list newest first. */\nexport function upsertChat(queryClient: QueryClient, chat: ChatResponse): void {\n queryClient.setQueryData<ChatResponse[]>(arbiQueryKeys.chatList(), (old) => {\n if (!old) return old\n const rest = old.filter((c) => c.external_id !== chat.external_id)\n return [chat, ...rest].sort(byActivity)\n })\n}\n\n/** Drop a chat I left or lost, with its thread. */\nexport function removeChat(queryClient: QueryClient, chatExtId: string): void {\n queryClient.setQueryData<ChatResponse[]>(arbiQueryKeys.chatList(), (old) =>\n old?.filter((c) => c.external_id !== chatExtId)\n )\n queryClient.removeQueries({ queryKey: arbiQueryKeys.chatThread(chatExtId) })\n}\n\n/** Append a message to a loaded thread (deduplicated, replacing its optimistic copy). */\nexport function appendToThread(\n queryClient: QueryClient,\n chatExtId: string,\n message: ChatThreadMessage,\n replaceOptimisticId?: string\n): void {\n queryClient.setQueryData<ChatThreadData>(arbiQueryKeys.chatThread(chatExtId), (old) => {\n if (!old) return old\n const without = old.messages.filter(\n (m) => m.external_id !== message.external_id && m.external_id !== replaceOptimisticId\n )\n return { ...old, messages: [...without, message] }\n })\n}\n\n/** Take a message out of a loaded thread (a failed optimistic send). */\nexport function dropFromThread(queryClient: QueryClient, chatExtId: string, messageExtId: string) {\n queryClient.setQueryData<ChatThreadData>(arbiQueryKeys.chatThread(chatExtId), (old) =>\n old ? { ...old, messages: old.messages.filter((m) => m.external_id !== messageExtId) } : old\n )\n}\n\n/** Replace a message of a loaded thread where it stands (no-op when it is not loaded). */\nfunction replaceInThread(\n queryClient: QueryClient,\n chatExtId: string,\n message: ChatThreadMessage\n): void {\n queryClient.setQueryData<ChatThreadData>(arbiQueryKeys.chatThread(chatExtId), (old) => {\n if (!old || !old.messages.some((m) => m.external_id === message.external_id)) return old\n return {\n ...old,\n messages: old.messages.map((m) => (m.external_id === message.external_id ? message : m)),\n }\n })\n}\n\n/** Swap a chat's `last_message` in the list when it is still `message`. */\nfunction patchLastMessage(\n queryClient: QueryClient,\n chatExtId: string,\n message: ChatMessageResponse\n): void {\n queryClient.setQueryData<ChatResponse[]>(arbiQueryKeys.chatList(), (old) =>\n old?.map((c) =>\n c.external_id === chatExtId && c.last_message?.external_id === message.external_id\n ? { ...c, last_message: message }\n : c\n )\n )\n}\n\n/**\n * Delete one of my messages. The server keeps it in place as a tombstone (`deleted: true`), so\n * the cache marks it the same way at once — a refetch then returns exactly what is shown. Rolled\n * back (and rethrown) when the delete fails.\n */\nexport async function deleteChatMessage(\n arbi: Arbi,\n queryClient: QueryClient,\n chatExtId: string,\n messageExtId: string\n): Promise<void> {\n const thread = queryClient.getQueryData<ChatThreadData>(arbiQueryKeys.chatThread(chatExtId))\n const before = thread?.messages.find((m) => m.external_id === messageExtId)\n const listed = cachedChat(queryClient, chatExtId)\n const wasLast = listed?.last_message?.external_id === messageExtId ? listed.last_message : null\n if (before) replaceInThread(queryClient, chatExtId, chats.chatMessageTombstone(before))\n if (wasLast) patchLastMessage(queryClient, chatExtId, chats.chatMessageTombstone(wasLast))\n try {\n await arbi.chats.deleteMessage(chatExtId, messageExtId)\n } catch (err) {\n if (before) replaceInThread(queryClient, chatExtId, before)\n if (wasLast) patchLastMessage(queryClient, chatExtId, wasLast)\n throw err\n }\n}\n\n// ── Live events ─────────────────────────────────────────────────────────────\n\n/** A message changed in place (a deleted message's tombstone): update it where it stands. */\nasync function applyChangedMessage(\n arbi: Arbi,\n queryClient: QueryClient,\n chatExtId: string,\n message: ChatMessageResponse\n): Promise<void> {\n if (message.deleted) {\n replaceInThread(queryClient, chatExtId, chats.chatMessageTombstone(message))\n patchLastMessage(queryClient, chatExtId, chats.chatMessageTombstone(message))\n return\n }\n const chat = cachedChat(queryClient, chatExtId)\n if (!chat || !queryClient.getQueryData(arbiQueryKeys.chatThread(chatExtId))) return\n try {\n replaceInThread(queryClient, chatExtId, await arbi.chats.decrypt(chat, message))\n } catch (err) {\n console.error('[chats] could not decrypt a changed message', err)\n }\n}\n\n/** `chat.message`: a new message in one of my chats. */\nexport async function applyChatMessage(\n arbi: Arbi,\n queryClient: QueryClient,\n chatExtId: string,\n message: ChatMessageResponse\n): Promise<void> {\n // Not new: a tombstone replaces the message where it stands (never unread, never the preview).\n if (message.deleted) return applyChangedMessage(arbi, queryClient, chatExtId, message)\n const me = myChatId(arbi)\n const chat = cachedChat(queryClient, chatExtId)\n if (!chat) {\n // A chat I was just added to: fetch the list fresh.\n void queryClient.invalidateQueries({ queryKey: arbiQueryKeys.chatList() })\n return\n }\n const mine = message.sender_ext_id === me\n const isOpen = openChatExtId === chatExtId\n upsertChat(queryClient, {\n ...chat,\n last_message: message,\n last_message_at: message.created_at,\n unread_count: mine ? 0 : isOpen ? chat.unread_count : chat.unread_count + 1,\n members: mine\n ? chat.members.map((m) =>\n m.user.external_id === me ? { ...m, last_read_at: message.created_at } : m\n )\n : chat.members,\n })\n if (queryClient.getQueryData(arbiQueryKeys.chatThread(chatExtId))) {\n try {\n appendToThread(queryClient, chatExtId, await arbi.chats.decrypt(chat, message))\n } catch (err) {\n console.error('[chats] could not decrypt a live message', err)\n }\n }\n}\n\n/** `chat.read`: a member's watermark moved (a read receipt). */\nexport function applyChatRead(\n arbi: Arbi,\n queryClient: QueryClient,\n chatExtId: string,\n userExtId: string,\n lastReadAt: string\n): void {\n const me = myChatId(arbi)\n queryClient.setQueryData<ChatResponse[]>(arbiQueryKeys.chatList(), (old) =>\n old?.map((c) =>\n c.external_id !== chatExtId\n ? c\n : {\n ...c,\n unread_count: userExtId === me ? 0 : c.unread_count,\n members: c.members.map((m) =>\n m.user.external_id === userExtId ? { ...m, last_read_at: lastReadAt } : m\n ),\n }\n )\n )\n}\n\n/**\n * `chat.updated`: members, keys, title or a deleted message changed. Refetch the chat; when it now\n * needs a new epoch and I am still in it, seal one. A changed `message` (a deleted message's\n * tombstone) is applied in place; without one the open thread is reloaded.\n */\nexport async function applyChatUpdated(\n arbi: Arbi,\n queryClient: QueryClient,\n chatExtId: string,\n message?: ChatMessageResponse | null\n): Promise<void> {\n if (message) await applyChangedMessage(arbi, queryClient, chatExtId, message)\n let chat: ChatResponse\n try {\n chat = await arbi.chats.get(chatExtId)\n } catch {\n // Removed from it (or it is gone).\n removeChat(queryClient, chatExtId)\n return\n }\n if (chat.rotation_required) {\n // Best effort: whoever posts next rotates first.\n chat = await arbi.chats.ready(chat).catch(() => chat)\n }\n upsertChat(queryClient, chat)\n // Members, keys or a new epoch: reload the open thread. A changed message is already in place.\n if (!message)\n void queryClient.invalidateQueries({ queryKey: arbiQueryKeys.chatThread(chatExtId) })\n}\n\n/** My own agent's progress on a message I sent it (`agent_dm_status`). */\nexport type AgentDmStatus = WsAgentDmStatusMessage\n\n/** `agent_dm_status`: remember my agent's latest progress (read by `useAgentDmStatus`). */\nexport function applyAgentDmStatus(queryClient: QueryClient, status: AgentDmStatus): void {\n if (!['queued', 'working', 'done', 'failed'].includes(status.status)) return\n queryClient.setQueryData(arbiQueryKeys.agentDmStatus(status.agent_ext_id), status)\n}\n\n/** The chat events of the server's WebSocket, as `applyChatEvent` takes them. */\nexport type ChatEvent =\n | { type: 'chat.message'; chat_ext_id: string; message: ChatMessageResponse }\n | { type: 'chat.read'; chat_ext_id: string; user_ext_id: string; last_read_at: string }\n | { type: 'chat.updated'; chat_ext_id: string; message?: ChatMessageResponse | null }\n | AgentDmStatus\n\n/** Apply one chat event of the server's WebSocket to the cache. Other messages are ignored. */\nexport async function applyChatEvent(\n arbi: Arbi,\n queryClient: QueryClient,\n event: { type: string }\n): Promise<void> {\n const e = event as ChatEvent\n if (e.type === 'chat.message')\n return applyChatMessage(arbi, queryClient, e.chat_ext_id, e.message)\n if (e.type === 'chat.read')\n return applyChatRead(arbi, queryClient, e.chat_ext_id, e.user_ext_id, e.last_read_at)\n if (e.type === 'chat.updated')\n return applyChatUpdated(arbi, queryClient, e.chat_ext_id, e.message)\n if (e.type === 'agent_dm_status') applyAgentDmStatus(queryClient, e)\n}\n","import type { QueryClient } from '@tanstack/react-query'\nimport type { calls } from '@arbidocs/sdk/browser'\nimport { arbiQueryKeys, callQueryKeys } from '../query-keys'\n\nexport { callQueryKeys }\n\n/**\n * Calls are still experimental on this deployment (`useServerCallsExperimental` outside React,\n * from the cached config): unknown reads as experimental.\n */\nexport function serverCallsExperimental(queryClient: QueryClient): boolean {\n return queryClient.getQueryData<calls.CallsConfig>(callQueryKeys.config())?.experimental !== false\n}\n\n/**\n * A call started or ended (or changed): re-read it, and the activity of the thread that logs it\n * (its log card).\n */\nexport function invalidateCall(\n queryClient: QueryClient,\n message: { call_ext_id: string; log_conversation_ext_id?: string | null }\n): void {\n void queryClient.invalidateQueries({ queryKey: callQueryKeys.call(message.call_ext_id) })\n const thread = message.log_conversation_ext_id\n if (thread) {\n void queryClient.invalidateQueries({ queryKey: arbiQueryKeys.conversationActivity(thread) })\n }\n}\n","/**\n * The host's moderation of a call, as the moderated participant sees it.\n *\n * `POST /v1/calls/{id}/participants/{identity}/mute` mutes one of my tracks server-side and tells me\n * twice: a SERVER-sent LiveKit data message on {@link CALL_MODERATION_TOPIC}\n * (`{action: \"muted\", track, by_identity, by_name}`, plaintext, no content) and WS `call.muted`\n * (`{call_ext_id, track, by_name}`). Either may arrive first (or alone); one toast is shown.\n */\n\nexport const CALL_MODERATION_TOPIC = 'arbi.moderation'\n\nexport type ModeratedTrack = 'audio' | 'video'\n\nexport interface CallMutedNotice {\n track: ModeratedTrack\n byName: string | null\n}\n\nfunction trackOf(value: unknown): ModeratedTrack | null {\n return value === 'audio' || value === 'video' ? value : null\n}\n\n/** A moderation data packet's \"muted\" notice; null when malformed or another action. */\nexport function mutedNoticeFromPacket(payload: Uint8Array): CallMutedNotice | null {\n try {\n const parsed: unknown = JSON.parse(new TextDecoder().decode(payload))\n if (!parsed || typeof parsed !== 'object') return null\n const p = parsed as { action?: unknown; track?: unknown; by_name?: unknown }\n const track = trackOf(p.track)\n if (p.action !== 'muted' || !track) return null\n return { track, byName: typeof p.by_name === 'string' ? p.by_name : null }\n } catch {\n return null\n }\n}\n\n/** The toast for a notice. */\nexport function mutedNoticeText(notice: CallMutedNotice): string {\n return notice.track === 'audio' ? 'The host muted you' : 'The host turned off your camera'\n}\n\n/** Both channels report one mute within this window; a later mute of the same track toasts again. */\nexport const MUTED_DEDUPE_MS = 5_000\n\n/**\n * A notifier that shows one toast per mute: the data message and the WS event for the same track\n * within {@link MUTED_DEDUPE_MS} collapse into one.\n */\nexport function createMutedNotifier(\n show: (text: string) => void,\n now: () => number = Date.now\n): (notice: CallMutedNotice) => void {\n const last = new Map<ModeratedTrack, number>()\n return (notice) => {\n const at = now()\n const prev = last.get(notice.track)\n if (prev !== undefined && at - prev < MUTED_DEDUPE_MS) return\n last.set(notice.track, at)\n show(mutedNoticeText(notice))\n }\n}\n\n/** A WS `call.muted` as a notice for the open room; null when malformed. */\nexport function mutedNoticeFromEvent(\n raw: unknown\n): (CallMutedNotice & { callExtId: string }) | null {\n if (!raw || typeof raw !== 'object') return null\n const r = raw as { call_ext_id?: unknown; track?: unknown; by_name?: unknown }\n const track = trackOf(r.track)\n if (typeof r.call_ext_id !== 'string' || !track) return null\n return {\n callExtId: r.call_ext_id,\n track,\n byName: typeof r.by_name === 'string' ? r.by_name : null,\n }\n}\n","/**\n * The call session: the one active call of this browser tab, the queue of incoming rings, lobby\n * knocks waiting for this user's decision, and which calls the server says are live.\n *\n * Session-only and in memory: a LiveKit token and the call key never reach storage, so a reload\n * starts empty. One per tab (a tab is in at most one call), so it is a module store read with\n * {@link useCallSession} and changed with {@link callSession}'s actions, from React and from plain\n * code (call actions, the WebSocket) alike. Signing out calls {@link resetCallSession}.\n */\n\nimport { useSyncExternalStore } from 'react'\nimport type { calls } from '@arbidocs/sdk/browser'\n\n/** The devices picked before joining (the pre-join). */\nexport interface JoinChoices {\n audioEnabled: boolean\n videoEnabled: boolean\n audioDeviceId?: string\n videoDeviceId?: string\n}\n\n/** Microphone on, camera off: an answered ring joins with these (calls are audio-first). */\nexport const DEFAULT_JOIN_CHOICES: JoinChoices = { audioEnabled: true, videoEnabled: false }\n\nexport interface ActiveCall {\n callExtId: string\n /** The call's E2EE key K (32 bytes). */\n key: Uint8Array\n linkVersion: number\n /** Display label for the dock header. */\n title: string | null\n isCreator: boolean\n /** The call's E2E chat (DM, group or meeting chat), when it has one. */\n chatExtId: string | null\n /** Thread call: the thread with the log card. */\n logConversationExtId: string | null\n /** Thread call: its meeting thread, once made. */\n meetingConversationExtId: string | null\n /** Thread call: the workspace it is locked to. */\n lockedWorkspaceExtId: string | null\n /**\n * `prejoin` shows the device check, `knocking` waits in the lobby, `connected` mounts the room.\n * A webinar's audience also has `webinar-waiting` (\"Starting soon\": not live yet, joins by itself\n * once it is) and `webinar-ended` (the host ended it).\n */\n phase: 'prejoin' | 'knocking' | 'connected' | 'webinar-waiting' | 'webinar-ended'\n tokenResponse: calls.CallTokenResponse | null\n choices: JoinChoices | null\n /** While knocking: stops waiting for admission. */\n knock: { abort(): void } | null\n /** Users to ring once we join: user ext id → K sealed to them. */\n ringOnJoin: Record<string, string> | null\n /** A personal invite link's token (`/call/<id>?i=…`): redeemed for a token, no lobby. */\n inviteToken?: string | null\n minimized: boolean\n startedAt: number\n /**\n * A webinar's stage changed under us (moved to the audience, or stepped down): the server removes\n * us from the room and we join again with a new token, so that room's disconnect is not a leave.\n */\n rejoining?: boolean\n}\n\n/** A ring waiting for accept/decline. */\nexport interface IncomingCall extends Omit<calls.CallRingingMessage, 'type'> {\n receivedAt: number\n}\n\n/** Someone in the lobby of a call this user can admit people to. */\nexport interface PendingKnock {\n callExtId: string\n knockId: string\n displayName: string\n /** The signed-in user asking (everyone in a call has an account). */\n userExtId: string\n receivedAt: number\n}\n\n/** Where a live call belongs (from `call.started`). */\nexport interface LiveCall {\n chatExtId: string | null\n logConversationExtId: string | null\n meetingConversationExtId: string | null\n}\n\nexport interface CallSessionState {\n active: ActiveCall | null\n incoming: IncomingCall[]\n knocks: PendingKnock[]\n /** Calls the server says are live (`call.started` … `call.ended`), by call ext id. */\n liveCalls: Record<string, LiveCall>\n}\n\nexport type NewActiveCall = Omit<ActiveCall, 'minimized' | 'startedAt'> & { minimized?: boolean }\n\nexport const EMPTY_CALL_SESSION: CallSessionState = {\n active: null,\n incoming: [],\n knocks: [],\n liveCalls: {},\n}\n\nlet state: CallSessionState = EMPTY_CALL_SESSION\nconst listeners = new Set<() => void>()\n\nfunction update(next: (current: CallSessionState) => CallSessionState): void {\n const changed = next(state)\n if (changed === state) return\n state = changed\n for (const listener of listeners) listener()\n}\n\n/** The call session's state and actions (usable outside React). */\nexport const callSession = {\n getState: (): CallSessionState => state,\n\n subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n\n setActiveCall(next: NewActiveCall): void {\n update((s) => ({\n ...s,\n active: { ...next, minimized: next.minimized ?? false, startedAt: Date.now() },\n // Joining a call answers any ring for it; knocks belong to the previous call.\n incoming: s.incoming.filter((ring) => ring.call_ext_id !== next.callExtId),\n knocks: s.knocks.filter((k) => k.callExtId === next.callExtId),\n }))\n },\n\n /** Patch the active call (no-op when it is a different call or none). */\n updateActiveCall(callExtId: string, patch: Partial<ActiveCall>): void {\n update((s) =>\n s.active?.callExtId === callExtId ? { ...s, active: { ...s.active, ...patch } } : s\n )\n },\n\n endActiveCall(): void {\n update((s) => {\n s.active?.knock?.abort()\n return { ...s, active: null, knocks: [] }\n })\n },\n\n setCallMinimized(minimized: boolean): void {\n update((s) => (s.active ? { ...s, active: { ...s.active, minimized } } : s))\n },\n\n /** Queue a ring. A repeat ring for the same call replaces the earlier entry. */\n enqueueIncomingCall(ring: Omit<IncomingCall, 'receivedAt'>): void {\n update((s) => {\n // Already in this call — nothing to ring.\n if (s.active?.callExtId === ring.call_ext_id) return s\n return {\n ...s,\n incoming: [\n ...s.incoming.filter((r) => r.call_ext_id !== ring.call_ext_id),\n { ...ring, receivedAt: Date.now() },\n ],\n }\n })\n },\n\n removeIncomingCall(callExtId: string): void {\n update((s) => ({ ...s, incoming: s.incoming.filter((r) => r.call_ext_id !== callExtId) }))\n },\n\n addKnock(knock: Omit<PendingKnock, 'receivedAt'>): void {\n update((s) => {\n // Only prompt for the call we are in.\n if (s.active?.callExtId !== knock.callExtId) return s\n return {\n ...s,\n knocks: [\n ...s.knocks.filter((k) => k.knockId !== knock.knockId),\n { ...knock, receivedAt: Date.now() },\n ],\n }\n })\n },\n\n removeKnock(knockId: string): void {\n update((s) => ({ ...s, knocks: s.knocks.filter((k) => k.knockId !== knockId) }))\n },\n\n markCallLive(callExtId: string, where: LiveCall): void {\n update((s) => ({ ...s, liveCalls: { ...s.liveCalls, [callExtId]: where } }))\n },\n\n /** Call ended: forget it and drop any unanswered ring / knock for it. */\n markCallEnded(callExtId: string): void {\n update((s) => {\n const liveCalls = { ...s.liveCalls }\n delete liveCalls[callExtId]\n return {\n ...s,\n liveCalls,\n incoming: s.incoming.filter((r) => r.call_ext_id !== callExtId),\n knocks: s.knocks.filter((k) => k.callExtId !== callExtId),\n }\n })\n },\n}\n\n/** Forget everything (signing out). A call in progress is left by its room unmounting. */\nexport function resetCallSession(): void {\n update(() => EMPTY_CALL_SESSION)\n}\n\n/** Read the call session in React, re-rendering when the selected value changes. */\nexport function useCallSession<T>(selector: (state: CallSessionState) => T): T {\n return useSyncExternalStore(\n callSession.subscribe,\n () => selector(state),\n () => selector(state)\n )\n}\n\n/** The id of the thread's live call, if the server reports one (a thread call). */\nexport function threadLiveCallId(s: CallSessionState, conversationExtId: string): string | null {\n const found = Object.entries(s.liveCalls).find(\n ([, c]) => c.logConversationExtId === conversationExtId\n )\n return found ? found[0] : null\n}\n\n/** Whether the server reports a live call logged in this thread. */\nexport function threadHasLiveCall(s: CallSessionState, conversationExtId: string): boolean {\n return threadLiveCallId(s, conversationExtId) !== null\n}\n\n/** The id of the chat's live call (DM, group or meeting chat), if the server reports one. */\nexport function chatLiveCallId(s: CallSessionState, chatExtId: string): string | null {\n const found = Object.entries(s.liveCalls).find(([, c]) => c.chatExtId === chatExtId)\n return found ? found[0] : null\n}\n\n/** Whether the server reports a live call in this chat (DM, group or meeting chat). */\nexport function chatHasLiveCall(s: CallSessionState, chatExtId: string): boolean {\n return chatLiveCallId(s, chatExtId) !== null\n}\n","/**\n * The calls' WebSocket events, applied in one place ({@link applyCallEvent}):\n *\n * - rings, declines, lobby knocks and live/ended calls update the call session and the cache\n * (the thread log card and chat call cards re-read their call);\n * - what only an open view cares about — recording, transcription, webinar and moderation\n * changes — is relayed to whoever listens ({@link onCallEvent}: the call room, recording cards,\n * the webinar waiting screen).\n *\n * `<ArbiWebSocketProvider>` applies them to every `call.*` message; a host with its own socket\n * passes each one to `applyCallEvent`.\n */\n\nimport type { QueryClient } from '@tanstack/react-query'\nimport { calls } from '@arbidocs/sdk/browser'\nimport { notify } from '../notifications/notify-store'\nimport { callQueryKeys, invalidateCall, serverCallsExperimental } from './keys'\nimport { mutedNoticeFromEvent, type CallMutedNotice } from './moderation'\nimport { callSession } from './session'\n\n/** What each relayed event carries to its listeners. */\nexport interface CallRoomEvents {\n 'call.recording': calls.CallRecordingState & { call_ext_id: string }\n 'call.recording_deleted': { call_ext_id: string; recording_ext_id: string }\n 'call.transcription': calls.CallTranscriptionState & { call_ext_id: string }\n 'call.webinar': calls.CallWebinarMessage\n 'call.muted': CallMutedNotice & { callExtId: string }\n}\n\ntype RoomEventType = keyof CallRoomEvents\ntype Listener = (message: never) => void\nconst listeners = new Map<RoomEventType, Set<Listener>>()\n\n/** Listen to one relayed call event while a view is open. Returns the unsubscribe. */\nexport function onCallEvent<K extends RoomEventType>(\n type: K,\n listener: (message: CallRoomEvents[K]) => void\n): () => void {\n let set = listeners.get(type)\n if (!set) listeners.set(type, (set = new Set()))\n set.add(listener as Listener)\n return () => {\n set.delete(listener as Listener)\n }\n}\n\nfunction relay<K extends RoomEventType>(type: K, message: CallRoomEvents[K] | null): void {\n if (!message) return\n for (const listener of listeners.get(type) ?? []) {\n ;(listener as (m: CallRoomEvents[K]) => void)(message)\n }\n}\n\nexport interface ApplyCallEventOptions {\n /** My user id: my own ring echoed to another tab or device of mine is not a ring. */\n me?: string | null\n /**\n * Whether this person is rung. A host that keeps calls behind a switch passes false while it\n * is off: the caller then just sees no answer, and a call link still opens. Ignored where the\n * deployment made calls a regular feature (`experimental: false`): everyone is rung there.\n */\n acceptRings?: boolean\n}\n\nconst str = (value: unknown): string | null => (typeof value === 'string' && value ? value : null)\n\n/**\n * Apply one WebSocket event. Returns false for an event that is not a call event (so a host's\n * dispatcher can go on), true otherwise.\n */\nexport function applyCallEvent(\n queryClient: QueryClient,\n event: { type: string },\n options: ApplyCallEventOptions = {}\n): boolean {\n const m = event as Record<string, unknown> & { type: string }\n const callExtId = str(m.call_ext_id)\n switch (m.type) {\n case 'call.ringing': {\n const ring = m as unknown as calls.CallRingingMessage\n const optedOut = options.acceptRings === false && serverCallsExperimental(queryClient)\n if (optedOut || !callExtId || !ring.sealed_key) return true\n if (options.me && ring.caller_ext_id === options.me) return true\n callSession.enqueueIncomingCall({\n call_ext_id: ring.call_ext_id,\n room_name: ring.room_name,\n caller_ext_id: ring.caller_ext_id,\n caller_name: ring.caller_name,\n chat_ext_id: ring.chat_ext_id ?? null,\n log_conversation_ext_id: ring.log_conversation_ext_id ?? null,\n meeting_conversation_ext_id: ring.meeting_conversation_ext_id ?? null,\n workspace_ext_id: ring.workspace_ext_id ?? null,\n context: ring.context ?? null,\n sealed_key: ring.sealed_key,\n link_version: ring.link_version,\n title: ring.title ?? null,\n })\n return true\n }\n case 'call.declined':\n if (callExtId && callSession.getState().active?.callExtId === callExtId) {\n notify.info('Call declined', { record: false, ambient: false })\n }\n return true\n case 'call.started':\n if (!callExtId) return true\n callSession.markCallLive(callExtId, {\n chatExtId: str(m.chat_ext_id),\n logConversationExtId: str(m.log_conversation_ext_id),\n meetingConversationExtId: str(m.meeting_conversation_ext_id),\n })\n invalidateCall(queryClient, {\n call_ext_id: callExtId,\n log_conversation_ext_id: str(m.log_conversation_ext_id),\n })\n return true\n case 'call.ended':\n if (!callExtId) return true\n callSession.markCallEnded(callExtId)\n invalidateCall(queryClient, {\n call_ext_id: callExtId,\n log_conversation_ext_id: str(m.log_conversation_ext_id),\n })\n return true\n case 'call.knock': {\n // Lobby: someone asks to join the call we are in (we are its creator or an invitee).\n const knock = m as unknown as calls.CallKnockMessage\n if (!callExtId) return true\n callSession.addKnock({\n callExtId,\n knockId: knock.knock_id,\n displayName: knock.display_name,\n userExtId: knock.user_ext_id,\n })\n return true\n }\n case 'call.knock_resolved': {\n // Another host answered: clear the prompt here too.\n const knockId = str(m.knock_id)\n if (knockId) callSession.removeKnock(knockId)\n return true\n }\n case 'call.recording': {\n // The call's recording cards re-read their recordings.\n if (callExtId)\n void queryClient.invalidateQueries({ queryKey: callQueryKeys.recordingsOf(callExtId) })\n const state = calls.recordingStateFrom(m)\n relay('call.recording', callExtId && state ? { ...state, call_ext_id: callExtId } : null)\n return true\n }\n case 'call.recording_updated': {\n // A stopped recording's tail landed (a part committed after the stop): its cards re-read it.\n if (callExtId)\n void queryClient.invalidateQueries({ queryKey: callQueryKeys.recordingsOf(callExtId) })\n return true\n }\n case 'call.recording_deleted': {\n if (callExtId)\n void queryClient.invalidateQueries({ queryKey: callQueryKeys.recordingsOf(callExtId) })\n const recordingExtId = str(m.recording_ext_id)\n relay(\n 'call.recording_deleted',\n callExtId && recordingExtId\n ? { call_ext_id: callExtId, recording_ext_id: recordingExtId }\n : null\n )\n return true\n }\n case 'call.transcription': {\n const state = calls.transcriptionStateFrom(m)\n relay('call.transcription', callExtId && state ? { ...state, call_ext_id: callExtId } : null)\n return true\n }\n case 'call.webinar':\n relay(\n 'call.webinar',\n callExtId && typeof m.live === 'boolean'\n ? {\n type: 'call.webinar',\n call_ext_id: callExtId,\n live: m.live,\n chat: m.chat === 'everyone' ? 'everyone' : 'panelists',\n }\n : null\n )\n return true\n case 'call.muted':\n relay('call.muted', mutedNoticeFromEvent(m))\n return true\n default:\n return false\n }\n}\n","/**\n * Encryption at rest for the offline mailbox: the offline sealer of @arbidocs/client under\n * the `arbi-offline-mail-v1` namespace. Record keys and folder buckets are blind keyed hashes, so\n * folder names (an IMAP \"Clients/Acme\") or message ids never appear in plaintext either.\n */\n\nimport { createOfflineSealer } from '@arbidocs/client'\n\n/** The device has no unlocked identity key (signed out, or session storage cleared). */\nexport class OfflineMailLockedError extends Error {\n constructor() {\n super('Sign in to open the mail saved on this device')\n this.name = 'OfflineMailLockedError'\n }\n}\n\nconst sealer = createOfflineSealer('arbi-offline-mail-v1', {\n locked: () => new OfflineMailLockedError(),\n})\n\n/** Forget the derived keys (sign-out, or the offline data was removed). */\nexport const forgetOfflineMailKeys = sealer.forget\nexport const sealBytes = sealer.sealBytes\nexport const openBytes = sealer.openBytes\nexport const sealJson = sealer.sealJson\nexport const openJson = sealer.openJson\n/** A keyed hash of `parts`, used as an IndexedDB key so ids never appear in plaintext. */\nexport const blind = sealer.blind\n","/**\n * A generic encrypted-at-rest IndexedDB store: one object store of sealed records plus a small\n * plaintext `meta` store. A record's plaintext fields are only what IndexedDB needs to find and\n * evict it (a blind key, its kind, a blind bucket, a sort time, its size, last access); the content\n * is in `data`, sealed by the caller (see ./crypto). The store itself is crypto-agnostic.\n *\n * Used by the offline mailbox (`arbi-offline-mail`) and the offline calendar (`arbi-offline-calendar`).\n */\n\nconst DB_VERSION = 1\nconst RECORDS = 'records'\nconst META = 'meta'\n\nexport interface StoredRecord<K extends string = string> {\n /** Blind key (see crypto.blind). */\n key: string\n kind: K\n /** Blind group key. */\n bucket: string\n sortAt: number\n /** Ciphertext bytes, for the storage quota. */\n size: number\n /** Last read (ms), for least-recently-used eviction. */\n lastAccess: number\n /** Sealed contents. */\n data: Uint8Array\n}\n\nexport interface RecordStore<K extends string> {\n readonly dbName: string\n putRecord(record: StoredRecord<K>): Promise<void>\n getRecord(key: string): Promise<StoredRecord<K> | undefined>\n hasRecord(key: string): Promise<boolean>\n deleteRecords(keys: string[]): Promise<void>\n recordsInBucket(bucket: string): Promise<StoredRecord<K>[]>\n recordsOfKind(kind: K): Promise<StoredRecord<K>[]>\n allRecordSizes(): Promise<Array<Pick<StoredRecord<K>, 'key' | 'kind' | 'size'>>>\n /** Keys, kinds and last access (no data), for LRU eviction. */\n allRecordAccess(): Promise<Array<Pick<StoredRecord<K>, 'key' | 'kind' | 'lastAccess'>>>\n touchRecord(key: string): Promise<void>\n getMeta<T>(key: string): Promise<T | undefined>\n setMeta(key: string, value: unknown): Promise<void>\n /** Delete the whole database. */\n deleteDatabase(): Promise<void>\n}\n\nfunction promisify<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result)\n request.onerror = () => reject(request.error)\n })\n}\n\nexport function createRecordStore<K extends string>(dbName: string): RecordStore<K> {\n let dbPromise: Promise<IDBDatabase> | null = null\n\n function openDB(): Promise<IDBDatabase> {\n if (dbPromise) return dbPromise\n let request: IDBOpenDBRequest\n try {\n request = indexedDB.open(dbName, DB_VERSION)\n } catch (error) {\n // No indexedDB here, or opening it threw (e.g. blocked storage): nothing is kept, so the\n // next call tries again.\n return Promise.reject(error)\n }\n dbPromise = new Promise((resolve, reject) => {\n request.onerror = () => {\n dbPromise = null\n reject(request.error)\n }\n request.onsuccess = () => {\n const db = request.result\n // Another tab deleting the database (sign-out) must not be blocked by this handle.\n db.onversionchange = () => {\n db.close()\n dbPromise = null\n }\n resolve(db)\n }\n request.onupgradeneeded = () => {\n const db = request.result\n if (!db.objectStoreNames.contains(RECORDS)) {\n const records = db.createObjectStore(RECORDS, { keyPath: 'key' })\n records.createIndex('bucket', 'bucket')\n records.createIndex('kind', 'kind')\n }\n if (!db.objectStoreNames.contains(META)) db.createObjectStore(META, { keyPath: 'key' })\n }\n })\n return dbPromise\n }\n\n async function run<T>(\n store: string,\n mode: IDBTransactionMode,\n fn: (s: IDBObjectStore) => IDBRequest<T>\n ): Promise<T> {\n const db = await openDB()\n return promisify(fn(db.transaction(store, mode).objectStore(store)))\n }\n\n async function scan<T>(pick: (r: StoredRecord<K>) => T): Promise<T[]> {\n const db = await openDB()\n const store = db.transaction(RECORDS, 'readonly').objectStore(RECORDS)\n return new Promise((resolve, reject) => {\n const out: T[] = []\n const cursor = store.openCursor()\n cursor.onerror = () => reject(cursor.error)\n cursor.onsuccess = () => {\n const c = cursor.result\n if (!c) return resolve(out)\n out.push(pick(c.value as StoredRecord<K>))\n c.continue()\n }\n })\n }\n\n const self: RecordStore<K> = {\n dbName,\n async putRecord(record) {\n await run(RECORDS, 'readwrite', (s) => s.put(record))\n },\n async getRecord(key) {\n return (await run(RECORDS, 'readonly', (s) => s.get(key))) as StoredRecord<K> | undefined\n },\n async hasRecord(key) {\n return (await run(RECORDS, 'readonly', (s) => s.count(key))) > 0\n },\n async deleteRecords(keys) {\n if (keys.length === 0) return\n const db = await openDB()\n const tx = db.transaction(RECORDS, 'readwrite')\n const store = tx.objectStore(RECORDS)\n for (const key of keys) store.delete(key)\n await new Promise<void>((resolve, reject) => {\n tx.oncomplete = () => resolve()\n tx.onerror = () => reject(tx.error)\n tx.onabort = () => reject(tx.error)\n })\n },\n async recordsInBucket(bucket) {\n return (await run(RECORDS, 'readonly', (s) =>\n s.index('bucket').getAll(bucket)\n )) as StoredRecord<K>[]\n },\n async recordsOfKind(kind) {\n return (await run(RECORDS, 'readonly', (s) =>\n s.index('kind').getAll(kind)\n )) as StoredRecord<K>[]\n },\n allRecordSizes() {\n return scan(({ key, kind, size }) => ({ key, kind, size }))\n },\n allRecordAccess() {\n return scan(({ key, kind, lastAccess }) => ({ key, kind, lastAccess }))\n },\n async touchRecord(key) {\n const record = await self.getRecord(key)\n if (record) await self.putRecord({ ...record, lastAccess: Date.now() })\n },\n async getMeta<T>(key: string) {\n const row = (await run(META, 'readonly', (s) => s.get(key))) as { value: T } | undefined\n return row?.value\n },\n async setMeta(key, value) {\n await run(META, 'readwrite', (s) => s.put({ key, value }))\n },\n async deleteDatabase() {\n const db = await dbPromise?.catch(() => null)\n db?.close()\n dbPromise = null\n await new Promise<void>((resolve, reject) => {\n const request = indexedDB.deleteDatabase(dbName)\n request.onsuccess = () => resolve()\n request.onerror = () => reject(request.error)\n // Another tab still holds it open: it closes on versionchange, then this completes.\n request.onblocked = () => {}\n })\n },\n }\n return self\n}\n","/**\n * The offline mailbox's IndexedDB store (the generic encrypted record store, see\n * ../../offline-store/store, under the `arbi-offline-mail` database).\n *\n * Record plaintext fields are only what IndexedDB needs to find and evict a record: a blind key, its\n * kind, a blind bucket (which folder/account it belongs to), a sort time, its size and last read.\n * Everything else — subjects, addresses, bodies, attachments, drafts, queued mail and changes — is sealed in\n * `data` with the key from ./crypto. `meta` holds non-content facts: owner, sync times and counts.\n */\n\nimport { createRecordStore, type StoredRecord as GenericRecord } from '../../offline-store/store'\n\nexport const OFFLINE_MAIL_DB = 'arbi-offline-mail'\n\nexport type RecordKind =\n | 'accounts'\n | 'policy'\n | 'folders'\n | 'header'\n | 'body'\n | 'attachment'\n | 'draft'\n | 'outbox'\n | 'change'\n\nexport type StoredRecord = GenericRecord<RecordKind>\n\nconst store = createRecordStore<RecordKind>(OFFLINE_MAIL_DB)\n\nexport const putRecord = store.putRecord\nexport const getRecord = store.getRecord\nexport const hasRecord = store.hasRecord\nexport const deleteRecords = store.deleteRecords\nexport const recordsInBucket = store.recordsInBucket\nexport const recordsOfKind = store.recordsOfKind\n/** Keys + sizes only (no data), for quota accounting without loading ciphertext. */\nexport const allRecordSizes = store.allRecordSizes\nexport const touchRecord = store.touchRecord\nexport const getMeta = store.getMeta\nexport const setMeta = store.setMeta\n/** Delete the whole database (sign-out / \"Remove offline data\"). */\nexport const deleteOfflineMailDatabase = store.deleteDatabase\n","/**\n * Per-device offline mail settings and the shared change/status signal.\n *\n * Settings are a device preference, not mail content, so they live in localStorage keyed by the\n * user (two people sharing a browser keep separate choices). The mail itself is wiped on sign-out;\n * the preference survives so the mailbox re-syncs after the user signs back in on this device\n * (decided in #2187: they hold no mail, and keeping them favours the person coming back).\n *\n * By default the last 7 days of mail are kept; the person can choose a shorter or longer window,\n * or none (\"Off\": only drafts and the Outbox stay on the device).\n *\n * `onOfflineMailChange` notifies this tab and, through a BroadcastChannel, other open tabs whenever\n * the offline store changes (sync progress, Outbox, drafts), so every Mail view stays current.\n */\n\n/** How far back mail is kept on this device. */\nexport type OfflineMailScope = '1d' | '7d' | '30d' | '90d'\n\nexport interface OfflineMailSettings {\n /** Keep recent mail on this device (the window is `scope`). Off: only drafts and the Outbox. */\n enabled: boolean\n /** How far back mail is kept. */\n scope: OfflineMailScope\n /** Attachments of one message larger than this (MB, in total) are not saved. */\n attachmentCapMb: number\n /** Total space the offline mailbox may use (MB); the least recently opened attachments go first. */\n quotaMb: number\n}\n\n/** On, for the last week: recent mail can be read and answered offline unless the person opts out. */\nexport const DEFAULT_OFFLINE_MAIL_SETTINGS: OfflineMailSettings = {\n enabled: true,\n scope: '7d',\n attachmentCapMb: 25,\n quotaMb: 1024,\n}\n\nexport const SCOPE_DAYS: Record<OfflineMailScope, number> = {\n '1d': 1,\n '7d': 7,\n '30d': 30,\n '90d': 90,\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000\n\n/** The oldest receive time (ms) kept on the device under `settings`. */\nexport function offlineWindowStart(settings: OfflineMailSettings, now = Date.now()): number {\n return now - SCOPE_DAYS[settings.scope] * DAY_MS\n}\n\n/** Whether a message received at `receivedAt` belongs on the device (an undated one always does). */\nexport function inOfflineWindow(\n settings: OfflineMailSettings,\n receivedAt: string | undefined,\n now = Date.now()\n): boolean {\n const at = receivedAt ? Date.parse(receivedAt) || 0 : 0\n return settings.enabled && (!at || at >= offlineWindowStart(settings, now))\n}\n\n/**\n * Saved choices from earlier versions: a longer window (\"Last year\", \"All mail\") becomes the\n * longest one offered now, anything unknown the default.\n */\nfunction normalize(saved: Partial<OfflineMailSettings>): OfflineMailSettings {\n const merged = { ...DEFAULT_OFFLINE_MAIL_SETTINGS, ...saved }\n const scope: string = merged.scope\n if (scope in SCOPE_DAYS) return merged\n const longer = scope === '365d' || scope === 'all'\n return { ...merged, scope: longer ? '90d' : DEFAULT_OFFLINE_MAIL_SETTINGS.scope }\n}\n\nconst STORAGE_PREFIX = 'arbi-offline-mail-settings:'\n\n/** The signed-in user whose settings apply (`null`: signed out, nothing is kept). */\nexport type OfflineMailUser = string | null | undefined\n\n/** Nobody signed in (or a restored tab whose profile isn't loaded yet): keep no mail. */\nconst SIGNED_OUT: OfflineMailSettings = { ...DEFAULT_OFFLINE_MAIL_SETTINGS, enabled: false }\n\nexport function getOfflineMailSettings(userExtId: OfflineMailUser): OfflineMailSettings {\n if (!userExtId) return SIGNED_OUT\n const key = STORAGE_PREFIX + userExtId\n try {\n const raw = localStorage.getItem(key)\n return raw\n ? normalize(JSON.parse(raw) as Partial<OfflineMailSettings>)\n : DEFAULT_OFFLINE_MAIL_SETTINGS\n } catch {\n return DEFAULT_OFFLINE_MAIL_SETTINGS\n }\n}\n\nexport function setOfflineMailSettings(\n userExtId: OfflineMailUser,\n patch: Partial<OfflineMailSettings>\n): OfflineMailSettings {\n const next = { ...getOfflineMailSettings(userExtId), ...patch }\n if (userExtId) {\n try {\n localStorage.setItem(STORAGE_PREFIX + userExtId, JSON.stringify(next))\n } catch {\n // Storage full/blocked: the choice applies for this session only.\n }\n }\n notifyOfflineMailChange('settings')\n return next\n}\n\nexport type OfflineMailChange =\n 'settings' | 'mailbox' | 'outbox' | 'drafts' | 'changes' | 'status' | 'wiped'\n\nconst listeners = new Set<(change: OfflineMailChange) => void>()\nconst CHANNEL = 'arbi-offline-mail'\nlet channel: BroadcastChannel | null = null\n\nfunction getChannel(): BroadcastChannel | null {\n if (channel || typeof BroadcastChannel === 'undefined') return channel\n channel = new BroadcastChannel(CHANNEL)\n channel.onmessage = (event: MessageEvent<OfflineMailChange>) => {\n for (const listener of listeners) listener(event.data)\n }\n return channel\n}\n\nexport function notifyOfflineMailChange(change: OfflineMailChange): void {\n for (const listener of listeners) listener(change)\n try {\n getChannel()?.postMessage(change)\n } catch {\n // Channel closed (tab unloading): other tabs pick the change up on their next read.\n }\n}\n\nexport function onOfflineMailChange(listener: (change: OfflineMailChange) => void): () => void {\n getChannel()\n listeners.add(listener)\n return () => listeners.delete(listener)\n}\n","/**\n * Removing the offline mailbox: on sign-out, on \"Remove offline data\", and when a different account\n * signs in on this device.\n */\n\nimport { forgetOfflineMailKeys } from './crypto'\nimport { deleteOfflineMailDatabase, getMeta, setMeta } from './store'\nimport { notifyOfflineMailChange } from './settings'\n\nconst OWNER_KEY = 'owner'\n\n/** Delete everything the offline mailbox stored on this device (mail, drafts, Outbox, status). */\nexport async function wipeOfflineMail(): Promise<void> {\n forgetOfflineMailKeys()\n await deleteOfflineMailDatabase()\n notifyOfflineMailChange('wiped')\n}\n\n/**\n * Bind the store to the account signing in. A store written by someone else on this device is\n * removed (it is sealed to their key, so it is unreadable anyway, but it must not take space or\n * leave their queued mail behind).\n */\nexport async function claimOfflineMail(userExtId: string): Promise<void> {\n const owner = await getMeta<string>(OWNER_KEY)\n if (owner && owner !== userExtId) await wipeOfflineMail()\n await setMeta(OWNER_KEY, userExtId)\n}\n","/**\n * Profile: the signed-in user's name and picture.\n *\n * Two layers, like the rest of the package. The bound hooks (`useProfile`, `useUpdateProfile`) go\n * through the account client in scope (`useAccountClient`: the host configuration's `account`, else\n * one on its `arbi`). The controllers (`useNameEditor`, `useAvatarCrop`) own only the form\n * state and take the save as a function, so an app that has its own API layer (ARBI-frontend)\n * binds the same `ProfileCard` / `AvatarPicker` parts without a provider.\n */\n\nimport { useCallback, useEffect, useState } from 'react'\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { LoginProfile } from '@arbidocs/sdk/browser'\nimport { arbiQueryKeys } from '../hooks'\nimport { useAccountClient } from './client'\n\n// ── View model ──────────────────────────────────────────────────────────────\n\nexport interface ProfileView {\n givenName: string\n familyName: string | null\n picture: string | null\n email: string\n /** The sign-in provider manages the email (it cannot be changed here). */\n isSso: boolean\n /** Verified tick: passed an ID check under this name, not expired. */\n isIdentityVerified?: boolean\n /** Given and family name joined; empty while the profile has no name. */\n fullName: string\n}\n\n/** The profile as the UI reads it. `fullName` is the profile name, never the email. */\nexport function toProfileView(profile: LoginProfile): ProfileView {\n return {\n ...profile,\n fullName: [profile.givenName, profile.familyName]\n .map((part) => part?.trim())\n .filter(Boolean)\n .join(' '),\n }\n}\n\n// ── SDK-bound hooks ─────────────────────────────────────────────────────────\n\n/** The signed-in user's profile; `null` when the session has none (restored without a login). */\nexport function useProfile(options?: { enabled?: boolean }) {\n const { client, enabled } = useAccountClient()\n const qc = useQueryClient()\n // The profile changes outside these hooks too (another tab, an ID check, a re-login).\n useEffect(\n () => client.onProfileChange(() => qc.setQueryData(arbiQueryKeys.profile(), client.profile())),\n [client, qc]\n )\n return useQuery({\n queryKey: arbiQueryKeys.profile(),\n queryFn: (): ProfileView | null => client.profile(),\n enabled: (options?.enabled ?? true) && enabled,\n staleTime: Infinity,\n })\n}\n\nexport interface UpdateProfileVars {\n givenName?: string\n /** `''` clears it. */\n familyName?: string\n /** A data URL or URL; `''` clears it (the backend caps it at 100 KB). */\n picture?: string\n}\n\n/**\n * Save the name and/or picture (PATCH /v1/user/profile). The profile query is updated in place and\n * the workspace list (member rows carry the name) is refreshed, so every surface follows.\n */\nexport function useUpdateProfile() {\n const { client } = useAccountClient()\n const qc = useQueryClient()\n return useMutation({\n mutationFn: async (vars: UpdateProfileVars) => {\n await client.updateProfile(vars)\n return client.profile()\n },\n onSuccess: (profile) => {\n qc.setQueryData(arbiQueryKeys.profile(), profile)\n // A rename can drop (or restore) the verified tick.\n void qc.invalidateQueries({ queryKey: arbiQueryKeys.identity() })\n return qc.invalidateQueries({ queryKey: arbiQueryKeys.workspaces() })\n },\n })\n}\n\n// ── Controllers (no SDK) ────────────────────────────────────────────────────\n\nexport interface UseNameEditorOptions {\n givenName: string | null | undefined\n familyName: string | null | undefined\n /** Persist the new name. Reject to keep the editor open (see `error`). */\n save: (name: { givenName: string; familyName: string }) => Promise<unknown> | unknown\n}\n\nexport interface NameEditor {\n isEditing: boolean\n givenName: string\n familyName: string\n onGivenNameChange: (value: string) => void\n onFamilyNameChange: (value: string) => void\n onStartEdit: () => void\n onSave: () => void\n onCancel: () => void\n isSaving: boolean\n /** The last failed save. */\n error: unknown\n}\n\n/**\n * State of the inline name editor of `ProfileCard`: start edit (seeded from the current name),\n * save (trimmed; a blank given name is not saved), cancel. Spread it into `ProfileCard`'s `name`\n * with `display`.\n */\nexport function useNameEditor({ givenName, familyName, save }: UseNameEditorOptions): NameEditor {\n const [isEditing, setIsEditing] = useState(false)\n const [given, setGiven] = useState('')\n const [family, setFamily] = useState('')\n const [isSaving, setIsSaving] = useState(false)\n const [error, setError] = useState<unknown>(null)\n\n const onStartEdit = useCallback(() => {\n setGiven(givenName ?? '')\n setFamily(familyName ?? '')\n setError(null)\n setIsEditing(true)\n }, [givenName, familyName])\n\n const onSave = useCallback(() => {\n const trimmedGiven = given.trim()\n if (!trimmedGiven) return\n setIsSaving(true)\n setError(null)\n void Promise.resolve()\n .then(() => save({ givenName: trimmedGiven, familyName: family.trim() }))\n .then(() => setIsEditing(false))\n .catch((e: unknown) => setError(e))\n .finally(() => setIsSaving(false))\n }, [given, family, save])\n\n return {\n isEditing,\n givenName: given,\n familyName: family,\n onGivenNameChange: setGiven,\n onFamilyNameChange: setFamily,\n onStartEdit,\n onSave,\n onCancel: () => setIsEditing(false),\n isSaving,\n error,\n }\n}\n\n/** Largest picture file accepted before cropping. */\nexport const MAX_AVATAR_FILE_BYTES = 5 * 1024 * 1024\n\nexport type AvatarFileProblem = 'not-image' | 'too-large' | 'unreadable'\n\nexport interface UseAvatarCropOptions {\n /** Persist the cropped picture (a data URL). */\n upload: (dataUrl: string) => Promise<unknown> | unknown\n /** Report a rejected file or a failed read; default is silent (the picker just does not open). */\n onProblem?: (problem: AvatarFileProblem) => void\n maxBytes?: number\n}\n\nexport interface AvatarCrop {\n /** Pass to `AvatarPicker.onFileChosen`. */\n onFileChosen: (file: File) => void\n /** The chosen image awaiting a crop (a data URL), else `null`. */\n pendingImage: string | null\n /** Pass to the crop dialog's `onComplete`. */\n onCropComplete: (result: { dataUrl: string }) => void\n onCropCancel: () => void\n isUploading: boolean\n}\n\n/**\n * The choose, validate, crop, upload sequence behind `AvatarPicker`: the picker hands over a file,\n * this checks it (image, size) and reads it for the crop dialog, then uploads the cropped data URL.\n */\nexport function useAvatarCrop({\n upload,\n onProblem,\n maxBytes = MAX_AVATAR_FILE_BYTES,\n}: UseAvatarCropOptions): AvatarCrop {\n const [pendingImage, setPendingImage] = useState<string | null>(null)\n const [isUploading, setIsUploading] = useState(false)\n\n const onFileChosen = useCallback(\n (file: File) => {\n if (!file.type.startsWith('image/')) return onProblem?.('not-image')\n if (file.size > maxBytes) return onProblem?.('too-large')\n const reader = new FileReader()\n reader.onload = () => setPendingImage(reader.result as string)\n reader.onerror = () => onProblem?.('unreadable')\n reader.readAsDataURL(file)\n },\n [maxBytes, onProblem]\n )\n\n const onCropComplete = useCallback(\n (result: { dataUrl: string }) => {\n setPendingImage(null)\n setIsUploading(true)\n void Promise.resolve()\n .then(() => upload(result.dataUrl))\n .catch(() => {\n // The host's `upload` reports its own failure; the picker just stops spinning.\n })\n .finally(() => setIsUploading(false))\n },\n [upload]\n )\n\n return {\n onFileChosen,\n pendingImage,\n onCropComplete,\n onCropCancel: () => setPendingImage(null),\n isUploading,\n }\n}\n","/**\n * The account client the account hooks and the settings blocks talk to.\n *\n * An {@link AccountClient} is the signed-in user's account: profile, email, password, sessions,\n * sign-out, deletion and the ID check. {@link arbiAccountClient} builds the default one from an\n * `Arbi` (used when the host configuration has no `account`). A host with its own session and API\n * client (ARBI-frontend) gives its own as the host configuration's `account`, as it does for `mail`.\n */\n\nimport { useMemo } from 'react'\nimport type { components } from '@arbidocs/client'\nimport type { Arbi } from '@arbidocs/sdk/browser'\nimport { useArbiHost } from '../host/config'\nimport { clearSealedDrafts } from '../session/drafts'\nimport { clearChatArtifacts } from '../chat/chat-artifacts'\nimport { wipeOfflineMail } from '../mail/offline/wipe'\nimport type { IdentityVerificationApi } from './identity'\nimport type { PasswordChangeVars } from './password'\nimport { toProfileView, type ProfileView, type UpdateProfileVars } from './profile'\n\nexport type AccountSession = components['schemas']['SessionInfoResponse']\n\nexport interface AccountSignOutOptions {\n /** End every session on the server first, this device's included. */\n everywhere?: boolean\n /** Why: a person's choice, or the account was just closed. */\n reason?: 'manual' | 'account-deleted'\n}\n\nexport interface AccountClient {\n /** The signed-in user's profile; `null` when there is none. */\n profile(): ProfileView | null\n /** Be told when {@link profile} changes. Returns the unsubscribe. */\n onProfileChange(listener: () => void): () => void\n /** Save the name and/or picture; the profile follows. */\n updateProfile(vars: UpdateProfileVars): Promise<void>\n /** Send the verification code to the new address (not for SSO accounts). */\n requestEmailChange(newEmail: string): Promise<void>\n /** Confirm the new address with the emailed code; the profile follows. */\n confirmEmailChange(newEmail: string, code: string): Promise<void>\n /** Change (or, in recovery mode, set) the master password. */\n changePassword(vars: PasswordChangeVars): Promise<void>\n /** Available only when the deployment permits local sign-in. */\n requestSsoDisconnectCode?(newEmail: string): Promise<void>\n disconnectSso?(vars: { newEmail: string; code?: string; password: string }): Promise<void>\n listSessions(): Promise<AccountSession[]>\n /** The id this device's session is listed under, or `null`. */\n currentSessionId(): string | null\n /** End one of the user's sessions at once (not this device's: that is {@link signOut}). */\n terminateSession(sessionId: string): Promise<void>\n /** Sign this device out of the host (and, `everywhere`, every other session first). */\n signOut(options?: AccountSignOutOptions): Promise<void>\n /** Close the account. Rejects with the backend's refusal when something blocks it. */\n requestDeletion(): Promise<unknown>\n /** The ID check behind the verified tick. */\n identity: IdentityVerificationApi\n}\n\nexport interface AccountClientContextValue {\n client: AccountClient\n /** Account reads run only while this is true (signed in). */\n enabled: boolean\n}\n\nfunction arbiProfile(arbi: Arbi): ProfileView | null {\n const profile = arbi.user.profile()\n return profile ? toProfileView(profile) : null\n}\n\n/**\n * The account client over an `Arbi`. `overrides` replace any of its calls: a host that keeps its\n * own signed-in state passes `signOut` (and `profile`) so the SDK and the host stay in step.\n */\nexport function arbiAccountClient(\n arbi: Arbi,\n overrides: Partial<AccountClient> = {}\n): AccountClient {\n return {\n profile: () => arbiProfile(arbi),\n onProfileChange: (listener) => arbi.user.onProfileChange(() => listener()),\n updateProfile: async (vars) => {\n await arbi.profile.update({\n ...(vars.givenName !== undefined && { given_name: vars.givenName.trim() }),\n ...(vars.familyName !== undefined && { family_name: vars.familyName.trim() }),\n ...(vars.picture !== undefined && { picture: vars.picture }),\n })\n },\n requestEmailChange: (newEmail) => arbi.account.requestEmailChange(newEmail),\n confirmEmailChange: async (newEmail, code) => {\n await arbi.account.confirmEmailChange(newEmail, code)\n },\n changePassword: (vars) => arbi.account.changePassword(vars),\n listSessions: () => arbi.sessions.list(),\n currentSessionId: () => arbi.sessions.currentId(),\n terminateSession: (sessionId) => arbi.sessions.terminate(sessionId),\n signOut: async ({ everywhere } = {}) => {\n try {\n if (everywhere) await arbi.sessions.logoutAll()\n else await arbi.logout()\n } finally {\n // The drafts and mail kept on the device never outlive the session.\n clearSealedDrafts()\n clearChatArtifacts()\n await wipeOfflineMail().catch(() => {})\n }\n },\n requestDeletion: () => arbi.account.requestDeletion(),\n identity: {\n status: () => arbi.identity.status(),\n purchase: () => arbi.identity.purchase(),\n startSession: () => arbi.identity.startSession(),\n refresh: () => arbi.identity.refresh(),\n acceptName: () => arbi.identity.acceptName(),\n remove: () => arbi.identity.remove(),\n },\n ...overrides,\n }\n}\n\nconst NO_ACCOUNT_ERROR = 'The account needs an <ArbiHost> with `account` or an `arbi`'\nconst refuse = () => Promise.reject(new Error(NO_ACCOUNT_ERROR))\n\nconst NO_ACCOUNT: AccountClientContextValue = {\n client: {\n profile: () => null,\n onProfileChange: () => () => {},\n updateProfile: refuse,\n requestEmailChange: refuse,\n confirmEmailChange: refuse,\n changePassword: refuse,\n listSessions: refuse,\n currentSessionId: () => null,\n terminateSession: refuse,\n signOut: refuse,\n requestDeletion: refuse,\n identity: {\n status: refuse,\n purchase: refuse,\n startSession: refuse,\n refresh: refuse,\n acceptName: refuse,\n remove: refuse,\n },\n },\n enabled: false,\n}\n\nconst ALWAYS = () => true\n\n/**\n * The account client in scope: the host configuration's `account`, else one on its `arbi` session\n * (`arbiAccountClient`).\n */\nexport function useAccountClient(): AccountClientContextValue {\n const { account, arbi } = useArbiHost()\n const fallback = useMemo(() => (arbi ? arbiAccountClient(arbi) : null), [arbi])\n // The host's hook is fixed for its configuration's life.\n const useEnabled = account?.useEnabled ?? ALWAYS\n const enabled = useEnabled()\n const client = account?.client\n const provided = useMemo(() => (client ? { client, enabled } : null), [client, enabled])\n if (provided) return provided\n // Neither: reads stay off and writes are refused rather than breaking the screen.\n if (!arbi || !fallback) return NO_ACCOUNT\n return { client: fallback, enabled: arbi.isLoggedIn }\n}\n","/**\n * Feature entitlements: native mail, the calendar and hosting calls are paid features (Pro on the hosted\n * service; internal users on a Box). Joining a call someone else hosts is never gated. The API refuses gated\n * routes with 403 and the same `reason`, so a UI hides what the API would refuse.\n */\n\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { account } from '@arbidocs/sdk/browser'\nimport { useArbiHost } from '../host/config'\nimport { arbiQueryKeys } from '../hooks'\n\nexport type FeatureEntitlements = account.FeatureEntitlements\nexport type EntitledFeature = account.EntitledFeature\nexport type FeatureRefusal = account.FeatureRefusal\n\n/**\n * The person's entitlements; `data` is undefined until known (treat that as not entitled), and\n * stays so under a host with no `arbi` (e.g. one that brings only its own mail client).\n */\nexport function useEntitlements() {\n const { arbi } = useArbiHost()\n return useQuery({\n queryKey: arbiQueryKeys.entitlements(),\n queryFn: () => arbi!.account.entitlements(),\n enabled: !!arbi?.isLoggedIn,\n // A plan change shows on the next read; the API enforces it at once either way.\n staleTime: 5 * 60 * 1000,\n })\n}\n\n/**\n * Read the entitlements again: after something that changes them (Pro bought, a plan change), so\n * the paid pages open at once instead of waiting out the cached copy.\n */\nexport function useInvalidateEntitlements(): () => Promise<void> {\n const qc = useQueryClient()\n return () => qc.invalidateQueries({ queryKey: arbiQueryKeys.entitlements() })\n}\n\n/** Whether the person may use `feature` (false while unknown, so nothing the API would refuse flashes up). */\nexport function useIsEntitled(feature: EntitledFeature): boolean {\n const { data } = useEntitlements()\n return data?.[feature] === true\n}\n\n/** Why the paid features are off, in the words a UI shows. */\nexport function entitlementRefusal(reason: FeatureRefusal | null | undefined): {\n title: string\n description: string\n} {\n switch (reason) {\n case 'external_user':\n return {\n title: 'Not available for external users',\n description:\n 'Mail, Calendar and hosting calls are for members of the firm. You can still join calls others host.',\n }\n case 'not_entitled':\n return {\n title: 'Not available on this account',\n description: 'Mail, Calendar and hosting calls are not available on this account.',\n }\n default:\n return {\n title: 'Available on Pro',\n description:\n 'Mail, Calendar and hosting calls come with a Pro plan. You can still join calls others host.',\n }\n }\n}\n","/**\n * The meeting chat of a call (ARBI `docs/meeting-chats-plan.md` §3–4).\n *\n * Every call has one place its conversation persists:\n * - an E2E `chats` row (`cht-`): the direct chat of a DM call, the group of a group call, or a\n * `meeting` chat made lazily on first use — its key is a fresh random epoch key sealed in this\n * browser to each member (creator + invitees by default), its title encrypted under it;\n * - a shared thread (`con-`) in the locked workspace of a thread call — encrypted by the server\n * with the workspace key like any thread, so @ARBI works there.\n *\n * `ensureMeetingChat` is get-or-create (`POST /v1/calls/{id}/chat`; the server serialises\n * concurrent creators, the loser gets the winner's chat).\n */\n\nimport {\n decryptCallTitle,\n encryptWithChatKey,\n generateChatKey,\n sealCallKey,\n sealChatKey,\n} from '@arbidocs/client'\nimport type { QueryClient } from '@tanstack/react-query'\nimport { chats, type Arbi, type calls } from '@arbidocs/sdk/browser'\nimport { arbiQueryKeys } from '../hooks'\nimport { cachedChats } from '../directmessages/chat-cache'\nimport { invalidateConversationLists } from '../conversation-list'\n\nexport type MeetingChatRef =\n | { kind: 'chat'; chatExtId: string }\n | { kind: 'thread'; conversationExtId: string; workspaceExtId: string }\n\n/** The meeting chat a call (or a `POST /chat` answer) already names, or null. */\nexport function meetingChatRefFrom(\n row: Pick<calls.CallResponse, 'chat_ext_id' | 'meeting_conversation_ext_id'> & {\n locked_workspace_ext_id?: string | null\n workspace_ext_id?: string | null\n }\n): MeetingChatRef | null {\n if (row.chat_ext_id) return { kind: 'chat', chatExtId: row.chat_ext_id }\n const workspaceExtId = row.workspace_ext_id ?? row.locked_workspace_ext_id\n if (row.meeting_conversation_ext_id && workspaceExtId) {\n return {\n kind: 'thread',\n conversationExtId: row.meeting_conversation_ext_id,\n workspaceExtId,\n }\n }\n return null\n}\n\n/** The keys the call row itself carries: its creator's and each invitee's. */\nexport function callMemberKeys(\n call: Pick<calls.CallResponse, 'creator_ext_id' | 'creator_encryption_public_key' | 'invitees'>\n): Map<string, string> {\n const keys = new Map<string, string>()\n if (call.creator_ext_id && call.creator_encryption_public_key) {\n keys.set(call.creator_ext_id, call.creator_encryption_public_key)\n }\n for (const i of call.invitees ?? []) {\n if (i.encryption_public_key) keys.set(i.user_ext_id, i.encryption_public_key)\n }\n return keys\n}\n\n/**\n * Public keys of the people a meeting chat is sealed to: the call row's own keys first (creator and\n * invitees — no dependence on my chat list or contacts), then, for extra members only, known chat\n * members and contacts.\n */\nasync function publicKeysFor(\n arbi: Arbi,\n queryClient: QueryClient,\n userExtIds: string[],\n known: Map<string, string>\n): Promise<Map<string, string>> {\n const wanted = new Set(userExtIds)\n const keys = new Map<string, string>([...known].filter(([id]) => wanted.has(id)))\n if (![...wanted].some((id) => !keys.has(id))) return keys\n for (const chat of cachedChats(queryClient)) {\n for (const m of chat.members) {\n if (\n wanted.has(m.user.external_id) &&\n !keys.has(m.user.external_id) &&\n m.user.encryption_public_key\n ) {\n keys.set(m.user.external_id, m.user.encryption_public_key)\n }\n }\n }\n if ([...wanted].some((id) => !keys.has(id))) {\n try {\n for (const c of await arbi.contacts.list()) {\n const u = c.user\n if (u && wanted.has(u.external_id) && u.encryption_public_key && !keys.has(u.external_id)) {\n keys.set(u.external_id, u.encryption_public_key)\n }\n }\n } catch (err) {\n console.warn('[calls] could not load contacts for the meeting chat', err)\n }\n }\n return keys\n}\n\n/**\n * The E2E body for a new meeting chat: a fresh chat key sealed to me and to each member whose\n * public key is known (the call's creator and invitees by default, with the keys the call row\n * carries; `extraMembers` beyond them also get K sealed to them, so they can always join), and the\n * call's title under the chat key. A member without a key is left out (they can be added later);\n * `members` may be empty — a chat with only me is valid.\n */\nexport async function buildMeetingChatBody(\n arbi: Arbi,\n queryClient: QueryClient,\n call: calls.CallResponse,\n callKey: Uint8Array,\n extraMembers: string[] = []\n): Promise<calls.CallChatCreate> {\n if (!arbi.isLoggedIn) throw new Error('Sign in to keep the meeting chat')\n const { externalId: me, publicKey: myPublicKey } = await arbi.chats.identity()\n const hosts = new Set<string>([\n call.creator_ext_id,\n ...(call.invitees ?? []).map((i) => i.user_ext_id),\n ])\n const memberIds = [...new Set([...hosts, ...extraMembers])].filter(\n (id) => id && id !== me && !chats.isAgentExtId(id)\n )\n const pubKeys = await publicKeysFor(arbi, queryClient, memberIds, callMemberKeys(call))\n const key = await generateChatKey()\n try {\n const members: calls.CallChatCreate['members'] = []\n for (const id of memberIds) {\n const pk = pubKeys.get(id)\n if (!pk) {\n // Nobody to seal to: they can be added to the chat later.\n console.warn('[calls] no public key for meeting-chat member', id)\n continue\n }\n members.push({\n user_ext_id: id,\n sealed_key: await sealChatKey(key, pk),\n ...(hosts.has(id) ? {} : { call_sealed_key: await sealCallKey(callKey, pk) }),\n })\n }\n const title = call.title ? await decryptCallTitle(call.title, callKey) : null\n return {\n title: await encryptWithChatKey(title?.trim() || 'Meeting', key),\n creator_sealed_key: await sealChatKey(key, myPublicKey),\n members,\n }\n } finally {\n key.fill(0)\n }\n}\n\nconst pending = new Map<string, Promise<MeetingChatRef>>()\n\n/** The call's meeting chat, made now if it has none. Concurrent callers share one request. */\nexport function ensureMeetingChat(\n arbi: Arbi,\n queryClient: QueryClient,\n callExtId: string,\n callKey: Uint8Array\n): Promise<MeetingChatRef> {\n const inFlight = pending.get(callExtId)\n if (inFlight) return inFlight\n const run = (async () => {\n const call = await arbi.calls.get(callExtId)\n const existing = meetingChatRefFrom(call)\n if (existing) return existing\n const body = call.workspace_locked\n ? undefined\n : await buildMeetingChatBody(arbi, queryClient, call, callKey)\n const answer = await arbi.calls.chat(callExtId, body)\n const ref = meetingChatRefFrom({\n ...answer,\n locked_workspace_ext_id: call.locked_workspace_ext_id,\n })\n if (!ref) throw new Error('The server did not return a meeting chat')\n // The new chat or meeting thread joins its list.\n if (ref.kind === 'chat')\n void queryClient.invalidateQueries({ queryKey: arbiQueryKeys.chatList() })\n else void invalidateConversationLists(queryClient)\n return ref\n })()\n pending.set(callExtId, run)\n // Keep a resolved ref cached for the session; forget failures so a retry can succeed.\n run.catch(() => pending.delete(callExtId))\n return run\n}\n\n/** Forget cached refs (tests; sign-out). */\nexport function clearMeetingChatCache(): void {\n pending.clear()\n}\n\n/** A call whose meeting chat is to be shown, with what is already known about it. */\nexport interface MeetingChatTarget {\n callExtId: string\n /** The call's E2E chat (DM / group / meeting), when known. */\n chatExtId?: string | null\n /** A thread call's meeting thread, when known, and its workspace. */\n meetingConversationExtId?: string | null\n workspaceExtId?: string | null\n /** K, when the caller already has it (else my sealed copy is opened). */\n callKey?: Uint8Array | null\n}\n\n/** The ref for a target whose chat is already known, or null when it must be made / looked up. */\nexport function knownMeetingChat(target: MeetingChatTarget): MeetingChatRef | null {\n if (target.chatExtId) return { kind: 'chat', chatExtId: target.chatExtId }\n if (target.meetingConversationExtId && target.workspaceExtId) {\n return {\n kind: 'thread',\n conversationExtId: target.meetingConversationExtId,\n workspaceExtId: target.workspaceExtId,\n }\n }\n return null\n}\n\n/** The target's meeting chat: the known one, else made on first use (needs K). */\nexport async function resolveMeetingChat(\n arbi: Arbi,\n queryClient: QueryClient,\n target: MeetingChatTarget\n): Promise<MeetingChatRef> {\n const known = knownMeetingChat(target)\n if (known) return known\n const key = target.callKey ?? (await arbi.calls.key(await arbi.calls.get(target.callExtId)))\n if (!key) throw new Error('You are not invited to this call')\n return ensureMeetingChat(arbi, queryClient, target.callExtId, key)\n}\n","/**\n * Reading stored call transcripts (light, so lists and message views can import it cheaply). A\n * transcript chat message's own format (`isTranscriptMessage`, `parseTranscriptContent`) is the\n * SDK's (`chats`). See `./transcription.ts` for how they are written.\n */\n\nimport { chats } from '@arbidocs/sdk/browser'\n\n/** The `transcript_segment` marker of a workspace thread message. */\nexport interface TranscriptSegmentMarker {\n segment_id: string\n speaker_ext_id: string\n started_at?: string | null\n}\n\n/**\n * Transcript messages in spoken order: by each line's `started_at` (when it was said), falling back to when it was\n * stored. Live lines are stored as they are spoken, but a recording's Transcribe stores one speaker track after\n * another, so storage order would list every line of one speaker before the next speaker's.\n */\nexport function sortTranscriptMessages<\n T extends { content?: string | null; created_at: string; decryptFailed?: boolean },\n>(messages: readonly T[]): T[] {\n const spokenAt = (m: T) => {\n const parsed = m.decryptFailed ? null : chats.parseTranscriptContent(m.content)\n const at = Date.parse(parsed?.started_at ?? m.created_at)\n return Number.isNaN(at) ? 0 : at\n }\n return messages\n .map((m, i) => ({ m, i, at: spokenAt(m) }))\n .sort((a, b) => a.at - b.at || a.i - b.i)\n .map(({ m }) => m)\n}\n\n/**\n * The `transcript_segment` marker of a thread message's `tools` (keyed by tool name, like\n * `workflow_step`), if it carries one.\n */\nexport function transcriptSegmentOf(tools: unknown): TranscriptSegmentMarker | undefined {\n if (!tools || typeof tools !== 'object') return undefined\n const record = tools as Record<string, unknown>\n const byKey = record.transcript_segment\n const candidates = byKey ? [byKey] : Object.values(record)\n for (const c of candidates) {\n if (!c || typeof c !== 'object') continue\n const t = c as Partial<TranscriptSegmentMarker> & { name?: unknown }\n if (c !== byKey && t.name !== 'transcript_segment') continue\n if (typeof t.segment_id !== 'string' || typeof t.speaker_ext_id !== 'string') continue\n return {\n segment_id: t.segment_id,\n speaker_ext_id: t.speaker_ext_id,\n started_at: typeof t.started_at === 'string' ? t.started_at : null,\n }\n }\n return undefined\n}\n\n/** One line of an exported transcript. */\nexport interface TranscriptExportLine {\n speaker: string\n /** ISO time the segment started; null when unknown. */\n at: string | null\n text: string\n}\n\nconst pad = (n: number) => String(n).padStart(2, '0')\n\n/** `hh:mm:ss` in local time, or '' for a missing/invalid time. */\nexport function clockTime(iso: string | null | undefined): string {\n if (!iso) return ''\n const d = new Date(iso)\n if (Number.isNaN(d.getTime())) return ''\n return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`\n}\n\n/**\n * A meeting transcript as ONE markdown comment: title, date, then `Speaker [hh:mm:ss]: text`\n * lines in time order (the host's \"Save transcript to thread\").\n */\nexport function formatTranscriptMarkdown(opts: {\n title: string | null | undefined\n /** The meeting's date (first line's time when omitted). */\n date?: Date | null\n lines: TranscriptExportLine[]\n}): string {\n const lines = [...opts.lines]\n .filter((l) => l.text.trim())\n .sort((a, b) => (Date.parse(a.at ?? '') || 0) - (Date.parse(b.at ?? '') || 0))\n const first = lines.find((l) => l.at)?.at\n const date = opts.date ?? (first ? new Date(first) : null)\n const heading = `## Transcript: ${opts.title?.trim() || 'Meeting'}`\n const dateLine = date\n ? `_${date.toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })}_`\n : null\n const body = lines.map((l) => {\n const time = clockTime(l.at)\n // One paragraph per line; collapse inner newlines so a line never breaks the layout.\n return `**${l.speaker}**${time ? ` [${time}]` : ''}: ${l.text.trim().replace(/\\s*\\n\\s*/g, ' ')}`\n })\n return [heading, ...(dateLine ? [dateLine] : []), ...body].join('\\n\\n')\n}\n\n/**\n * A workspace meeting thread's transcript: its `transcript_segment` comments as export lines (the\n * speaker from the members list, the text without its \"Name: \" prefix), deduped by segment id.\n */\nexport function threadTranscriptLines(\n messages: { content?: string | null; created_at?: string | null; tools?: unknown }[],\n nameOf: (speakerExtId: string) => string | null\n): TranscriptExportLine[] {\n const seen = new Set<string>()\n const lines: TranscriptExportLine[] = []\n for (const m of messages) {\n const marker = transcriptSegmentOf(m.tools)\n if (!marker || seen.has(marker.segment_id)) continue\n seen.add(marker.segment_id)\n const name = nameOf(marker.speaker_ext_id)\n lines.push({\n speaker: name || 'Former member',\n at: marker.started_at ?? m.created_at ?? null,\n text: stripSpeakerPrefix(m.content ?? '', name),\n })\n }\n return lines\n}\n\n/** A thread comment's text (\"Name: text\") without its speaker prefix. */\nexport function stripSpeakerPrefix(content: string, speakerName: string | null): string {\n if (speakerName && content.startsWith(`${speakerName}: `)) {\n return content.slice(speakerName.length + 2)\n }\n return content\n}\n\n// ── A recording's transcript supersedes the live one ────────────────────────\n\n/** The segment id of a line transcribed from a recording (`rec-<track>-…`), not live. */\nexport function isRecordingSegmentId(segmentId: string | null | undefined): boolean {\n return typeof segmentId === 'string' && segmentId.startsWith('rec-')\n}\n\n/** A recording's time window and its tracks (from `GET /v1/calls/{id}/recordings`). */\nexport interface RecordingWindow {\n trackExtIds: readonly string[]\n /** ISO start of the recording. */\n startedAt: string\n /** ISO stop of the recording; null while it records (open-ended). */\n stoppedAt: string | null\n}\n\n/** The recordings of a call as windows. */\nexport function recordingWindowsOf(\n recordings: readonly {\n started_at: string\n stopped_at?: string | null\n tracks?: readonly { external_id: string }[] | null\n }[]\n): RecordingWindow[] {\n return recordings.map((r) => ({\n trackExtIds: (r.tracks ?? []).map((t) => t.external_id),\n startedAt: r.started_at,\n stoppedAt: r.stopped_at ?? null,\n }))\n}\n\n/** What the supersede rule needs of a transcript line; null for a message that is not one. */\nexport interface TranscriptLineRef {\n segmentId: string | null | undefined\n /** ISO time the line was spoken (or stored). */\n startedAt: string | null | undefined\n}\n\n/** A transcript chat message (E2E meeting chat): its `dedupe_key` is the segment id. */\nexport function chatTranscriptRef(message: {\n kind?: string | null\n content?: string | null\n created_at: string\n dedupe_key?: string | null\n decryptFailed?: boolean\n}): TranscriptLineRef | null {\n if (!chats.isTranscriptMessage(message)) return null\n const parsed = message.decryptFailed ? null : chats.parseTranscriptContent(message.content)\n return { segmentId: message.dedupe_key, startedAt: parsed?.started_at ?? message.created_at }\n}\n\n/** A workspace meeting thread message with a `transcript_segment` marker. */\nexport function threadTranscriptRef(message: {\n created_at?: string | null\n tools?: unknown\n meta?: { tools?: unknown } | null\n}): TranscriptLineRef | null {\n const marker = transcriptSegmentOf(message.tools ?? message.meta?.tools)\n if (!marker) return null\n return { segmentId: marker.segment_id, startedAt: marker.started_at ?? message.created_at }\n}\n\n/**\n * The LIVE transcript lines a recording's transcript supersedes. Transcribing a recording stores its\n * lines (`rec-…` segment ids) beside the live ones of the same speech, so both would show. For each\n * recording that has transcript lines, the live lines spoken inside its [started_at, stopped_at]\n * window are superseded; without a recording transcript, the live lines stand.\n *\n * A `rec-` line of a recording not in `recordings` (the list did not load) still supersedes: its\n * window is then the span of those lines' own times.\n *\n * `refOf` returns null for a message that is not a transcript line (never superseded).\n */\nexport function supersededTranscriptLines<T>(\n lines: readonly T[],\n refOf: (line: T) => TranscriptLineRef | null,\n recordings: readonly RecordingWindow[] = []\n): Set<T> {\n const superseded = new Set<T>()\n const recLines: { segmentId: string; at: number }[] = []\n for (const line of lines) {\n const ref = refOf(line)\n if (ref && isRecordingSegmentId(ref.segmentId)) {\n recLines.push({ segmentId: ref.segmentId as string, at: Date.parse(ref.startedAt ?? '') })\n }\n }\n if (!recLines.length) return superseded\n\n const windows: [number, number][] = []\n const claimed = new Set<string>()\n for (const r of recordings) {\n const prefixes = r.trackExtIds.map((t) => `rec-${t}-`)\n const mine = recLines.filter((l) => prefixes.some((p) => l.segmentId.startsWith(p)))\n if (!mine.length) continue\n for (const l of mine) claimed.add(l.segmentId)\n const start = Date.parse(r.startedAt)\n const stop = r.stoppedAt ? Date.parse(r.stoppedAt) : Number.POSITIVE_INFINITY\n if (!Number.isNaN(start)) windows.push([start, Number.isNaN(stop) ? Infinity : stop])\n }\n const unclaimed = recLines.filter((l) => !claimed.has(l.segmentId) && !Number.isNaN(l.at))\n if (unclaimed.length) {\n const times = unclaimed.map((l) => l.at)\n windows.push([Math.min(...times), Math.max(...times)])\n }\n if (!windows.length) return superseded\n\n for (const line of lines) {\n const ref = refOf(line)\n if (!ref || isRecordingSegmentId(ref.segmentId)) continue\n const at = Date.parse(ref.startedAt ?? '')\n if (Number.isNaN(at)) continue\n if (windows.some(([start, stop]) => at >= start && at <= stop)) superseded.add(line)\n }\n return superseded\n}\n\n/** `lines` without the live lines a recording's transcript supersedes (see above). */\nexport function supersedeLiveTranscript<T>(\n lines: readonly T[],\n refOf: (line: T) => TranscriptLineRef | null,\n recordings: readonly RecordingWindow[] = []\n): T[] {\n const superseded = supersededTranscriptLines(lines, refOf, recordings)\n return superseded.size ? lines.filter((l) => !superseded.has(l)) : [...lines]\n}\n\n/** Whether any of these messages is a line transcribed from a recording. */\nexport function hasRecordingTranscript<T>(\n lines: readonly T[],\n refOf: (line: T) => TranscriptLineRef | null\n): boolean {\n return lines.some((l) => isRecordingSegmentId(refOf(l)?.segmentId))\n}\n","/**\n * In-call chat (ARBI `docs/meeting-chats-plan.md` §4).\n *\n * Live: each message goes over the LiveKit data channel (reliable, topic {@link CALL_CHAT_TOPIC})\n * as `base64(nonce ‖ secretbox(json, K))` — K is the call's frame key, so the SFU, which never\n * holds K, only forwards ciphertext (media E2EE does not cover data packets).\n *\n * Persisted: a signed-in member's message is also posted to the call's meeting chat\n * ({@link ensureMeetingChat}, made lazily on the first one): an E2E chat message under the chat\n * key, or a comment in the workspace meeting thread (encrypted by the server with the workspace\n * key). A workspace guest on a thread call can read the meeting chat but not post to it: the panel\n * is read-only for them.\n */\n\nimport { decryptWithChatKey, encryptWithChatKey } from '@arbidocs/client'\nimport type { QueryClient } from '@tanstack/react-query'\nimport { ArbiApiError, type Arbi } from '@arbidocs/sdk/browser'\nimport { cachedChat, type ChatResponse } from '../directmessages/chat-cache'\nimport { getUserDisplayName } from '../users/user-display'\nimport { ensureMeetingChat, meetingChatRefFrom, type MeetingChatRef } from './meeting-chat'\nimport { transcriptSegmentOf } from './transcript-format'\n\nexport const CALL_CHAT_TOPIC = 'arbi.chat'\n/** What a workspace guest sees instead of a composer in a thread call's meeting chat. */\nexport const READ_ONLY_MEETING_TEXT = 'Read only: you can’t post in this meeting chat'\n/** Longest message accepted over the data channel (well under LiveKit's reliable-packet limit). */\nexport const CALL_CHAT_MAX_CHARS = 4000\n\n/** What one data packet carries, encrypted under K. */\nexport interface CallChatPacket {\n v: 1\n /** Random id, so a sender's echo and a re-delivery deduplicate. */\n id: string\n text: string\n /** Sender clock, ms since epoch. */\n at: number\n}\n\n/** One line of the in-call chat panel. */\nexport interface CallChatLine {\n id: string\n senderIdentity: string\n senderName: string\n text: string\n at: number\n mine: boolean\n /**\n * `live`: data channel only (received from someone else, or not kept); `saving` / `saved` /\n * `unsaved`: my own message and its persistence; `history`: loaded from the meeting chat.\n */\n status: 'live' | 'saving' | 'saved' | 'unsaved' | 'history'\n}\n\nconst encoder = new TextEncoder()\nconst decoder = new TextDecoder()\n\nexport function newPacketId(): string {\n const bytes = new Uint8Array(8)\n globalThis.crypto.getRandomValues(bytes)\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\n/** Encrypt a chat packet under K for `publishData`. */\nexport async function encodeCallChatPacket(\n packet: CallChatPacket,\n callKey: Uint8Array\n): Promise<Uint8Array<ArrayBuffer>> {\n return encoder.encode(await encryptWithChatKey(JSON.stringify(packet), callKey))\n}\n\n/** Open a received packet; null when it is not ours, not under K, or malformed. */\nexport async function decodeCallChatPacket(\n payload: Uint8Array,\n callKey: Uint8Array\n): Promise<CallChatPacket | null> {\n try {\n const parsed: unknown = JSON.parse(await decryptWithChatKey(decoder.decode(payload), callKey))\n if (!parsed || typeof parsed !== 'object') return null\n const p = parsed as Partial<CallChatPacket>\n if (p.v !== 1 || typeof p.id !== 'string' || typeof p.text !== 'string') return null\n const text = p.text.slice(0, CALL_CHAT_MAX_CHARS)\n if (!text.trim()) return null\n return { v: 1, id: p.id, text, at: typeof p.at === 'number' ? p.at : Date.now() }\n } catch {\n return null\n }\n}\n\n/** One of my chats: from the cache when the list has it, else fetched. */\nexport async function chatByExtId(\n arbi: Arbi,\n queryClient: QueryClient,\n chatExtId: string\n): Promise<ChatResponse> {\n return cachedChat(queryClient, chatExtId) ?? arbi.chats.get(chatExtId)\n}\n\n/**\n * Post my message to the meeting chat, making the chat first if the call has none. Throws when it\n * cannot be kept (not a host of the call, the thread's workspace not open, …).\n */\nexport async function persistMeetingMessage(\n arbi: Arbi,\n queryClient: QueryClient,\n callExtId: string,\n callKey: Uint8Array,\n text: string\n): Promise<MeetingChatRef> {\n const ref = await ensureMeetingChat(arbi, queryClient, callExtId, callKey)\n if (ref.kind === 'chat') {\n await arbi.chats.send(await chatByExtId(arbi, queryClient, ref.chatExtId), text)\n } else {\n try {\n await arbi.conversations.postMessage({\n content: text,\n conversation_ext_id: ref.conversationExtId,\n assistant_mode: 'mentioned',\n })\n } catch (err) {\n const status = err instanceof ArbiApiError ? err.status : undefined\n const detail = err instanceof ArbiApiError ? err.detail : undefined\n throw new Error(\n `Could not save the message to the meeting thread (${status ?? '?'})${detail ? `: ${detail}` : ''}`\n )\n }\n }\n return ref\n}\n\n/** A persisted message, before it is matched against live lines. */\nexport interface HistoryMessage {\n id: string\n senderExtId: string | null\n /** Known name, or null to resolve from the room. */\n senderName: string | null\n text: string\n at: number\n}\n\nexport const HISTORY_LIMIT = 50\n\n/**\n * The meeting chat's recent messages (oldest first), decrypted, or null when the call has no\n * meeting chat yet. Never creates one.\n */\nexport async function loadMeetingHistory(\n arbi: Arbi,\n queryClient: QueryClient,\n callExtId: string\n): Promise<{ ref: MeetingChatRef; messages: HistoryMessage[] } | null> {\n const ref = meetingChatRefFrom(await arbi.calls.get(callExtId))\n if (!ref) return null\n if (ref.kind === 'chat') {\n const chat = await chatByExtId(arbi, queryClient, ref.chatExtId)\n const page = await arbi.chats.page(chat)\n const names = new Map(chat.members.map((m) => [m.user.external_id, getUserDisplayName(m.user)]))\n return {\n ref,\n messages: page.messages\n .filter((m) => m.kind === 'text' && !m.decryptFailed && m.content)\n .slice(-HISTORY_LIMIT)\n .map((m) => ({\n id: m.external_id,\n senderExtId: m.sender_ext_id ?? null,\n senderName: (m.sender_ext_id && names.get(m.sender_ext_id)) || null,\n text: m.content ?? '',\n at: Date.parse(m.created_at),\n })),\n }\n }\n // A thread with no messages yet answers 404: nothing to show.\n const data = await arbi.conversations.getThreads(ref.conversationExtId).catch(() => null)\n if (!data) return { ref, messages: [] }\n const seen = new Map<string, HistoryMessage>()\n for (const thread of data.threads) {\n for (const m of thread.history) {\n if (seen.has(m.external_id) || m.role === 'system' || !m.content) continue\n // Transcript lines are captions, not chat.\n if (transcriptSegmentOf(m.tools)) continue\n seen.set(m.external_id, {\n id: m.external_id,\n senderExtId: m.role === 'assistant' ? null : m.created_by_ext_id,\n senderName: m.role === 'assistant' ? 'ARBI' : null,\n text: m.content,\n at: Date.parse(m.created_at),\n })\n }\n }\n return {\n ref,\n messages: [...seen.values()].sort((a, b) => a.at - b.at).slice(-HISTORY_LIMIT),\n }\n}\n\n/** Live and persisted copies of one message within this window are the same message. */\nconst SAME_MESSAGE_MS = 5 * 60 * 1000\n\n/**\n * The panel's lines: history first, then live lines, minus the live lines that a member already\n * persisted (same sender, same text, close in time), ordered by time.\n */\nexport function mergeChatLines(\n history: HistoryMessage[],\n live: CallChatLine[],\n context: { myIdentity: string; nameOf(identity: string): string | null }\n): CallChatLine[] {\n const historyLines: CallChatLine[] = history.map((m) => ({\n id: `h:${m.id}`,\n senderIdentity: m.senderExtId ?? 'arbi',\n senderName: m.senderName ?? (m.senderExtId ? context.nameOf(m.senderExtId) : null) ?? 'Someone',\n text: m.text,\n at: m.at,\n mine: !!m.senderExtId && m.senderExtId === context.myIdentity,\n status: 'history',\n }))\n const used = new Set<number>()\n const liveOnly = live.filter((line) => {\n const match = history.findIndex(\n (m, i) =>\n !used.has(i) &&\n m.senderExtId === line.senderIdentity &&\n m.text === line.text &&\n Math.abs(m.at - line.at) < SAME_MESSAGE_MS\n )\n if (match < 0) return true\n used.add(match)\n return false\n })\n return [...historyLines, ...liveOnly].sort((a, b) => a.at - b.at)\n}\n","/**\n * Call recording in the browser (ARBI `docs/meeting-chats-plan.md` §6).\n *\n * Calls are end-to-end encrypted, so the server never records them: every participant's browser\n * records its OWN microphone (attribution by origin), and in \"Audio + video\" mode the host's\n * browser also records one composite of its view. The server only ever stores ciphertext.\n *\n * Keys: each track (one per speaker, plus the composite) has a random file key, wrapped under the\n * conversation key — the meeting chat's epoch key for a contacts call, the workspace key for a\n * thread call — and stored with the track (`wrapped_key`, `key_version`). Each ~30 s part is\n * SecretBox-encrypted with the file key here and uploaded like a document: presign → PUT the\n * ciphertext straight to the object store → commit (HEAD-verified server-side).\n *\n * Offsets are relative to the recording's `started_at`, so the tracks align in time.\n */\n\nimport {\n base64ToBytes,\n bytesToBase64,\n decryptFile,\n encryptFile,\n generateChatKey,\n initSodium,\n} from '@arbidocs/client'\nimport type { QueryClient } from '@tanstack/react-query'\nimport { CallApiError, calls, documents, type Arbi } from '@arbidocs/sdk/browser'\nimport { chatByExtId } from './in-call-chat'\nimport { ensureMeetingChat, meetingChatRefFrom, type MeetingChatRef } from './meeting-chat'\n\n/** Server-sent, plaintext: `{on, mode, recording_ext_id, by_identity, by_name}`. */\nexport const CALL_RECORDING_TOPIC = 'arbi.recording'\n/** Part length: every part is a standalone file of about this long. */\nexport const RECORDING_CHUNK_MS = 30_000\n/** Speaker audio: Opus ~16–24 kbps mono (~7–11 MB per speaker-hour). */\nexport const SPEAKER_AUDIO_BITS_PER_SECOND = 24_000\n/** Composite video (≤720p): ~300–600 kbps. */\nexport const COMPOSITE_VIDEO_BITS_PER_SECOND = 500_000\nexport const COMPOSITE_AUDIO_BITS_PER_SECOND = 32_000\n\n// ── Time ────────────────────────────────────────────────────────────────────\n\n/** A part's offset from the recording start, ms (never negative). */\nexport function partOffsetMs(partStartedAt: number, recordingStartedAt: string | number): number {\n const start =\n typeof recordingStartedAt === 'number' ? recordingStartedAt : Date.parse(recordingStartedAt)\n if (!Number.isFinite(start)) return 0\n return Math.max(0, Math.round(partStartedAt - start))\n}\n\n// ── Keys ────────────────────────────────────────────────────────────────────\n\n/** A fresh random 32-byte file key. */\nexport function newTrackKey(): Promise<Uint8Array> {\n return generateChatKey()\n}\n\n/** The file key under the conversation key: base64(nonce ‖ secretbox). */\nexport async function wrapTrackKey(\n fileKey: Uint8Array,\n conversationKey: Uint8Array\n): Promise<string> {\n await initSodium()\n return bytesToBase64(encryptFile(fileKey, conversationKey))\n}\n\nexport async function unwrapTrackKey(\n wrapped: string,\n conversationKey: Uint8Array\n): Promise<Uint8Array> {\n await initSodium()\n return decryptFile(base64ToBytes(wrapped), conversationKey)\n}\n\nexport async function encryptPart(bytes: Uint8Array, fileKey: Uint8Array): Promise<Uint8Array> {\n await initSodium()\n return encryptFile(bytes, fileKey)\n}\n\nexport async function decryptPart(cipher: Uint8Array, fileKey: Uint8Array): Promise<Uint8Array> {\n await initSodium()\n return decryptFile(cipher, fileKey)\n}\n\n/** The key a recording's tracks are wrapped under, and its version (null: the workspace key). */\nexport interface ConversationKey {\n key: Uint8Array\n keyVersion: number | null\n}\n\n/**\n * The conversation key of a call's recordings:\n * - a thread call: its locked workspace's key (the key direct uploads use, unwrapped here);\n * - a contacts call: the meeting chat's epoch key — the current one to record (`keyVersion`\n * omitted; the chat is made on first use, which needs the call key K), or the given version\n * to read a track.\n */\nexport async function conversationKeyForCall(\n arbi: Arbi,\n queryClient: QueryClient,\n call: Pick<\n calls.CallResponse,\n 'external_id' | 'chat_ext_id' | 'workspace_locked' | 'locked_workspace_ext_id'\n >,\n options: { callKey?: Uint8Array | null; keyVersion?: number | null } = {}\n): Promise<ConversationKey> {\n if (call.workspace_locked && call.locked_workspace_ext_id) {\n const key = await arbi.workspaces.key(call.locked_workspace_ext_id)\n return { key, keyVersion: null }\n }\n let chatExtId = call.chat_ext_id ?? null\n if (!chatExtId) {\n if (!options.callKey) throw new Error('This call has no meeting chat to keep a recording in')\n const ref = await ensureMeetingChat(arbi, queryClient, call.external_id, options.callKey)\n if (ref.kind !== 'chat') throw new Error('Expected an end-to-end meeting chat')\n chatExtId = ref.chatExtId\n }\n const chat = await arbi.chats.ready(await chatByExtId(arbi, queryClient, chatExtId))\n const ring = await arbi.chats.ring(chat)\n const version = options.keyVersion ?? ring.currentVersion\n const key = ring.keys.get(version)\n if (!key) throw new Error('No key for this recording in the meeting chat')\n return { key, keyVersion: version }\n}\n\n/** The meeting chat a call's recording transcript goes to (made on first use when K is known). */\nexport async function meetingRefForCall(\n arbi: Arbi,\n queryClient: QueryClient,\n call: calls.CallResponse,\n callKey?: Uint8Array | null\n): Promise<MeetingChatRef> {\n const ref = meetingChatRefFrom(call)\n if (ref) return ref\n if (!callKey) throw new Error('This call has no meeting chat yet')\n return ensureMeetingChat(arbi, queryClient, call.external_id, callKey)\n}\n\n// ── Upload ──────────────────────────────────────────────────────────────────\n\n/** One recorded part, as the recorder hands it over. */\nexport interface PendingPart {\n blob: Blob\n /** Wall clock, ms since epoch. */\n startedAt: number\n durationMs: number\n}\n\ntype RecordingApi = Arbi['calls']['recording']\n\nexport interface UploaderDeps {\n createTrack: RecordingApi['createTrack']\n presign: RecordingApi['presignPart']\n commit: RecordingApi['commitPart']\n getRecording: RecordingApi['get']\n put(url: string, body: Uint8Array, headers?: Record<string, string>): Promise<void>\n encrypt(bytes: Uint8Array, key: Uint8Array): Promise<Uint8Array>\n wrap(fileKey: Uint8Array, conversationKey: Uint8Array): Promise<string>\n unwrap(wrapped: string, conversationKey: Uint8Array): Promise<Uint8Array>\n newKey(): Promise<Uint8Array>\n sleep(ms: number): Promise<void>\n}\n\nconst defaultDeps = (arbi: Arbi): UploaderDeps => ({\n createTrack: (...args) => arbi.calls.recording.createTrack(...args),\n presign: (...args) => arbi.calls.recording.presignPart(...args),\n commit: (...args) => arbi.calls.recording.commitPart(...args),\n getRecording: (...args) => arbi.calls.recording.get(...args),\n put: (url, body, headers) => documents.presignedPut(url, body, undefined, headers),\n encrypt: encryptPart,\n wrap: wrapTrackKey,\n unwrap: unwrapTrackKey,\n newKey: newTrackKey,\n sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n})\n\n/** Attempts per part (and for the track) before a transient failure is given up. */\nexport const UPLOAD_ATTEMPTS = 5\nexport const UPLOAD_BACKOFF_MS = 1000\n\n/** A part number already in use: retried under the next number. */\nclass SeqTakenError extends Error {}\n\n/**\n * Network errors, 408/429, 5xx (but 507, the quota) and a commit that found no object yet\n * (409 `part_not_uploaded`: presign and PUT again — re-presigning is allowed until committed).\n */\nexport function isRetryable(err: unknown): boolean {\n if (!(err instanceof CallApiError)) return true\n if (calls.isQuotaError(err)) return false\n if (err.code === 'part_not_uploaded') return true\n const s = err.status ?? 0\n return s === 0 || s === 408 || s === 429 || (s >= 500 && s !== 507)\n}\n\n/** The recording takes no more parts: over (past its grace), gone, or not ours to add to. */\nexport function isRecordingOver(err: unknown): boolean {\n if (!(err instanceof CallApiError)) return false\n return (\n err.code === 'recording_stopped' ||\n err.code === 'composite_needs_video' ||\n err.status === 404 ||\n err.status === 410\n )\n}\n\nexport interface TrackUploaderOptions {\n /** Who uploads: the parts go through `arbi.calls.recording`. */\n arbi: Arbi\n callExtId: string\n recordingExtId: string\n kind: calls.RecordingTrackKind\n mimeType: string\n /** The conversation key to wrap the file key under (resolved once, lazily). */\n conversationKey(): Promise<ConversationKey>\n /** The recording's start (`CallRecordingState.started_at`; otherwise read from the recording). */\n recordingStartedAt?: string | null\n /** The call link version I joined with (admission, as for transcription). */\n linkVersion?: number | null\n /** The recording can take no more parts (quota): the server stopped it. */\n onStopped?(err: CallApiError): void\n /** A part could not be kept. */\n onPartLost?(err: unknown): void\n deps?: Partial<UploaderDeps>\n}\n\n/**\n * Uploads one track's parts in order: encrypt → presign → PUT → commit, retried with backoff.\n *\n * The track is created on the first part. Creation is idempotent per speaker (and for the\n * composite), so after a reconnect — or a reload — the server hands back the SAME track: its\n * existing wrapped key is unwrapped and used, and numbering resumes after its last part.\n */\nexport class TrackUploader {\n private readonly deps: UploaderDeps\n private track: { externalId: string; fileKey: Uint8Array; startedAt: string } | null = null\n private trackPromise: Promise<NonNullable<TrackUploader['track']>> | null = null\n private nextSeq = 0\n private queue: PendingPart[] = []\n private running: Promise<void> | null = null\n /** No more parts are accepted (quota, or the recording is gone). */\n stopped = false\n /** Parts committed (for tests and the UI). */\n committed = 0\n\n constructor(private readonly options: TrackUploaderOptions) {\n this.deps = { ...defaultDeps(options.arbi), ...options.deps }\n }\n\n /** Queue a part; parts upload one at a time, in order. */\n enqueue(part: PendingPart): void {\n if (this.stopped) return\n this.queue.push(part)\n this.drain()\n }\n\n /** Resolves once every queued part was uploaded (or given up). */\n async flush(): Promise<void> {\n while (this.running) await this.running\n }\n\n private drain(): void {\n if (this.running) return\n this.running = (async () => {\n try {\n while (this.queue.length && !this.stopped) {\n const part = this.queue.shift()!\n await this.uploadPart(part)\n }\n } finally {\n this.running = null\n }\n })()\n }\n\n private async withRetry<T>(label: string, run: () => Promise<T>): Promise<T> {\n let lastError: unknown\n for (let attempt = 0; attempt < UPLOAD_ATTEMPTS; attempt++) {\n try {\n return await run()\n } catch (err) {\n lastError = err\n if (!isRetryable(err) || attempt === UPLOAD_ATTEMPTS - 1) break\n console.warn(`[calls] recording ${label} failed, retrying`, err)\n await this.deps.sleep(UPLOAD_BACKOFF_MS * 2 ** attempt)\n }\n }\n throw lastError\n }\n\n private ensureTrack(): Promise<NonNullable<TrackUploader['track']>> {\n if (this.track) return Promise.resolve(this.track)\n if (!this.trackPromise) {\n this.trackPromise = this.withRetry('track', () => this.createTrack()).then(\n (track) => {\n this.track = track\n return track\n },\n (err: unknown) => {\n this.trackPromise = null\n throw err\n }\n )\n }\n return this.trackPromise\n }\n\n private async createTrack(): Promise<NonNullable<TrackUploader['track']>> {\n const { callExtId, recordingExtId, kind, mimeType } = this.options\n const conv = await this.options.conversationKey()\n const fileKey = await this.deps.newKey()\n const wrapped = await this.deps.wrap(fileKey, conv.key)\n const created: calls.RecordingTrack = await this.deps.createTrack(callExtId, recordingExtId, {\n kind,\n wrapped_key: wrapped,\n key_version: conv.keyVersion,\n mime_type: mimeType,\n ...(this.options.linkVersion ? { link_version: this.options.linkVersion } : {}),\n })\n // An existing track (reconnect): its key is the one its parts are under.\n const key =\n created.wrapped_key && created.wrapped_key !== wrapped\n ? await this.deps.unwrap(created.wrapped_key, conv.key)\n : fileKey\n let startedAt = this.options.recordingStartedAt ?? null\n let parts = created.parts\n if (!startedAt || !parts) {\n const recording: calls.CallRecording = await this.deps.getRecording(callExtId, recordingExtId)\n startedAt = startedAt ?? recording.started_at\n parts =\n parts ?? (recording.tracks ?? []).find((t) => t.external_id === created.external_id)?.parts\n }\n this.nextSeq = Math.max(this.nextSeq, nextSeqAfter(parts ?? []))\n return { externalId: created.external_id, fileKey: key, startedAt }\n }\n\n private async uploadPart(part: PendingPart): Promise<void> {\n const { callExtId, recordingExtId } = this.options\n try {\n const track = await this.ensureTrack()\n const bytes = new Uint8Array(await part.blob.arrayBuffer())\n const cipher = await this.deps.encrypt(bytes, track.fileKey)\n const offset = partOffsetMs(part.startedAt, track.startedAt)\n let seq = this.nextSeq++\n await this.withRetry('part', async () => {\n let upload\n try {\n upload = await this.deps.presign(callExtId, recordingExtId, track.externalId, {\n seq,\n offset_ms: offset,\n duration_ms: Math.max(0, Math.round(part.durationMs)),\n size_bytes: cipher.length,\n })\n } catch (err) {\n // That number is committed already (an earlier session's part): take the next one.\n if (err instanceof CallApiError && err.code === 'part_committed') {\n seq = this.nextSeq++\n throw new SeqTakenError(err.message)\n }\n throw err\n }\n await this.deps.put(upload.upload_url, cipher, upload.headers ?? undefined)\n await this.deps.commit(callExtId, recordingExtId, track.externalId, seq)\n })\n this.committed += 1\n } catch (err) {\n if (calls.isQuotaError(err)) {\n this.stopped = true\n this.queue = []\n this.options.onStopped?.(err as CallApiError)\n return\n }\n if (isRecordingOver(err)) {\n // Stopped past its grace, deleted, or a composite for an audio-only recording.\n this.stopped = true\n this.queue = []\n }\n console.warn('[calls] a recording part could not be kept', err)\n this.options.onPartLost?.(err)\n }\n }\n}\n\n/** The seq after a track's last part (0 for a new track). */\nexport function nextSeqAfter(parts: { seq: number }[]): number {\n return parts.reduce((max, p) => Math.max(max, p.seq + 1), 0)\n}\n\n/**\n * One uploader per (recording, track kind) for the whole page session, so a reconnect (the call\n * room remounting) resumes the same track and numbering instead of starting over.\n */\nconst uploaders = new Map<string, TrackUploader>()\n\nexport function trackUploaderFor(options: TrackUploaderOptions): TrackUploader {\n const key = `${options.recordingExtId}:${options.kind}`\n let uploader = uploaders.get(key)\n if (!uploader || uploader.stopped) {\n uploader = new TrackUploader(options)\n uploaders.set(key, uploader)\n }\n return uploader\n}\n\n/** Forget the uploaders (tests; sign-out). */\nexport function clearTrackUploaders(): void {\n uploaders.clear()\n}\n","/**\n * Playing and exporting a call recording in the browser (ARBI `docs/meeting-chats-plan.md` §6).\n * Everything here works on DECRYPTED part bytes; nothing leaves the browser.\n *\n * Speaker audio — mixed in sync with WebAudio: each speaker part is a standalone file placed at its\n * `offset_ms` on one AudioContext timeline. Parts are fetched, decrypted and decoded only a little\n * ahead of the playhead ({@link partsDue}) and dropped once played, so an hour-long recording never\n * sits decoded in memory.\n *\n * Composite video — the host's composite is recorded as ONE continuous WebM (MediaRecorder\n * timeslice: only the first part carries the header), so its decrypted parts concatenated in order\n * ARE the file: it plays in a plain <video> and downloads as one file. This was chosen over\n * MediaSource (codec strings and append juggling per browser) and over part-by-part playback\n * (a visible gap every 30 s) as the simplest robust option. A reconnect of the host starts a new\n * file mid-recording; {@link splitIntoFiles} cuts at each new header, so each run plays on its own.\n *\n * Download — each speaker track becomes one WAV (16 kHz mono) with its parts at their offsets, so\n * every speaker's file starts at the recording's start and they line up in any editor.\n */\n\nimport type { calls } from '@arbidocs/sdk/browser'\nimport {\n DEFAULT_SEGMENTER_CONFIG,\n INITIAL_SEGMENTER_STATE,\n flushSegmenter,\n levelFromRms,\n stepSegmenter,\n type SegmenterConfig,\n type SegmenterEvent,\n type SegmenterState,\n} from '../documents/audio-segmenter'\n\n// ── Timeline ────────────────────────────────────────────────────────────────\n\nexport interface TimelinePart {\n trackExtId: string\n seq: number\n offsetMs: number\n durationMs: number\n}\n\nexport const partKey = (p: Pick<TimelinePart, 'trackExtId' | 'seq'>) => `${p.trackExtId}:${p.seq}`\n\n/** Every part of the given tracks, in time order. */\nexport function timelineOf(\n tracks: Pick<calls.RecordingTrack, 'external_id' | 'parts'>[]\n): TimelinePart[] {\n return tracks\n .flatMap((t) =>\n (t.parts ?? []).map((p: calls.RecordingPart) => ({\n trackExtId: t.external_id,\n seq: p.seq,\n offsetMs: p.offset_ms,\n durationMs: p.duration_ms,\n }))\n )\n .sort((a, b) => a.offsetMs - b.offsetMs || a.seq - b.seq)\n}\n\n/** The recording's length: the end of its last part. */\nexport function timelineDurationMs(parts: TimelinePart[]): number {\n return parts.reduce((max, p) => Math.max(max, p.offsetMs + p.durationMs), 0)\n}\n\n/** How far ahead of the playhead parts are fetched and decoded. */\nexport const PLAYBACK_LOOKAHEAD_MS = 20_000\n\n/** Parts not yet scheduled that play within the window `[playhead, playhead + lookahead)`. */\nexport function partsDue(\n parts: TimelinePart[],\n playheadMs: number,\n lookaheadMs: number,\n scheduled: ReadonlySet<string>\n): TimelinePart[] {\n return parts.filter(\n (p) =>\n !scheduled.has(partKey(p)) &&\n p.offsetMs < playheadMs + lookaheadMs &&\n p.offsetMs + p.durationMs > playheadMs\n )\n}\n\n/**\n * When to start a decoded part on the AudioContext clock. Playback began at context time\n * `ctxStart` from recording position `seekMs`, so position `t` plays at\n * `ctxStart + (t − seekMs)/1000`. A part whose start already passed (a seek into it, or it decoded\n * late) starts now, `offset` seconds into its buffer. Null: nothing of it is left to play.\n */\nexport function schedulePart(params: {\n partOffsetMs: number\n bufferDurationS: number\n ctxStart: number\n seekMs: number\n ctxNow: number\n}): { when: number; offset: number } | null {\n const { partOffsetMs, bufferDurationS, ctxStart, seekMs, ctxNow } = params\n let when = ctxStart + (partOffsetMs - seekMs) / 1000\n let offset = 0\n if (when < ctxNow) {\n offset = ctxNow - when\n when = ctxNow\n }\n if (offset >= bufferDurationS) return null\n return { when, offset }\n}\n\n/** The recording position (ms) the AudioContext clock is at. */\nexport function playheadAt(ctxNow: number, ctxStart: number, seekMs: number): number {\n return seekMs + Math.max(0, ctxNow - ctxStart) * 1000\n}\n\n// ── Files ───────────────────────────────────────────────────────────────────\n\nconst EBML_MAGIC = [0x1a, 0x45, 0xdf, 0xa3]\nconst FTYP = [0x66, 0x74, 0x79, 0x70]\n\n/** Whether these bytes start a media file (a WebM/Matroska EBML header or an MP4 `ftyp` box). */\nexport function startsMediaFile(bytes: Uint8Array): boolean {\n if (EBML_MAGIC.every((b, i) => bytes[i] === b)) return true\n return FTYP.every((b, i) => bytes[4 + i] === b)\n}\n\n/** Consecutive parts grouped into files: a new file at each part that starts with a header. */\nexport function splitIntoFiles(parts: Uint8Array[]): Uint8Array[][] {\n const files: Uint8Array[][] = []\n for (const part of parts) {\n if (!files.length || startsMediaFile(part)) files.push([part])\n else files[files.length - 1].push(part)\n }\n return files\n}\n\n/** File extension for a recorded MIME type. */\nexport function extensionFor(mimeType: string): string {\n if (mimeType.includes('mp4')) return mimeType.startsWith('video') ? 'mp4' : 'm4a'\n if (mimeType.includes('ogg')) return 'ogg'\n return 'webm'\n}\n\n/** A safe file name piece. */\nexport function fileSafe(name: string): string {\n return (\n name\n .replace(/[^\\p{L}\\p{N} ._-]+/gu, '')\n .trim()\n .replace(/\\s+/g, '-')\n .slice(0, 60) || 'recording'\n )\n}\n\n// ── WAV ─────────────────────────────────────────────────────────────────────\n\nexport const EXPORT_SAMPLE_RATE = 16_000\n\n/** Copy decoded samples (−1…1) into a 16-bit timeline at `offsetSamples`, clipped. */\nexport function writeSamples(\n target: Int16Array,\n samples: Float32Array,\n offsetSamples: number\n): void {\n const start = Math.max(0, Math.round(offsetSamples))\n const n = Math.min(samples.length, target.length - start)\n for (let i = 0; i < n; i++) {\n const s = Math.max(-1, Math.min(1, samples[i]))\n target[start + i] = s < 0 ? s * 0x8000 : s * 0x7fff\n }\n}\n\n/** A mono 16-bit PCM WAV file. */\nexport function encodeWav(samples: Int16Array, sampleRate: number): Uint8Array<ArrayBuffer> {\n const dataBytes = samples.length * 2\n const out = new Uint8Array(44 + dataBytes)\n const view = new DataView(out.buffer)\n const ascii = (offset: number, text: string) => {\n for (let i = 0; i < text.length; i++) out[offset + i] = text.charCodeAt(i)\n }\n ascii(0, 'RIFF')\n view.setUint32(4, 36 + dataBytes, true)\n ascii(8, 'WAVE')\n ascii(12, 'fmt ')\n view.setUint32(16, 16, true)\n view.setUint16(20, 1, true) // PCM\n view.setUint16(22, 1, true) // mono\n view.setUint32(24, sampleRate, true)\n view.setUint32(28, sampleRate * 2, true)\n view.setUint16(32, 2, true)\n view.setUint16(34, 16, true)\n ascii(36, 'data')\n view.setUint32(40, dataBytes, true)\n for (let i = 0; i < samples.length; i++) view.setInt16(44 + i * 2, samples[i], true)\n return out\n}\n\n/** Bytes as a standalone ArrayBuffer-backed view (Blob/decodeAudioData want one). */\nexport function standalone(bytes: Uint8Array): Uint8Array<ArrayBuffer> {\n return new Uint8Array(bytes) as Uint8Array<ArrayBuffer>\n}\n\n/** Decoded mono samples as a 16-bit WAV. */\nexport function floatToWav(samples: Float32Array, sampleRate: number): Uint8Array<ArrayBuffer> {\n const pcm = new Int16Array(samples.length)\n writeSamples(pcm, samples, 0)\n return encodeWav(pcm, sampleRate)\n}\n\n// ── Cutting at pauses (transcription) ───────────────────────────────────────\n\n/** One piece of a part, in samples. */\nexport interface AudioPiece {\n start: number\n end: number\n}\n\n/** A piece of a whole track: absolute sample positions on the track's timeline, and its audio. */\nexport interface TrackPiece extends AudioPiece {\n samples: Float32Array\n /** Voiced time in the piece. */\n voiceMs: number\n}\n\n/** Meter frame for the segmenter (the live recorder's meter ticks every 50 ms). */\nexport const SEGMENT_FRAME_MS = 50\n/** Kept before a piece's first voiced frame, so a word's onset is not clipped. */\nexport const SEGMENT_PRE_ROLL_MS = 200\n\n/**\n * The segmenter for a RECORDED track (transcribed after the call). Nothing waits on it, so it cuts\n * only at a real turn: 1.5 s without voice (live uses 700 ms for latency). A speaker's own track is\n * silent for seconds while others talk, but a quiet syllable mid-sentence can read as a short\n * \"pause\" — the 3-person demo split \"…two hundred and fifty / thousand pounds…\" at 700 ms and the\n * cut half came back as \"$250,000\". A piece runs to 25 s before it is cut (a 25 s 16 kHz WAV is\n * ~0.8 MB, well under the 2 MB cap), and a piece needs only 300 ms of voice: a short closing line\n * (\"Perfect. Thank you both.\") is speech, not a click.\n */\nexport const RECORDING_SEGMENTER: Partial<SegmenterConfig> = {\n maxMs: 25_000,\n minVoiceMs: 300,\n silenceMs: 1_500,\n}\n/**\n * A recorded piece with too little voice is held, and joined to the next piece when that starts\n * within this gap (\"Perfect.\" … \"Thank you both.\"); otherwise it is dropped.\n */\nexport const SHORT_PIECE_MERGE_GAP_MS = 1000\n\nexport interface TrackCutter {\n /**\n * Feed the next decoded part, placed at `offsetMs` on the track's timeline (a gap before it is\n * silence; an overlap with what came before is skipped). Returns the pieces it completed.\n */\n push(offsetMs: number, samples: Float32Array): TrackPiece[]\n /** The end of the track: closes the open piece. Returns the pieces it completed. */\n finish(): TrackPiece[]\n}\n\n/**\n * Cut a whole track at its pauses, part after part, with the SAME voice-activity segmenter as live\n * transcription (P5): RMS per 50 ms frame → meter level → `stepSegmenter`. The parts are placed on\n * ONE continuous timeline by their offsets, so a sentence spoken across a part boundary stays one\n * piece. Only the open piece (≤ `maxMs`) is kept in memory, never the whole track.\n *\n * `mergeShort`: a piece with less voice than `minVoiceMs` is held and joined to the next one if it\n * starts within {@link SHORT_PIECE_MERGE_GAP_MS}, instead of being dropped at once.\n */\nexport function createTrackCutter(\n sampleRate: number,\n config: Partial<SegmenterConfig> = {},\n options: { mergeShort?: boolean } = {}\n): TrackCutter {\n const cfg = { ...DEFAULT_SEGMENTER_CONFIG, ...config }\n const frame = Math.max(1, Math.round((sampleRate * SEGMENT_FRAME_MS) / 1000))\n const preRoll = Math.round((SEGMENT_PRE_ROLL_MS / 1000) * sampleRate)\n const toSample = (ms: number) => Math.round((ms / 1000) * sampleRate)\n const toMs = (sample: number) => (sample / sampleRate) * 1000\n\n // Retained audio: `buf[0..len)` holds the absolute samples `[bufStart, bufStart + len)`.\n let buf = new Float32Array(sampleRate * 4)\n let bufStart = 0\n let len = 0\n /** Absolute index of the first sample of the timeline (null before the first part). */\n let origin: number | null = null\n /** Absolute index just past the last sample fed. */\n let cursor = 0\n /** Absolute index of the next meter frame. */\n let nextFrame = 0\n let state: SegmenterState = INITIAL_SEGMENTER_STATE\n /** A short piece waiting for the next one (mergeShort). */\n let pending: { start: number; end: number; voiceMs: number; at: number } | null = null\n let out: TrackPiece[] = []\n\n const append = (samples: Float32Array) => {\n if (len + samples.length > buf.length) {\n const grown = new Float32Array(Math.max(buf.length * 2, len + samples.length))\n grown.set(buf.subarray(0, len))\n buf = grown\n }\n buf.set(samples, len)\n len += samples.length\n cursor += samples.length\n }\n\n const emit = (piece: { start: number; end: number; voiceMs: number }) => {\n const from = Math.max(piece.start, bufStart)\n if (piece.end <= from) return\n out.push({\n start: from,\n end: piece.end,\n voiceMs: piece.voiceMs,\n samples: buf.slice(from - bufStart, piece.end - bufStart),\n })\n }\n\n const onEvents = (events: SegmenterEvent[]) => {\n for (const e of events) {\n if (e.type === 'start') {\n // Too long a pause after a held short piece: it stands alone, too short to keep.\n if (pending && e.at - pending.at > SHORT_PIECE_MERGE_GAP_MS) pending = null\n continue\n }\n let piece = {\n start: Math.max(origin ?? 0, toSample(e.startedAt) - preRoll),\n end: Math.min(cursor, toSample(e.at)),\n voiceMs: e.voiceMs,\n at: e.at,\n }\n if (pending) {\n if (piece.end - pending.start <= toSample(cfg.maxMs) + preRoll) {\n piece = { ...piece, start: pending.start, voiceMs: piece.voiceMs + pending.voiceMs }\n }\n pending = null\n }\n if (piece.voiceMs >= cfg.minVoiceMs) emit(piece)\n else if (options.mergeShort && piece.end > piece.start) pending = piece\n }\n }\n\n const step = (from: number, n: number) => {\n let sum = 0\n const base = from - bufStart\n for (let j = 0; j < n; j++) sum += buf[base + j] * buf[base + j]\n const result = stepSegmenter(state, levelFromRms(Math.sqrt(sum / n)), toMs(from), cfg)\n state = result.state\n onEvents(result.events)\n }\n\n /** Run the meter over every whole frame fed so far, then drop audio no piece can reach. */\n const run = () => {\n while (nextFrame + frame <= cursor) {\n step(nextFrame, frame)\n nextFrame += frame\n }\n let keepFrom = cursor - preRoll\n if (state.phase === 'voice') keepFrom = Math.min(keepFrom, toSample(state.startedAt) - preRoll)\n if (pending) keepFrom = Math.min(keepFrom, pending.start)\n keepFrom = Math.min(Math.max(keepFrom, bufStart), nextFrame)\n if (keepFrom > bufStart) {\n buf.copyWithin(0, keepFrom - bufStart, len)\n len -= keepFrom - bufStart\n bufStart = keepFrom\n }\n }\n\n const take = () => {\n const done = out\n out = []\n return done\n }\n\n return {\n push(offsetMs, samples) {\n const at = toSample(Math.max(0, offsetMs))\n if (origin === null) {\n origin = at\n bufStart = cursor = nextFrame = at\n }\n if (at > cursor) {\n // A gap between parts (a reconnect): silence, a second at a time.\n const second = new Float32Array(sampleRate)\n while (cursor < at) {\n append(second.subarray(0, Math.min(sampleRate, at - cursor)))\n run()\n }\n }\n // Overlapping the previous part: skip what is already on the timeline.\n const fresh = at < cursor ? samples.subarray(Math.min(samples.length, cursor - at)) : samples\n if (fresh.length) {\n append(fresh)\n run()\n }\n return take()\n },\n finish() {\n if (origin !== null) {\n if (nextFrame < cursor) {\n step(nextFrame, cursor - nextFrame)\n nextFrame = cursor\n }\n onEvents(flushSegmenter(state, toMs(cursor), cfg).events)\n state = INITIAL_SEGMENTER_STATE\n }\n pending = null\n return take()\n },\n }\n}\n\n/**\n * Cut decoded speech at its pauses with the SAME voice-activity segmenter as live transcription\n * (P5): RMS per 50 ms frame → meter level → `stepSegmenter` (a piece ends after ~700 ms of\n * silence, is cut at `maxMs`, and is dropped with too little voice). A whole 30 s part sent at\n * once made the transcriber drop repeated sentences; short pieces do not.\n */\nexport function cutAtPauses(\n samples: Float32Array,\n sampleRate: number,\n config: Partial<SegmenterConfig> = {}\n): AudioPiece[] {\n const cutter = createTrackCutter(sampleRate, config)\n return [...cutter.push(0, samples), ...cutter.finish()].map(({ start, end }) => ({ start, end }))\n}\n","/**\n * Live call transcription (ARBI `docs/meeting-chats-plan.md` §5): captions over the data channel\n * and transcript persistence into the call's meeting chat.\n *\n * Attribution is by origin: a participant's browser transcribes only its own microphone, so the\n * speaker is whoever sent the audio — there is no diarization.\n *\n * Captions: the speaker's client broadcasts `{segment_id, text, identity, name, t}` on topic\n * {@link CALL_CAPTION_TOPIC}, sealed under the call key K exactly like an in-call chat packet, so\n * the SFU only forwards ciphertext. The transcription state itself is announced by the SERVER on\n * {@link CALL_TRANSCRIPTION_TOPIC} (plaintext; it carries no content).\n *\n * Persistence: the speaker stores its own segments. A speaker who cannot post to the meeting chat (a\n * workspace guest on a thread call: read-only) marks its captions `proxy`, and every member that can\n * post stores them instead — idempotent by the segment id (`dedupe_key` in a chat, the `transcript`\n * marker in a thread), so each is stored once whichever member wins.\n */\n\nimport { decryptWithChatKey, encryptWithChatKey } from '@arbidocs/client'\nimport type { QueryClient } from '@tanstack/react-query'\nimport type { Arbi, calls, chats } from '@arbidocs/sdk/browser'\nimport { chatByExtId } from './in-call-chat'\nimport { ensureMeetingChat, type MeetingChatRef } from './meeting-chat'\n\nexport const CALL_CAPTION_TOPIC = 'arbi.caption'\n/** Server-sent: `{on, by_identity, by_name}`. */\nexport const CALL_TRANSCRIPTION_TOPIC = 'arbi.transcription'\nexport const CAPTION_MAX_CHARS = 2000\n/**\n * Voice-activity tuning for live call speech: the shared 700 ms pause rule, but a segment needs only\n * 400 ms of voice, and a segment still too short waits 1 s more for the rest of a short phrase\n * (\"Perfect.\" … \"Thank you both.\") so a closing line is not dropped as two blips.\n */\nexport const LIVE_SEGMENTER = { minVoiceMs: 400, holdShortMs: 1_000 } as const\n\n/** One caption over the data channel, encrypted under K. */\nexport interface CaptionPacket {\n v: 1\n segment_id: string\n text: string\n /** The speaker's LiveKit identity (checked against the packet's sender on receipt). */\n identity: string\n name: string\n /** When the segment started, ms since epoch. */\n t: number\n /** The speaker cannot post to the meeting chat: members store this segment for them. */\n proxy?: boolean\n}\n\n/** A caption on screen. */\nexport interface CaptionLine {\n segmentId: string\n identity: string\n name: string\n text: string\n /** Segment start, ms since epoch. */\n t: number\n mine: boolean\n /** Local receipt time (drives the fade). */\n receivedAt: number\n}\n\n/** A transcript segment to store. */\nexport interface TranscriptSegment {\n segmentId: string\n text: string\n speakerIdentity: string\n speakerName: string\n /** ISO start of the segment. */\n startedAt: string | null\n}\n\nconst encoder = new TextEncoder()\nconst decoder = new TextDecoder()\nexport const SEGMENT_ID_PATTERN = /^[A-Za-z0-9_-]{8,64}$/\n\n/** A fresh segment id (`^[A-Za-z0-9_-]{8,64}$`). */\nexport function newSegmentId(): string {\n const bytes = new Uint8Array(12)\n globalThis.crypto.getRandomValues(bytes)\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\n// ── Captions ────────────────────────────────────────────────────────────────\n\nexport async function encodeCaptionPacket(\n packet: CaptionPacket,\n callKey: Uint8Array\n): Promise<Uint8Array<ArrayBuffer>> {\n return encoder.encode(await encryptWithChatKey(JSON.stringify(packet), callKey))\n}\n\n/** Open a received caption; null when it is not under K or malformed. */\nexport async function decodeCaptionPacket(\n payload: Uint8Array,\n callKey: Uint8Array\n): Promise<CaptionPacket | null> {\n try {\n const parsed: unknown = JSON.parse(await decryptWithChatKey(decoder.decode(payload), callKey))\n if (!parsed || typeof parsed !== 'object') return null\n const p = parsed as Partial<CaptionPacket>\n if (\n p.v !== 1 ||\n typeof p.segment_id !== 'string' ||\n !SEGMENT_ID_PATTERN.test(p.segment_id) ||\n typeof p.text !== 'string' ||\n typeof p.identity !== 'string'\n ) {\n return null\n }\n const text = p.text.trim().slice(0, CAPTION_MAX_CHARS)\n if (!text) return null\n return {\n v: 1,\n segment_id: p.segment_id,\n text,\n identity: p.identity,\n name: typeof p.name === 'string' ? p.name : p.identity,\n t: typeof p.t === 'number' ? p.t : Date.now(),\n ...(p.proxy === true ? { proxy: true } : {}),\n }\n } catch {\n return null\n }\n}\n\n// ── Persistence routing ─────────────────────────────────────────────────────\n\n/**\n * Who stores a segment:\n * - `own`: my own segment, and I can post to the meeting chat;\n * - `proxy`: someone else's segment marked `proxy` (they cannot post: a workspace guest), and I\n * can post — every such member stores it, the server dedupes by segment id;\n * - null: the speaker stores it themselves, or I cannot post (read-only, or no call key to open\n * the meeting chat with). My own segment then goes out marked `proxy` for members to store.\n */\nexport function transcriptPersistRole(params: {\n myIdentity: string\n /** I can post to the meeting chat (not a workspace guest of a thread call). */\n canPost: boolean\n hasCallKey: boolean\n speakerIdentity: string\n /** The caption was marked `proxy` by its speaker. */\n speakerNeedsProxy: boolean\n}): 'own' | 'proxy' | null {\n if (!params.canPost || !params.hasCallKey) return null\n if (params.speakerIdentity === params.myIdentity) return 'own'\n if (params.speakerNeedsProxy) return 'proxy'\n return null\n}\n\n/** The thread comment text of a segment (\"Name: text\"). */\nexport function threadTranscriptText(segment: TranscriptSegment): string {\n return `${segment.speakerName}: ${segment.text}`\n}\n\n/**\n * Store one segment in the call's meeting chat (made on first use): an E2E chat message of kind\n * `transcript` under the chat key, or a comment with a transcript marker in a workspace meeting\n * thread. Idempotent per segment id. Returns the chat it went to.\n *\n * In a thread the marker's `speaker_ext_id` is the speaker: the server accepts it from the speaker,\n * or from a member when the speaker is a guest of the workspace (422 `speaker_mismatch` otherwise).\n */\nexport async function persistTranscriptSegment(\n arbi: Arbi,\n queryClient: QueryClient,\n callExtId: string,\n callKey: Uint8Array,\n segment: TranscriptSegment\n): Promise<MeetingChatRef> {\n const ref = await ensureMeetingChat(arbi, queryClient, callExtId, callKey)\n await storeTranscriptSegment(arbi, queryClient, ref, segment)\n return ref\n}\n\n/**\n * Store one segment in a known meeting chat (see {@link persistTranscriptSegment}); also used for\n * the lines transcribed from a recording (segment ids `rec-<track>-<seq>`).\n */\nexport async function storeTranscriptSegment(\n arbi: Arbi,\n queryClient: QueryClient,\n ref: MeetingChatRef,\n segment: TranscriptSegment\n): Promise<void> {\n if (ref.kind === 'chat') {\n const chat = await arbi.chats.ready(await chatByExtId(arbi, queryClient, ref.chatExtId))\n const ring = await arbi.chats.ring(chat)\n const key = ring.keys.get(ring.currentVersion)\n if (!key) throw new Error('No key for the meeting chat')\n const content: chats.TranscriptContent = {\n text: segment.text,\n speaker_identity: segment.speakerIdentity,\n speaker_name: segment.speakerName,\n started_at: segment.startedAt,\n }\n await arbi.calls.transcription.postChatLine(ref.chatExtId, {\n content: await encryptWithChatKey(JSON.stringify(content), key),\n key_version: ring.currentVersion,\n dedupe_key: segment.segmentId,\n })\n return\n }\n const transcript: calls.TranscriptSegmentRef = {\n segment_id: segment.segmentId,\n speaker_ext_id: segment.speakerIdentity,\n started_at: segment.startedAt,\n }\n // A comment in the workspace meeting thread, idempotent per segment. The server takes it from its\n // speaker, or from a member for a speaker with the workspace guest role.\n await arbi.conversations.postMessage({\n content: threadTranscriptText(segment),\n conversation_ext_id: ref.conversationExtId,\n assistant_mode: 'mentioned',\n transcript,\n })\n}\n","/**\n * A stored call recording, opened in the browser: keys unwrapped, parts downloaded and decrypted\n * on demand, exported as files, and transcribed (ARBI `docs/meeting-chats-plan.md` §6).\n * See `recording-playback.ts` for the playback and export formats.\n */\n\nimport type { QueryClient } from '@tanstack/react-query'\nimport { continuityPrompt, type Arbi, type calls } from '@arbidocs/sdk/browser'\nimport { conversationKeyForCall, decryptPart, unwrapTrackKey } from './recording'\nimport {\n EXPORT_SAMPLE_RATE,\n RECORDING_SEGMENTER,\n createTrackCutter,\n encodeWav,\n floatToWav,\n extensionFor,\n fileSafe,\n splitIntoFiles,\n standalone,\n timelineDurationMs,\n timelineOf,\n writeSamples,\n type TrackPiece,\n} from './recording-playback'\nimport { storeTranscriptSegment, type TranscriptSegment } from './transcription'\nimport type { MeetingChatRef } from './meeting-chat'\n\nexport interface OpenedRecording {\n recording: calls.CallRecording\n /** File key per track ext id (a track whose key does not open is absent). */\n keys: Map<string, Uint8Array>\n}\n\n/** Read a recording and unwrap its tracks' file keys under the call's conversation key. */\nexport async function openRecording(\n arbi: Arbi,\n queryClient: QueryClient,\n call: calls.CallResponse,\n recordingExtId: string,\n loaded?: calls.CallRecording\n): Promise<OpenedRecording> {\n const recording = loaded ?? (await arbi.calls.recording.get(call.external_id, recordingExtId))\n const convKeys = new Map<string, Promise<Uint8Array>>()\n const keys = new Map<string, Uint8Array>()\n for (const track of recording.tracks ?? []) {\n const version = track.key_version ?? null\n const id = String(version)\n if (!convKeys.has(id)) {\n convKeys.set(\n id,\n conversationKeyForCall(arbi, queryClient, call, { keyVersion: version }).then((c) => c.key)\n )\n }\n try {\n keys.set(track.external_id, await unwrapTrackKey(track.wrapped_key, await convKeys.get(id)!))\n } catch (err) {\n console.warn('[calls] a recording track key does not open', err)\n }\n }\n return { recording, keys }\n}\n\nexport function speakerTracks(recording: calls.CallRecording): calls.RecordingTrack[] {\n return (recording.tracks ?? []).filter((t) => t.kind === 'speaker')\n}\n\nexport function compositeTrack(recording: calls.CallRecording): calls.RecordingTrack | null {\n return (recording.tracks ?? []).find((t) => t.kind === 'composite') ?? null\n}\n\nexport function sortedParts(track: calls.RecordingTrack): calls.RecordingPart[] {\n return [...(track.parts ?? [])].sort((a, b) => a.seq - b.seq)\n}\n\n/** One part's plaintext. */\nexport async function fetchPart(\n arbi: Arbi,\n part: calls.RecordingPart,\n fileKey: Uint8Array\n): Promise<Uint8Array> {\n if (!part.download_url) throw new Error('This recording’s media was deleted')\n return decryptPart(await arbi.calls.recording.downloadPart(part.download_url), fileKey)\n}\n\n/** The composite as playable files (normally one). */\nexport async function compositeFiles(\n arbi: Arbi,\n track: calls.RecordingTrack,\n fileKey: Uint8Array\n): Promise<Blob[]> {\n const parts: Uint8Array[] = []\n for (const part of sortedParts(track)) parts.push(await fetchPart(arbi, part, fileKey))\n const type = track.mime_type.split(';')[0] || 'video/webm'\n return splitIntoFiles(parts).map((file) => new Blob(file.map(standalone), { type }))\n}\n\n/** Decode a standalone audio part to mono samples at 16 kHz (decodeAudioData resamples). */\nexport async function decodeMono16k(bytes: Uint8Array): Promise<Float32Array> {\n const ctx = new OfflineAudioContext(1, 1, EXPORT_SAMPLE_RATE)\n const buffer = await ctx.decodeAudioData(standalone(bytes).buffer)\n return buffer.getChannelData(0)\n}\n\n/** A speaker track as ONE WAV (16 kHz mono), its parts at their offsets from the recording start. */\nexport async function speakerWav(\n arbi: Arbi,\n track: calls.RecordingTrack,\n fileKey: Uint8Array,\n onPart?: () => void\n): Promise<Blob> {\n const parts = sortedParts(track)\n const totalMs = timelineDurationMs(timelineOf([track]))\n const samples = new Int16Array(Math.ceil((totalMs / 1000) * EXPORT_SAMPLE_RATE) + 1)\n // decodeAudioData resamples to the context's rate.\n for (const part of parts) {\n const bytes = await fetchPart(arbi, part, fileKey)\n try {\n writeSamples(\n samples,\n await decodeMono16k(bytes),\n (part.offset_ms / 1000) * EXPORT_SAMPLE_RATE\n )\n } catch (err) {\n console.warn('[calls] a recording part does not decode', err)\n }\n onPart?.()\n }\n return new Blob([encodeWav(samples, EXPORT_SAMPLE_RATE)], { type: 'audio/wav' })\n}\n\n/** Every decrypted file of a recording: one WAV per speaker, the composite as one video. */\nexport async function recordingFiles(\n arbi: Arbi,\n opened: OpenedRecording,\n title: string | null | undefined,\n onProgress?: (done: number, total: number) => void\n): Promise<File[]> {\n const { recording, keys } = opened\n const base = fileSafe(title || 'Meeting')\n const date = recording.started_at.slice(0, 10)\n const total = (recording.tracks ?? []).reduce((n, t) => n + (t.parts?.length ?? 0), 0)\n let done = 0\n const tick = () => onProgress?.(++done, total)\n const files: File[] = []\n for (const track of speakerTracks(recording)) {\n const key = keys.get(track.external_id)\n if (!key || !track.parts?.length) continue\n const wav = await speakerWav(arbi, track, key, tick)\n const who = fileSafe(track.speaker_name || track.speaker_ext_id || 'speaker')\n files.push(new File([wav], `${base}-${date}-${who}.wav`, { type: 'audio/wav' }))\n }\n const composite = compositeTrack(recording)\n const compositeKey = composite && keys.get(composite.external_id)\n if (composite && compositeKey && composite.parts?.length) {\n const videos = await compositeFiles(arbi, composite, compositeKey)\n done += composite.parts.length\n onProgress?.(done, total)\n const ext = extensionFor(composite.mime_type)\n videos.forEach((blob, i) =>\n files.push(\n new File([blob], `${base}-${date}-video${videos.length > 1 ? `-${i + 1}` : ''}.${ext}`, {\n type: blob.type,\n })\n )\n )\n }\n return files\n}\n\n/** A track's previous text, kept for the continuity prompt (a little more than it sends). */\nconst PROMPT_HISTORY_CHARS = 400\n\n/**\n * The host's \"Transcribe\": each speaker track is decrypted and decoded here part by part, its parts\n * placed on ONE continuous timeline by their offsets (gaps are silence) and cut at the track's\n * pauses ({@link createTrackCutter} with {@link RECORDING_SEGMENTER}: the live 700 ms pause rule,\n * pieces up to 25 s), so no sentence is split at a part boundary. Each piece (a 16 kHz mono WAV) is\n * sent through the shared transcription helper WITHOUT a part `seq`: its index across the track\n * (`segment`) and its start from the recording start (`segment_offset_ms`) → segment id\n * `rec-<track>-s<n>`. The user's language and the speaker's previous text (continuity prompt) go\n * with every piece.\n *\n * Each line is stored as an attributed transcript entry exactly like a live one (P5) — an E2E chat\n * message of kind `transcript` with `dedupe_key` = the segment id, or a thread comment with the\n * transcript marker — so running it twice stores each line once; the recording's lines then\n * supersede the live ones of the same window. A part this browser cannot decode is sent whole\n * (`rec-<track>-<seq>`) and is silence on the timeline.\n */\nexport async function transcribeRecording(params: {\n arbi: Arbi\n queryClient: QueryClient\n callExtId: string\n opened: OpenedRecording\n ref: MeetingChatRef\n onProgress?(done: number, total: number): void\n /** Decoder (tests); default WebAudio at 16 kHz. */\n decode?(bytes: Uint8Array): Promise<Float32Array>\n /** ISO-639-1 spoken language, when known; otherwise the transcriber detects it. */\n language?: string\n}): Promise<{ stored: number; failed: number }> {\n const {\n arbi,\n queryClient,\n callExtId,\n opened,\n ref,\n onProgress,\n decode = decodeMono16k,\n language,\n } = params\n const { recording, keys } = opened\n const tracks = speakerTracks(recording).filter((t) => keys.has(t.external_id))\n const total = tracks.reduce((n, t) => n + (t.parts?.length ?? 0), 0)\n const start = Date.parse(recording.started_at)\n let done = 0\n let stored = 0\n let failed = 0\n\n for (const track of tracks) {\n const key = keys.get(track.external_id)!\n const cutter = createTrackCutter(EXPORT_SAMPLE_RATE, RECORDING_SEGMENTER, { mergeShort: true })\n let previous = ''\n let nextSegment = 0\n\n const send = async (request: {\n audio: Blob\n seq?: number\n segment?: number\n /** From the recording start (a piece), or from the part start (a whole part: 0). */\n offsetMs: number\n }) => {\n try {\n const result = await arbi.calls.recording.transcribe(callExtId, recording.external_id, {\n audio: request.audio,\n trackExtId: track.external_id,\n seq: request.seq ?? null,\n segment: request.segment ?? null,\n segmentOffsetMs: request.seq == null ? request.offsetMs : 0,\n language,\n prompt: continuityPrompt(previous),\n })\n const text = result.text.trim()\n if (!text) return\n previous = `${previous} ${text}`.slice(-PROMPT_HISTORY_CHARS)\n const segment: TranscriptSegment = {\n segmentId:\n result.segment_id ||\n (request.seq != null\n ? `rec-${track.external_id}-${request.seq}`\n : `rec-${track.external_id}-s${request.segment}`),\n text,\n speakerIdentity: result.speaker_ext_id ?? track.speaker_ext_id ?? '',\n speakerName: result.speaker_name ?? track.speaker_name ?? 'Speaker',\n startedAt: Number.isFinite(start)\n ? new Date(start + (result.offset_ms ?? Math.round(request.offsetMs))).toISOString()\n : null,\n }\n await storeTranscriptSegment(arbi, queryClient, ref, segment)\n stored += 1\n } catch (err) {\n failed += 1\n console.warn('[calls] a recording piece could not be transcribed', err)\n }\n }\n const sendPieces = async (pieces: TrackPiece[]) => {\n for (const piece of pieces) {\n await send({\n audio: new Blob([floatToWav(piece.samples, EXPORT_SAMPLE_RATE)], { type: 'audio/wav' }),\n segment: nextSegment++,\n offsetMs: (piece.start / EXPORT_SAMPLE_RATE) * 1000,\n })\n }\n }\n\n for (const part of sortedParts(track)) {\n let bytes: Uint8Array\n try {\n bytes = await fetchPart(arbi, part, key)\n } catch (err) {\n failed += 1\n console.warn('[calls] a recording part could not be read', err)\n onProgress?.(++done, total)\n continue\n }\n let pcm: Float32Array | null = null\n try {\n pcm = await decode(bytes)\n } catch (err) {\n console.warn('[calls] a recording part does not decode; sending it whole', err)\n }\n if (pcm) {\n await sendPieces(cutter.push(part.offset_ms, pcm))\n } else {\n await send({\n audio: new Blob([standalone(bytes)], { type: track.mime_type }),\n seq: part.seq,\n offsetMs: part.offset_ms,\n })\n }\n onProgress?.(++done, total)\n }\n // The end of the track: the last piece (a closing line) is sent too.\n await sendPieces(cutter.finish())\n }\n return { stored, failed }\n}\n","/**\n * Call data hooks over `arbi.calls`: the deployment's calls config and a call by id. They need no\n * LiveKit; the room hooks live in the `@arbidocs/react/calls` entry.\n *\n * Requires an `ArbiHost` (with an `arbi`) and a React Query client above it.\n */\n\nimport { useCallback, useMemo } from 'react'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { calls } from '@arbidocs/sdk/browser'\nimport { useArbi } from '../useArbi'\nimport { callQueryKeys } from './keys'\nimport { meetingRefForCall } from './recording'\nimport { openRecording, transcribeRecording, type OpenedRecording } from './recording-media'\nimport { recordingWindowsOf, type RecordingWindow } from './transcript-format'\n\n/**\n * The deployment's calls config. Any error (404 on a backend without calls, 5xx) reads as\n * \"disabled\", so call buttons simply stay hidden.\n */\nexport function useCallsConfig(): calls.CallsConfig | undefined {\n const arbi = useArbi()\n // A plain query (no abort signal): the header buttons remount while the app boots, and an\n // aborted request must not stick as \"disabled\".\n const { data } = useQuery({\n queryKey: callQueryKeys.config(),\n queryFn: () => arbi.calls.config(),\n enabled: arbi.isLoggedIn,\n staleTime: 5 * 60_000,\n retry: false,\n })\n return data\n}\n\n/** Calls are available on this deployment (the server has LiveKit configured). */\nexport function useServerCallsEnabled(): boolean {\n return useCallsConfig()?.enabled === true\n}\n\n/**\n * Calls are still experimental on this deployment (the default): the call UI shows only to people\n * who opted in with the host's switch. A deployment that made calls a regular feature\n * (`CALLS_EXPERIMENTAL=false`, `experimental: false`) shows it to everyone. Unknown reads as\n * experimental.\n */\nexport function useServerCallsExperimental(): boolean {\n return useCallsConfig()?.experimental !== false\n}\n\n/** Whether this deployment has a transcriber. Any error reads as \"no\": the controls stay hidden. */\nexport function useCallTranscriptionAvailable(): boolean {\n return useCallsConfig()?.transcription_available === true\n}\n\n/** A call by id (the thread log card, a chat's call card); refreshed by `call.started/ended`. */\nexport function useCall(callExtId: string | null | undefined) {\n const arbi = useArbi()\n return useQuery({\n queryKey: callQueryKeys.call(callExtId ?? ''),\n queryFn: () => arbi.calls.get(callExtId!),\n enabled: !!callExtId && arbi.isLoggedIn,\n staleTime: 60_000,\n retry: false,\n })\n}\n\n/**\n * A call's recordings (readers only: members of its meeting chat). Kept fresh by the recording\n * events (`applyCallEvent`).\n */\nexport function useCallRecordings(callExtId: string | null | undefined, enabled = true) {\n const arbi = useArbi()\n return useQuery({\n queryKey: callQueryKeys.recordings(callExtId ?? ''),\n queryFn: () => arbi.calls.recording.list(callExtId!),\n enabled: !!callExtId && enabled && arbi.isLoggedIn,\n retry: false,\n })\n}\n\n/** One recording of a call. */\nexport function useCallRecording(\n callExtId: string | null | undefined,\n recordingExtId: string | null | undefined\n) {\n const arbi = useArbi()\n return useQuery({\n queryKey: callQueryKeys.recording(callExtId ?? '', recordingExtId ?? ''),\n queryFn: () => arbi.calls.recording.get(callExtId!, recordingExtId!),\n enabled: !!callExtId && !!recordingExtId && arbi.isLoggedIn,\n retry: false,\n })\n}\n\nconst NO_WINDOWS: RecordingWindow[] = []\n\n/**\n * The call's recordings as windows: a recording's transcript supersedes the live lines spoken while\n * it recorded. Fetched only when `enabled` (the transcript has lines from a recording), sharing the\n * recordings list's cache. Empty while loading or when the list cannot be read.\n */\nexport function useRecordingWindows(\n callExtId: string | null | undefined,\n enabled: boolean\n): RecordingWindow[] {\n const { data } = useCallRecordings(callExtId, enabled)\n return useMemo(() => (data ? recordingWindowsOf(data) : NO_WINDOWS), [data])\n}\n\n/** The personal invite links sent for a call (the host's people picker). */\nexport function useCallInviteLinks(callExtId: string | null | undefined) {\n const arbi = useArbi()\n return useQuery({\n queryKey: callQueryKeys.inviteLinks(callExtId ?? ''),\n queryFn: () => arbi.calls.inviteLinks.list(callExtId!),\n enabled: !!callExtId && arbi.isLoggedIn,\n retry: false,\n })\n}\n\n/**\n * What a recording card does with a call's recordings: re-read them, open one (its track keys,\n * unwrapped under the meeting chat's or workspace's key), and transcribe one into the meeting chat.\n */\nexport function useRecordingActions(call: calls.CallResponse) {\n const arbi = useArbi()\n const queryClient = useQueryClient()\n return useMemo(\n () => ({\n refresh: () =>\n void queryClient.invalidateQueries({\n queryKey: callQueryKeys.recordingsOf(call.external_id),\n }),\n open: (recording: calls.CallRecording): Promise<OpenedRecording> =>\n openRecording(arbi, queryClient, call, recording.external_id, recording),\n transcribe: async (\n opened: OpenedRecording,\n onProgress: (done: number, total: number) => void\n ) =>\n transcribeRecording({\n arbi,\n queryClient,\n callExtId: call.external_id,\n opened,\n ref: await meetingRefForCall(arbi, queryClient, call),\n onProgress,\n }),\n }),\n [arbi, queryClient, call]\n )\n}\n\n/** Revoke a personal invite link of a call I host; the list re-reads. */\nexport function useRevokeCallInviteLink(callExtId: string) {\n const arbi = useArbi()\n const queryClient = useQueryClient()\n return useCallback(\n async (inviteExtId: string) => {\n await arbi.calls.inviteLinks.revoke(callExtId, inviteExtId)\n void queryClient.invalidateQueries({ queryKey: callQueryKeys.inviteLinks(callExtId) })\n },\n [arbi, queryClient, callExtId]\n )\n}\n","/**\n * The host's \"Save … to thread\": one comment in the picked thread, or a new thread titled after the\n * meeting when no thread is picked (`conversationExtId` null). Resolves to the thread it went to.\n */\n\nimport { useMemo } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { documents, type Arbi } from '@arbidocs/sdk/browser'\nimport { useArbi } from '../useArbi'\nimport { invalidateConversationLists } from '../conversation-list'\nimport { formatTranscriptMarkdown, type TranscriptExportLine } from './transcript-format'\n\nasync function postToThread(\n arbi: Arbi,\n conversationExtId: string | null,\n newThreadTitle: string,\n content: string\n): Promise<string | null> {\n const posted = await arbi.conversations.postMessage({\n content,\n assistant_mode: 'mentioned',\n ...(conversationExtId ? { conversation_ext_id: conversationExtId } : { title: newThreadTitle }),\n })\n return posted?.conversation_ext_id ?? null\n}\n\n/** The (locally decrypted) transcript as ONE comment. */\nexport function postTranscriptToThread(\n arbi: Arbi,\n params: {\n conversationExtId: string | null\n title: string | null | undefined\n lines: TranscriptExportLine[]\n }\n): Promise<string | null> {\n return postToThread(\n arbi,\n params.conversationExtId,\n `Transcript: ${params.title?.trim() || 'Meeting'}`,\n formatTranscriptMarkdown({ title: params.title, lines: params.lines })\n )\n}\n\n/** A recording's uploaded documents, listed as `doc-…` pills in ONE comment. */\nexport function postRecordingDocsToThread(\n arbi: Arbi,\n params: {\n conversationExtId: string | null\n title: string | null | undefined\n docExtIds: string[]\n }\n): Promise<string | null> {\n const meeting = params.title?.trim() || 'Meeting'\n const content = [\n `**Recording: ${meeting}**`,\n '',\n ...params.docExtIds.map((id) => `- \\`${id}\\``),\n ].join('\\n')\n return postToThread(arbi, params.conversationExtId, `Recording: ${meeting}`, content)\n}\n\n/**\n * A recording's decrypted files, uploaded as shared documents into the call's workspace\n * (`workspaceExtId`) and listed in ONE comment. They are encrypted here under that workspace's key\n * and sent straight to storage (`uploadDirect`, whose init and commit name the workspace); without\n * a key of mine for it they go through the server, named. Never into whichever workspace happens\n * to be open.\n */\nexport async function saveRecordingFilesToThread(\n arbi: Arbi,\n params: {\n workspaceExtId: string\n files: File[]\n conversationExtId: string | null\n title: string | null | undefined\n /** The upload starts (after the files are decrypted). */\n onUploading?: () => void\n }\n): Promise<string | null> {\n const workspaceId = params.workspaceExtId\n // A workspace I hold no key for (public, a temporary grant, a guest's) goes through the server,\n // which encrypts; the upload names the workspace either way.\n const workspaceKey = await arbi.workspaces.key(workspaceId).catch(() => null)\n params.onUploading?.()\n const result = workspaceKey\n ? await arbi.documents.uploadDirect(\n workspaceId,\n workspaceKey,\n await Promise.all(\n params.files.map(async (file) => ({\n name: file.name,\n data: new Uint8Array(await file.arrayBuffer()),\n contentType: file.type || documents.mimeFromName(file.name),\n }))\n ),\n { shared: true }\n )\n : await arbi.documents.upload(params.files, { shared: true, workspaceExtId: workspaceId })\n const docExtIds = result.doc_ext_ids ?? []\n const skipped = result.skipped ?? []\n if (!docExtIds.length) {\n throw new Error(`The files were not uploaded${skipped[0] ? `: ${skipped[0].reason}` : ''}`)\n }\n return postRecordingDocsToThread(arbi, {\n conversationExtId: params.conversationExtId,\n title: params.title,\n docExtIds,\n })\n}\n\n/** The \"Save … to thread\" actions on the signed-in session; the thread lists are re-read after. */\nexport function useSaveToThread() {\n const arbi = useArbi()\n const queryClient = useQueryClient()\n return useMemo(() => {\n const thenRefresh = async <T>(run: Promise<T>): Promise<T> => {\n const result = await run\n void invalidateConversationLists(queryClient)\n return result\n }\n return {\n transcript: (params: Parameters<typeof postTranscriptToThread>[1]) =>\n thenRefresh(postTranscriptToThread(arbi, params)),\n recording: (params: Parameters<typeof saveRecordingFilesToThread>[1]) =>\n thenRefresh(saveRecordingFilesToThread(arbi, params)),\n }\n }, [arbi, queryClient])\n}\n","/**\n * Call actions — usable outside React (buttons, notification actions, the WS\n * ringer). One call system: every call has a random key K made in the\n * creator's browser (see `@arbidocs/client` call-keys):\n *\n * - start (thread / chat / contacts): generate K, create the call with K\n * sealed to ourselves and to the people picked, show the pre-join; on\n * join, ring them. Nobody else is rung: a thread call rings only the\n * members picked, never the whole workspace. Unregistered contacts get a\n * personal invite link by e-mail instead (register, then straight into the\n * call — there are no anonymous guests);\n * - accept a ring: open K from the ring's sealed blob and join at once;\n * - open a call (link / calendar event / notification): K from the link fragment,\n * else our sealed copy on the server.\n *\n * A thread call is get-or-create: if the thread already has a running call we\n * hold the key for, we join that one instead of starting another.\n */\n\nimport { buildCallLink, type CallPeer } from '@arbidocs/client'\nimport { CallApiError, type Arbi, type calls } from '@arbidocs/sdk/browser'\nimport { notify } from '../notifications/notify-store'\nimport {\n callSession,\n DEFAULT_JOIN_CHOICES,\n type ActiveCall,\n type IncomingCall,\n type JoinChoices,\n} from './session'\n\nexport type { CallPeer }\n\n/** `https://<host>/call/<id>#k=…&v=…` — the shareable link. */\nexport function callLink(callExtId: string, key: Uint8Array, linkVersion: number): string {\n return buildCallLink(window.location.origin, callExtId, key, linkVersion)\n}\n\nfunction alreadyInCall(callExtId?: string): boolean {\n const active = callSession.getState().active\n if (!active || active.callExtId === callExtId) return false\n notify.warning('You are already in a call. Leave it first.')\n return true\n}\n\nfunction fail(err: unknown, what = 'start the call') {\n console.error(`[calls] failed to ${what}`, err)\n const message = err instanceof Error ? err.message : 'Unknown error'\n notify.error(`Could not ${what}: ${message}`)\n}\n\n/** Where a call belongs, for the active call (and the in-call chat). */\nfunction callContext(\n call: Pick<\n calls.CallResponse,\n | 'chat_ext_id'\n | 'log_conversation_ext_id'\n | 'meeting_conversation_ext_id'\n | 'locked_workspace_ext_id'\n >\n): Pick<\n ActiveCall,\n 'chatExtId' | 'logConversationExtId' | 'meetingConversationExtId' | 'lockedWorkspaceExtId'\n> {\n return {\n chatExtId: call.chat_ext_id ?? null,\n logConversationExtId: call.log_conversation_ext_id ?? null,\n meetingConversationExtId: call.meeting_conversation_ext_id ?? null,\n lockedWorkspaceExtId: call.locked_workspace_ext_id ?? null,\n }\n}\n\n/** An unregistered contact who gets a personal invite link by e-mail. */\nexport interface CallInviteContact {\n contactExtId: string\n /** The name they are invited under; encrypted under K. */\n name: string\n}\n\n/**\n * Mail each contact a personal invite link: an ARBI registration link that, once they have an\n * account, opens `/call/<id>?i=<token>#k=…` and admits them straight into the call. Best effort: a\n * failure for one contact is reported and does not stop the others. Resolves to how many went out.\n */\nasync function sendInviteLinks(\n arbi: Arbi,\n callExtId: string,\n key: Uint8Array,\n linkVersion: number,\n invites: CallInviteContact[]\n): Promise<number> {\n let sent = 0\n for (const invite of invites) {\n try {\n await arbi.calls.inviteLinks.create(callExtId, {\n contact_ext_id: invite.contactExtId,\n link: callLink(callExtId, key, linkVersion),\n })\n sent += 1\n } catch (err) {\n fail(err, `invite ${invite.name || 'a contact'}`)\n }\n }\n if (sent) notify.success(`Sent ${sent} ${sent === 1 ? 'invitation' : 'invitations'}`)\n return sent\n}\n\n/**\n * Create a call and open its pre-join. `ring` are invited now (K sealed to each) and rung when we\n * join; `invites` get a personal invite link by e-mail straight away.\n */\nasync function startCall(\n arbi: Arbi,\n opts: {\n context: { chatExtId?: string | null; threadExtId?: string | null }\n title?: string | null\n ring: CallPeer[]\n invites?: CallInviteContact[]\n }\n): Promise<boolean> {\n if (alreadyInCall()) return false\n try {\n const {\n call,\n key,\n title,\n sealedKeys: ringOnJoin,\n } = await arbi.calls.start({ ...opts.context, title: opts.title, invitees: opts.ring })\n callSession.setActiveCall({\n callExtId: call.external_id,\n key,\n linkVersion: call.link_version,\n title,\n isCreator: call.is_creator,\n ...callContext(call),\n phase: 'prejoin',\n tokenResponse: null,\n choices: null,\n knock: null,\n ringOnJoin: Object.keys(ringOnJoin).length ? ringOnJoin : null,\n })\n if (opts.invites?.length) {\n void sendInviteLinks(arbi, call.external_id, key, call.link_version, opts.invites)\n }\n return true\n } catch (err) {\n fail(err)\n return false\n }\n}\n\n/**\n * A thread call (shared threads only): locked to the thread's workspace and logged in the thread.\n * Get-or-create: a running call on the thread is joined. Only `ring` (the members picked) are rung.\n */\nexport function startThreadCall(\n arbi: Arbi,\n opts: {\n threadExtId: string\n title?: string | null\n ring: CallPeer[]\n }\n): Promise<boolean> {\n return startCall(arbi, {\n context: { threadExtId: opts.threadExtId },\n title: opts.title,\n ring: opts.ring,\n })\n}\n\n/** A chat's call (a DM or a group): the chat is its meeting chat. `ring` are rung when we join. */\nexport function startChatCall(\n arbi: Arbi,\n opts: {\n chatExtId: string\n title?: string | null\n ring: CallPeer[]\n }\n): Promise<boolean> {\n return startCall(arbi, {\n context: { chatExtId: opts.chatExtId },\n title: opts.title,\n ring: opts.ring,\n })\n}\n\n/** Call a person: the call of our direct chat (made if missing), ringing them. */\nexport async function startDmCall(\n arbi: Arbi,\n peer: CallPeer & { name?: string | null }\n): Promise<boolean> {\n if (alreadyInCall()) return false\n if (!peer.encryption_public_key) {\n fail(new Error('This person cannot receive encrypted calls'))\n return false\n }\n let chatExtId: string\n try {\n chatExtId = (await arbi.chats.openDirect(peer.external_id)).external_id\n } catch (err) {\n fail(err)\n return false\n }\n return startChatCall(arbi, { chatExtId, title: peer.name ?? null, ring: [peer] })\n}\n\n/**\n * A contacts call (\"New call\"): registered contacts are rung, unregistered ones get a personal invite\n * link. Its meeting chat is made on first use.\n */\nexport function startContactsCall(\n arbi: Arbi,\n opts: {\n title?: string | null\n ring: CallPeer[]\n invites: CallInviteContact[]\n }\n): Promise<boolean> {\n return startCall(arbi, { context: {}, title: opts.title, ring: opts.ring, invites: opts.invites })\n}\n\n/**\n * Open a call by id (link, upcoming list, notification) into the pre-join.\n * `key` comes from the link fragment; without it we use our sealed copy.\n * `inviteToken` (a personal invite link's `?i=`) is redeemed on join: straight in, no lobby.\n */\nexport async function openCall(\n arbi: Arbi,\n opts: {\n callExtId: string\n key?: Uint8Array | null\n linkVersion?: number | null\n inviteToken?: string | null\n }\n): Promise<boolean> {\n if (alreadyInCall(opts.callExtId)) return false\n try {\n const { call, key, title } = await arbi.calls.open(opts.callExtId, opts.key)\n callSession.setActiveCall({\n callExtId: call.external_id,\n key,\n linkVersion: opts.linkVersion ?? call.link_version,\n title,\n isCreator: call.is_creator,\n ...callContext(call),\n phase: 'prejoin',\n tokenResponse: null,\n choices: null,\n knock: null,\n ringOnJoin: null,\n inviteToken: opts.inviteToken ?? null,\n })\n return true\n } catch (err) {\n fail(err, 'open the call')\n return false\n }\n}\n\n/** Answer a ring: open K from the ring and join straight away (no pre-join). */\nexport async function acceptRing(arbi: Arbi, ring: IncomingCall): Promise<boolean> {\n // Accepting while in another call switches to the new one.\n const current = callSession.getState().active\n if (current && current.callExtId !== ring.call_ext_id) callSession.endActiveCall()\n try {\n const { key, token, title } = await arbi.calls.answer(ring)\n callSession.setActiveCall({\n callExtId: ring.call_ext_id,\n key,\n linkVersion: ring.link_version,\n title: title || ring.caller_name,\n isCreator: false,\n chatExtId: ring.chat_ext_id ?? null,\n logConversationExtId: ring.log_conversation_ext_id ?? null,\n meetingConversationExtId: ring.meeting_conversation_ext_id ?? null,\n lockedWorkspaceExtId: ring.context === 'thread' ? (ring.workspace_ext_id ?? null) : null,\n phase: 'connected',\n tokenResponse: token,\n choices: DEFAULT_JOIN_CHOICES,\n knock: null,\n ringOnJoin: null,\n })\n return true\n } catch (err) {\n fail(err, 'join the call')\n callSession.removeIncomingCall(ring.call_ext_id)\n return false\n }\n}\n\nexport function declineRing(arbi: Arbi, ring: IncomingCall): void {\n callSession.removeIncomingCall(ring.call_ext_id)\n arbi.calls\n .decline(ring.call_ext_id)\n .catch((err: unknown) => console.warn('[calls] decline failed', err))\n}\n\nconst KNOCK_POLL_MS = 2_000\n\n/**\n * Wait in the lobby: poll a knock until it is admitted (→ token), denied or\n * expired (→ throws). `signal` aborts the wait (the user gave up).\n */\nasync function waitForAdmission(\n arbi: Arbi,\n callExtId: string,\n knock: Pick<calls.CallKnockResponse, 'knock_id'>,\n signal: AbortSignal\n): Promise<calls.CallTokenResponse> {\n for (;;) {\n if (signal.aborted) throw new DOMException('Stopped waiting', 'AbortError')\n await new Promise((resolve) => setTimeout(resolve, KNOCK_POLL_MS))\n if (signal.aborted) throw new DOMException('Stopped waiting', 'AbortError')\n const state = await arbi.calls.pollKnock(callExtId, knock.knock_id)\n if (state.status === 'admitted' && state.token) return state.token\n if (state.status === 'denied') throw new CallApiError('The host declined your request.', 403)\n if (state.status === 'expired') throw new CallApiError('Nobody let you in. Try again.', 410)\n }\n}\n\n/**\n * Pre-join done: get a token (or knock when the policy says so), connect, and\n * ring whoever this call was started for.\n */\nexport async function joinFromPreJoin(arbi: Arbi, choices: JoinChoices): Promise<void> {\n const active = callSession.getState().active\n if (!active) return\n const { callExtId, linkVersion, inviteToken } = active\n const update = (patch: Partial<ActiveCall>) => callSession.updateActiveCall(callExtId, patch)\n try {\n let tokenResponse: calls.CallTokenResponse\n try {\n // A personal invite link admits its account straight in (no lobby).\n tokenResponse = await arbi.calls.token(callExtId, linkVersion, inviteToken)\n } catch (err) {\n if (!(err instanceof CallApiError && err.code === 'knock_required')) throw err\n // Knock as the signed-in user (the server names me to the hosts from my account); the\n // token comes at once when the policy lets us straight in.\n const knock = await arbi.calls.knock(callExtId, linkVersion)\n if (knock.status === 'admitted' && knock.token) tokenResponse = knock.token\n else {\n const controller = new AbortController()\n update({ phase: 'knocking', choices, knock: { abort: () => controller.abort() } })\n tokenResponse = await waitForAdmission(arbi, callExtId, knock, controller.signal)\n }\n }\n if (callSession.getState().active?.callExtId !== callExtId) return // left meanwhile\n update({ phase: 'connected', tokenResponse, choices, knock: null })\n const ring = active.ringOnJoin\n if (ring && Object.keys(ring).length) {\n update({ ringOnJoin: null })\n await arbi.calls\n .ring(callExtId, ring, linkVersion)\n .catch((err: unknown) => console.warn('[calls] ring failed', err))\n }\n } catch (err) {\n if (err instanceof DOMException && err.name === 'AbortError') return\n // A webinar's audience before go-live: \"Starting soon\", joining by itself once it is live.\n if (err instanceof CallApiError && err.code === 'webinar_not_live') {\n update({ phase: 'webinar-waiting', choices, knock: null })\n return\n }\n fail(err, 'join the call')\n if (callSession.getState().active?.callExtId === callExtId) {\n callSession.endActiveCall()\n }\n }\n}\n","/**\n * The webinar's wire formats on the client: the server's LiveKit data messages (topic\n * `arbi.webinar`, trusted only when server-sent) and the Q&A text, end-to-end under the call key K\n * in the same secretbox format as the call title (`encryptCallTitle` / `decryptCallTitle`), and\n * joining a webinar again without leaving it.\n */\n\nimport type { Arbi, calls } from '@arbidocs/sdk/browser'\nimport { decryptCallTitle, encryptCallTitle } from '@arbidocs/client'\nimport { joinFromPreJoin } from './actions'\nimport { callSession, type JoinChoices } from './session'\n\nexport const CALL_WEBINAR_TOPIC = 'arbi.webinar'\n\n/** The audience cannot publish: no microphone, no camera, no device check. */\nexport const AUDIENCE_CHOICES: JoinChoices = { audioEnabled: false, videoEnabled: false }\n\ntype Hand = NonNullable<calls.WebinarState['hands']>[number]\n\nexport type WebinarPacket =\n | { type: 'state'; live: boolean; live_at: string | null; chat: calls.WebinarChat }\n | { type: 'hands'; hands: Hand[] }\n | { type: 'questions' }\n | { type: 'promoted' }\n | { type: 'demoted' }\n\nconst isHand = (h: unknown): h is Hand =>\n !!h &&\n typeof h === 'object' &&\n typeof (h as Hand).identity === 'string' &&\n typeof (h as Hand).raised_at === 'string'\n\n/**\n * A data packet on {@link CALL_WEBINAR_TOPIC}, or null. `fromParticipant` is whether a participant\n * sent it: only the server speaks on this topic, so anything from a participant is dropped.\n */\nexport function parseWebinarPacket(\n payload: Uint8Array,\n fromParticipant: boolean\n): WebinarPacket | null {\n if (fromParticipant) return null\n let raw: unknown\n try {\n raw = JSON.parse(new TextDecoder().decode(payload))\n } catch {\n return null\n }\n if (!raw || typeof raw !== 'object') return null\n const p = raw as Record<string, unknown>\n switch (p.type) {\n case 'state':\n if (typeof p.live !== 'boolean') return null\n return {\n type: 'state',\n live: p.live,\n live_at: typeof p.live_at === 'string' ? p.live_at : null,\n chat: p.chat === 'everyone' ? 'everyone' : 'panelists',\n }\n case 'hands':\n return {\n type: 'hands',\n hands: Array.isArray(p.hands)\n ? p.hands.filter(isHand).map((h) => ({ ...h, name: String(h.name ?? '') }))\n : [],\n }\n case 'questions':\n case 'promoted':\n case 'demoted':\n return { type: p.type }\n default:\n return null\n }\n}\n\n/** A question with its text opened under K (`null` when it does not open: shown as such). */\nexport interface OpenQuestion extends calls.CallQuestion {\n text: string\n answerText: string | null\n}\n\nconst UNREADABLE = '(This question could not be decrypted.)'\n\nexport function sealQuestionText(text: string, key: Uint8Array): Promise<string> {\n return encryptCallTitle(text, key)\n}\n\nexport async function openQuestion(q: calls.CallQuestion, key: Uint8Array): Promise<OpenQuestion> {\n const [text, answerText] = await Promise.all([\n decryptCallTitle(q.body, key),\n q.answer ? decryptCallTitle(q.answer, key) : Promise.resolve(null),\n ])\n return { ...q, text: text ?? UNREADABLE, answerText }\n}\n\n// ── Joining again ───────────────────────────────────────────────────────────\n//\n// After a moderator moved me to the audience (or I stepped down, or declined the stage) the server\n// removes me from the room, and I come straight back with a new token — hidden and listen-only,\n// because the session's stage state says so. No pre-join and no error screen: the same join path\n// as the pre-join's, with the audience's choices.\n\n/** The room I am in is about to drop me for a rejoin: its disconnect is not a leave. */\nexport function markRejoining(callExtId: string, on = true): void {\n callSession.updateActiveCall(callExtId, { rejoining: on })\n}\n\n/** Join again as the audience (the room that dropped me has already gone). */\nexport async function rejoinAsAudience(arbi: Arbi, callExtId: string): Promise<void> {\n if (callSession.getState().active?.callExtId !== callExtId) return\n markRejoining(callExtId)\n try {\n await joinFromPreJoin(arbi, AUDIENCE_CHOICES)\n } finally {\n markRejoining(callExtId, false)\n }\n}\n","/**\n * Workspace guests may join a thread call (and are transcribed) but its meeting chat is read-only\n * for them: no in-call chat composer, no meeting-thread composer. Their transcript lines are stored\n * by a member's client (see `transcriptPersistRole`).\n */\n\nimport { useWorkspaces } from '../hooks'\nimport { useArbi } from '../useArbi'\n\ninterface WorkspaceRoster {\n external_id: string\n users?: { role?: string | null; user: { external_id: string } }[] | null\n}\n\n/** Whether I am a guest of `workspaceExtId` (pure, for tests). */\nexport function isWorkspaceGuestOf(\n workspaces: readonly WorkspaceRoster[] | undefined,\n workspaceExtId: string | null | undefined,\n me: string | null | undefined\n): boolean {\n if (!workspaceExtId || !me || !workspaces) return false\n const workspace = workspaces.find((w) => w.external_id === workspaceExtId)\n return workspace?.users?.find((wu) => wu.user.external_id === me)?.role === 'guest'\n}\n\n/** True when I am a guest of the workspace a thread call is locked to (null: not a thread call). */\nexport function useMeetingReadOnly(lockedWorkspaceExtId: string | null | undefined): boolean {\n const arbi = useArbi()\n const me = arbi.isLoggedIn ? (arbi.user.identity()?.externalId ?? null) : null\n const { data: workspaces } = useWorkspaces({\n enabled: !!lockedWorkspaceExtId && arbi.isLoggedIn,\n })\n return isWorkspaceGuestOf(\n workspaces as readonly WorkspaceRoster[] | undefined,\n lockedWorkspaceExtId,\n me\n )\n}\n","/**\n * The call-UI gate. The call UI (call buttons, the incoming-call ringer, upcoming calls) shows only\n * when the server has calls enabled AND the host's `calls` feature is on (`useFeature('calls')`:\n * ARBI's Settings → Experimental toggle; a host that does not list it, on). The switch is an\n * experimental opt-in: on a deployment where calls are not experimental (`experimental: false`) the\n * call UI shows whatever it says. A call link opens whenever the server has calls enabled.\n */\n\nimport { useFeature } from '../host/features'\nimport { useIsEntitled } from '../account/entitlements'\nimport { useServerCallsEnabled, useServerCallsExperimental } from './hooks'\n\n/** The call UI shows: the server has calls AND (they are not experimental there OR the host's switch is on). */\nexport function useCallsEnabled(): boolean {\n const serverEnabled = useServerCallsEnabled()\n const experimental = useServerCallsExperimental()\n const switchedOn = useFeature('calls')\n return serverEnabled && (switchedOn || !experimental)\n}\n\n/**\n * Whether the person may HOST calls (start or schedule one, record one): the call UI is on and they\n * are entitled (Pro on the hosted service, an internal user on a Box). Joining a call someone else\n * hosts needs only `useCallsEnabled`.\n */\nexport function useCanHostCalls(): boolean {\n const callsEnabled = useCallsEnabled()\n const entitled = useIsEntitled('host_calls')\n return callsEnabled && entitled\n}\n","/**\n * \"Open meeting chat\" for every link that has one (a thread's call log card, a calendar event): a\n * known chat or meeting thread opens directly; otherwise the call's meeting chat is made on first\n * use. It opens where the host shows meeting chats (`calls.openMeetingChat`).\n */\n\nimport { useCallback } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { useArbi } from '../useArbi'\nimport { useArbiHost } from '../host/config'\nimport { notify } from '../notifications/notify-store'\nimport { resolveMeetingChat, type MeetingChatTarget } from './meeting-chat'\n\nexport function useOpenCallMeetingChat(): (target: MeetingChatTarget) => Promise<void> {\n const arbi = useArbi()\n const queryClient = useQueryClient()\n const open = useArbiHost().calls?.openMeetingChat\n return useCallback(\n async (target) => {\n try {\n const ref = await resolveMeetingChat(arbi, queryClient, target)\n open?.(ref)\n } catch (err) {\n console.warn('[calls] could not open the meeting chat', err)\n notify.error(\n `Could not open the meeting chat: ${err instanceof Error ? err.message : 'unknown error'}`\n )\n }\n },\n [arbi, queryClient, open]\n )\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/account/settings.ts","../src/host/features.ts","../src/conversation-list.ts","../src/directmessages/chat-cache.ts","../src/calls/keys.ts","../src/calls/moderation.ts","../src/calls/session.ts","../src/calls/events.ts","../src/mail/offline/crypto.ts","../src/offline-store/store.ts","../src/mail/offline/store.ts","../src/mail/offline/settings.ts","../src/mail/offline/wipe.ts","../src/account/profile.ts","../src/account/client.tsx","../src/account/entitlements.ts","../src/calls/meeting-chat.ts","../src/calls/transcript-format.ts","../src/calls/in-call-chat.ts","../src/calls/recording.ts","../src/calls/recording-playback.ts","../src/calls/transcription.ts","../src/calls/recording-media.ts","../src/calls/hooks.ts","../src/calls/save-to-thread.ts","../src/calls/actions.ts","../src/calls/webinar.ts","../src/calls/read-only.ts","../src/calls/gate.ts","../src/calls/open-meeting-chat.ts"],"names":["store","listeners","notify","update","useSyncExternalStore","state","useQueryClient","useQuery","useMutation","useCallback","useMemo","chats","encryptWithChatKey","generateChatKey","key","calls","partOffsetMs","pending","encoder","decoder","decryptWithChatKey","documents","CallApiError","decryptCallTitle"],"mappings":"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","file":"chunk-PJMCBB6E.js","sourcesContent":["/**\n * The user's settings record (flags, pins, preferences, notification choices) and its writes.\n */\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { components } from '@arbidocs/client'\nimport { useArbi } from '../useArbi'\nimport { arbiQueryKeys } from '../hooks'\n\n/** A PATCH of the user settings: only the fields given change (lists are sent whole). */\nexport type UserSettingsPatch = components['schemas']['UserSettingsUpdate']\n\n/** The user's settings record (flags, pins, notification preferences). */\nexport function useUserSettings(options?: { enabled?: boolean }) {\n const arbi = useArbi()\n return useQuery({\n queryKey: arbiQueryKeys.userSettings(),\n queryFn: () => arbi.settings.get(),\n // Edited about once a month, and every write here patches the cache: no refetch on each mount.\n staleTime: 30 * 60 * 1000,\n enabled: (options?.enabled ?? true) && arbi.isLoggedIn,\n })\n}\n\n/**\n * Write some of the user settings. The cached record takes the patch straight away, so a switch\n * does not wait for the round trip; a failed write rolls it back.\n */\nexport function useUpdateUserSettings() {\n const arbi = useArbi()\n const qc = useQueryClient()\n return useMutation({\n mutationFn: (patch: UserSettingsPatch) => arbi.settings.update(patch),\n onMutate: async (patch) => {\n await qc.cancelQueries({ queryKey: arbiQueryKeys.userSettings() })\n const previous = qc.getQueryData(arbiQueryKeys.userSettings())\n qc.setQueryData(arbiQueryKeys.userSettings(), (old: object | undefined) =>\n old ? { ...old, ...patch } : old\n )\n return { previous }\n },\n onError: (_err, _patch, context) => {\n qc.setQueryData(arbiQueryKeys.userSettings(), context?.previous)\n },\n onSettled: () => qc.invalidateQueries({ queryKey: arbiQueryKeys.userSettings() }),\n })\n}\n","/**\n * Features: the parts of the product a host turns on or off for the user (`<ArbiHost config={{\n * features }}>`), read anywhere with `useFeature(name)`.\n *\n * Each listed feature says where its switch is kept: a boolean field of the user's settings record\n * (`{ userSetting: 'show_skills', default: false }`, so it follows the user to every device), or\n * this device (`{ local: store, default: false }`, for a switch the server has no field for). A\n * feature the host does not list is on, so a site that lists none offers everything.\n */\n\nimport { useCallback, useMemo, useSyncExternalStore } from 'react'\nimport { useUpdateUserSettings, useUserSettings, type UserSettingsPatch } from '../account/settings'\nimport { useArbiHost } from './config'\n\n/** The features the kits ask about; a host may list its own as well. */\nexport type ArbiFeature =\n 'workflows' | 'mail' | 'databases' | 'calls' | 'welcomeJourney' | (string & Record<never, never>)\n\n/** A boolean field of the user's settings record. */\nexport type UserSettingFlag = {\n [K in keyof UserSettingsPatch]-?: NonNullable<UserSettingsPatch[K]> extends boolean ? K : never\n}[keyof UserSettingsPatch]\n\n/** Switches kept on this device (`createLocalFeatureStore`). */\nexport interface LocalFeatureStore {\n /** The saved value; `undefined` while there is none. */\n get(feature: string): boolean | undefined\n set(feature: string, on: boolean): void\n subscribe(listener: () => void): () => void\n /** Whether the saved values have been read (an earlier build's brought in, once). */\n isHydrated(): boolean\n hydrated(): Promise<void>\n}\n\n/** Where a feature's switch is kept, and its value while none is saved. */\nexport type FeatureSource =\n | { userSetting: UserSettingFlag; default: boolean }\n | { local: LocalFeatureStore; default: boolean }\n\n/** The host's features by name. */\nexport type ArbiFeatures = Readonly<Partial<Record<ArbiFeature, FeatureSource>>>\n\n/** A feature's value and how to change it. */\nexport interface FeatureSwitch {\n on: boolean\n /** Turn it on or off (a feature the host does not list cannot be turned off). */\n set: (on: boolean) => void\n}\n\nconst ALWAYS_ON: FeatureSwitch = { on: true, set: () => {} }\n\nfunction useUnlisted(): FeatureSwitch {\n return ALWAYS_ON\n}\n\nfunction useUserSettingSwitch(source: FeatureSource): FeatureSwitch {\n const { userSetting, default: fallback } = source as Extract<\n FeatureSource,\n { userSetting: UserSettingFlag }\n >\n const { data } = useUserSettings()\n const { mutate } = useUpdateUserSettings()\n const saved = (data as Partial<Record<UserSettingFlag, boolean | null>> | undefined)?.[\n userSetting\n ]\n const set = useCallback((on: boolean) => mutate({ [userSetting]: on }), [mutate, userSetting])\n return { on: saved ?? fallback, set }\n}\n\nfunction useLocalSwitch(source: FeatureSource, feature: string): FeatureSwitch {\n const { local, default: fallback } = source as Extract<FeatureSource, { local: unknown }>\n const saved = useSyncExternalStore(local.subscribe, () => local.get(feature))\n const set = useCallback((on: boolean) => local.set(feature, on), [local, feature])\n return { on: saved ?? fallback, set }\n}\n\n/**\n * The switch of one feature. The host's configuration is one object for its life, so the hook a\n * feature's source picks is always the same one.\n */\nexport function useFeatureSwitch(feature: ArbiFeature): FeatureSwitch {\n const source = useArbiHost().features?.[feature]\n const useSource = !source\n ? useUnlisted\n : 'userSetting' in source\n ? useUserSettingSwitch\n : useLocalSwitch\n return useSource(source as FeatureSource, feature)\n}\n\n/** Whether a feature is on for the user (on when the host does not list it). */\nexport function useFeature(feature: ArbiFeature): boolean {\n return useFeatureSwitch(feature).on\n}\n\nfunction useNoSettings(): { data: undefined } {\n return { data: undefined }\n}\n\n/**\n * Whether any feature is on, for a list whose features are only known at render (a host's\n * navigation config): one hook for all of them, re-rendering when a setting or a device switch\n * changes. The same answer as `useFeature` for each.\n */\nexport function useFeatureCheck(): (feature: ArbiFeature) => boolean {\n const features = useArbiHost().features\n // Like `useFeatureSwitch`: the settings are read only for a host that keeps a feature there, and\n // its configuration is one object for its life.\n const useSettings = Object.values(features ?? {}).some(\n (source) => !!source && 'userSetting' in source\n )\n ? useUserSettings\n : useNoSettings\n const { data } = useSettings()\n const locals = useMemo(() => {\n const entries = Object.entries(features ?? {}).filter(\n (entry): entry is [string, Extract<FeatureSource, { local: LocalFeatureStore }>] =>\n !!entry[1] && 'local' in entry[1]\n )\n return {\n subscribe: (listener: () => void) => {\n const stops = [...new Set(entries.map(([, source]) => source.local))].map((store) =>\n store.subscribe(listener)\n )\n return () => stops.forEach((stop) => stop())\n },\n // A primitive snapshot, so an unrelated store write does not re-render.\n snapshot: () => entries.map(([name, source]) => `${source.local.get(name)}`).join(','),\n }\n }, [features])\n // Subscribed so a device switch re-renders; the check below reads the stores themselves.\n useSyncExternalStore(locals.subscribe, locals.snapshot, locals.snapshot)\n return (feature) => {\n const source = features?.[feature]\n if (!source) return true\n if ('local' in source) return source.local.get(feature) ?? source.default\n const saved = (data as Partial<Record<UserSettingFlag, boolean | null>> | undefined)?.[\n source.userSetting\n ]\n return saved ?? source.default\n }\n}\n\n/** A local feature's value outside React (its default while none is saved). */\nexport function readLocalFeature(features: ArbiFeatures, feature: ArbiFeature): boolean {\n const source = features[feature]\n if (!source) return true\n if ('local' in source) return source.local.get(feature) ?? source.default\n return source.default\n}\n\nexport interface LocalFeatureStoreOptions {\n /** The storage key. */\n key: string\n /** Where the values are kept. Default: `localStorage` (memory only where it cannot be used). */\n storage?: Pick<Storage, 'getItem' | 'setItem'> | null\n /**\n * Values an earlier build kept elsewhere, brought in once: on the first load that finds nothing\n * saved under `key`. The store is hydrated once they are in.\n */\n migrate?: () => Promise<Record<string, boolean> | null>\n}\n\nfunction defaultStorage(): Pick<Storage, 'getItem' | 'setItem'> | null {\n try {\n return typeof localStorage === 'undefined' ? null : localStorage\n } catch {\n return null\n }\n}\n\nfunction parseValues(raw: string | null): Record<string, boolean> | null {\n if (raw === null) return null\n try {\n const parsed: unknown = JSON.parse(raw)\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return {}\n return Object.fromEntries(\n Object.entries(parsed).filter(([, value]) => typeof value === 'boolean')\n ) as Record<string, boolean>\n } catch {\n return {}\n }\n}\n\n/** Feature switches kept on this device, read synchronously once saved. */\nexport function createLocalFeatureStore({\n key,\n storage = defaultStorage(),\n migrate,\n}: LocalFeatureStoreOptions): LocalFeatureStore {\n const listeners = new Set<() => void>()\n const read = (): string | null => {\n try {\n return storage?.getItem(key) ?? null\n } catch {\n return null\n }\n }\n const write = () => {\n try {\n storage?.setItem(key, JSON.stringify(values))\n } catch {\n // Memory only: private windows, a full quota.\n }\n }\n const notify = () => listeners.forEach((listener) => listener())\n\n const saved = parseValues(read())\n let values: Record<string, boolean> = saved ?? {}\n let hydrated = saved !== null || !migrate\n const ready: Promise<void> = hydrated\n ? Promise.resolve()\n : migrate!()\n .catch((error: unknown) => {\n console.warn(`[features] The saved ${key} could not be brought in`, error)\n return null\n })\n .then((earlier) => {\n // A switch flipped while the earlier values were read wins.\n values = { ...(earlier ?? {}), ...values }\n write()\n hydrated = true\n notify()\n })\n\n return {\n get: (feature) => values[feature],\n set(feature, on) {\n if (values[feature] === on) return\n values = { ...values, [feature]: on }\n write()\n notify()\n },\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n isHydrated: () => hydrated,\n hydrated: () => ready,\n }\n}\n","/**\n * The workspace's conversation list in the query cache (`arbiQueryKeys.conversations(ws)`).\n *\n * The list is kept current by its writers, not by re-reading it: the workspace open seeds it, every\n * write patches the rows it changed (with the server's version when the server returns one), and\n * the workspace's events invalidate it. These are the writers, for every host and kit, so that one\n * list serves the whole app.\n *\n * Conversation ids are unique across workspaces, so a row is patched (or looked up) by its id in\n * every cached list; only adding a row needs to name its workspace.\n */\n\nimport type { QueryClient, QueryKey } from '@tanstack/react-query'\nimport type { ConversationResponse } from '@arbidocs/sdk/browser'\nimport { arbiQueryKeys } from './hooks'\n\n/** Whether a query key is a workspace's conversation list (not its threads, activity or usage). */\nexport function isConversationListKey(queryKey: QueryKey): boolean {\n return queryKey.length === 3 && queryKey[0] === 'arbi' && queryKey[1] === 'conversations'\n}\n\nconst LISTS = {\n predicate: (query: { queryKey: QueryKey }) => isConversationListKey(query.queryKey),\n}\n\n/** Apply `reducer` to a workspace's cached list (an absent list counts as empty). */\nexport function patchConversationList(\n queryClient: QueryClient,\n workspaceId: string | null | undefined,\n reducer: (old: ConversationResponse[]) => ConversationResponse[]\n): void {\n if (!workspaceId) return\n const key = arbiQueryKeys.conversations(workspaceId)\n const current = queryClient.getQueryData<ConversationResponse[]>(key) ?? []\n const next = reducer(current)\n if (next !== current) queryClient.setQueryData(key, next)\n}\n\n/** Put a new conversation at the top of its workspace's list (replacing an earlier copy). */\nexport function addConversationToList(\n queryClient: QueryClient,\n workspaceId: string | null | undefined,\n conversation: ConversationResponse\n): void {\n patchConversationList(queryClient, workspaceId, (old) => [\n conversation,\n ...old.filter((c) => c.external_id !== conversation.external_id),\n ])\n}\n\n/** Change one conversation's row, wherever it is listed. */\nexport function patchConversation(\n queryClient: QueryClient,\n conversationId: string,\n update: Partial<ConversationResponse> | ((row: ConversationResponse) => ConversationResponse)\n): void {\n queryClient.setQueriesData<ConversationResponse[]>(LISTS, (old) => {\n if (!old?.some((c) => c.external_id === conversationId)) return old\n return old.map((c) =>\n c.external_id === conversationId\n ? typeof update === 'function'\n ? update(c)\n : { ...c, ...update }\n : c\n )\n })\n}\n\n/** Take a conversation out of every list. */\nexport function removeConversationFromLists(\n queryClient: QueryClient,\n conversationId: string\n): void {\n queryClient.setQueriesData<ConversationResponse[]>(LISTS, (old) =>\n old?.some((c) => c.external_id === conversationId)\n ? old.filter((c) => c.external_id !== conversationId)\n : old\n )\n}\n\n/** Read the lists again: a change the cache cannot know (the server shared a thread, a run ended). */\nexport function invalidateConversationLists(queryClient: QueryClient): Promise<void> {\n return queryClient.invalidateQueries(LISTS)\n}\n\n/** A conversation's row, from whichever cached list holds it. */\nexport function findConversation(\n queryClient: QueryClient,\n conversationId: string | null | undefined\n): ConversationResponse | undefined {\n if (!conversationId) return undefined\n for (const [, list] of queryClient.getQueriesData<ConversationResponse[]>(LISTS)) {\n const row = list?.find((c) => c.external_id === conversationId)\n if (row) return row\n }\n return undefined\n}\n\n/** Whether the conversation is shared with the workspace, from the cached lists (false when unknown). */\nexport function isConversationShared(\n queryClient: QueryClient,\n conversationId: string | null | undefined\n): boolean {\n return findConversation(queryClient, conversationId)?.is_shared ?? false\n}\n\n/**\n * Refresh what hangs off one conversation (its activity, usage, pending access): not the lists, and\n * not its threads, which the chat engine and the conversation's events keep current.\n */\nexport function invalidateConversationDetails(\n queryClient: QueryClient,\n conversationId: string\n): Promise<void> {\n return queryClient.invalidateQueries({\n predicate: ({ queryKey }) =>\n queryKey[0] === 'arbi' &&\n queryKey[1] === 'conversations' &&\n queryKey[2] === conversationId &&\n queryKey[3] !== 'threads',\n })\n}\n","/**\n * The one chat cache: my chat list (`arbiQueryKeys.chatList()`, raw — titles and last messages\n * stay ciphertext) and each open thread (`arbiQueryKeys.chatThread(id)`, decrypted). Every reader\n * (the messenger, unread badges, the DM kit, a call's chat) and every writer (a send, a delete,\n * a WebSocket event) goes through here, so one change shows everywhere at once.\n *\n * Live events: `<ArbiWebSocketProvider>` applies them itself; a host with its own socket passes each\n * chat event to `applyChatEvent`.\n */\n\nimport type { QueryClient } from '@tanstack/react-query'\nimport type { WsAgentDmStatusMessage } from '@arbidocs/client'\nimport { chats, type Arbi } from '@arbidocs/sdk/browser'\nimport { arbiQueryKeys } from '../hooks'\n\nexport type ChatResponse = chats.ChatResponse\nexport type ChatMessageResponse = chats.ChatMessageResponse\nexport type DecryptedChatMessage = chats.DecryptedChatMessage\nexport type ChatAttachmentFile = chats.ChatAttachmentFile\nexport type ChatMemberKey = chats.ChatMemberKey\n\n/** A message in an open thread; an optimistic row carries a local id until the server echoes it. */\nexport type ChatThreadMessage = DecryptedChatMessage & { optimistic?: boolean }\n\n/** An open thread's cache entry: decrypted messages, oldest first. */\nexport interface ChatThreadData {\n messages: ChatThreadMessage[]\n /** False once the oldest page has been loaded. */\n hasMore: boolean\n}\n\n/** The signed-in user's external id, or null when signed out. */\nexport function myChatId(arbi: Arbi): string | null {\n try {\n return arbi.user.identity()?.externalId ?? null\n } catch {\n return null\n }\n}\n\n// ── The chat open in a messenger ────────────────────────────────────────────\n\n/** The chat open in a messenger, if any: its new messages do not count as unread. */\nlet openChatExtId: string | null = null\nexport function setOpenChat(chatExtId: string | null): void {\n openChatExtId = chatExtId\n}\nexport function getOpenChat(): string | null {\n return openChatExtId\n}\n\n// ── Writers ─────────────────────────────────────────────────────────────────\n\nfunction byActivity(a: ChatResponse, b: ChatResponse): number {\n const at = Date.parse(a.last_message_at ?? a.created_at)\n const bt = Date.parse(b.last_message_at ?? b.created_at)\n return bt - at\n}\n\n/** My chats as the cache holds them (empty until the list has loaded). */\nexport function cachedChats(queryClient: QueryClient): ChatResponse[] {\n return queryClient.getQueryData<ChatResponse[]>(arbiQueryKeys.chatList()) ?? []\n}\n\n/** One of my chats from the cache, if the list has it. */\nexport function cachedChat(queryClient: QueryClient, chatExtId: string): ChatResponse | undefined {\n return cachedChats(queryClient).find((c) => c.external_id === chatExtId)\n}\n\n/** Put a chat (new or updated) into the list, keeping the list newest first. */\nexport function upsertChat(queryClient: QueryClient, chat: ChatResponse): void {\n queryClient.setQueryData<ChatResponse[]>(arbiQueryKeys.chatList(), (old) => {\n if (!old) return old\n const rest = old.filter((c) => c.external_id !== chat.external_id)\n return [chat, ...rest].sort(byActivity)\n })\n}\n\n/** Drop a chat I left or lost, with its thread. */\nexport function removeChat(queryClient: QueryClient, chatExtId: string): void {\n queryClient.setQueryData<ChatResponse[]>(arbiQueryKeys.chatList(), (old) =>\n old?.filter((c) => c.external_id !== chatExtId)\n )\n queryClient.removeQueries({ queryKey: arbiQueryKeys.chatThread(chatExtId) })\n}\n\n/** Append a message to a loaded thread (deduplicated, replacing its optimistic copy). */\nexport function appendToThread(\n queryClient: QueryClient,\n chatExtId: string,\n message: ChatThreadMessage,\n replaceOptimisticId?: string\n): void {\n queryClient.setQueryData<ChatThreadData>(arbiQueryKeys.chatThread(chatExtId), (old) => {\n if (!old) return old\n const without = old.messages.filter(\n (m) => m.external_id !== message.external_id && m.external_id !== replaceOptimisticId\n )\n return { ...old, messages: [...without, message] }\n })\n}\n\n/** Take a message out of a loaded thread (a failed optimistic send). */\nexport function dropFromThread(queryClient: QueryClient, chatExtId: string, messageExtId: string) {\n queryClient.setQueryData<ChatThreadData>(arbiQueryKeys.chatThread(chatExtId), (old) =>\n old ? { ...old, messages: old.messages.filter((m) => m.external_id !== messageExtId) } : old\n )\n}\n\n/** Replace a message of a loaded thread where it stands (no-op when it is not loaded). */\nfunction replaceInThread(\n queryClient: QueryClient,\n chatExtId: string,\n message: ChatThreadMessage\n): void {\n queryClient.setQueryData<ChatThreadData>(arbiQueryKeys.chatThread(chatExtId), (old) => {\n if (!old || !old.messages.some((m) => m.external_id === message.external_id)) return old\n return {\n ...old,\n messages: old.messages.map((m) => (m.external_id === message.external_id ? message : m)),\n }\n })\n}\n\n/** Swap a chat's `last_message` in the list when it is still `message`. */\nfunction patchLastMessage(\n queryClient: QueryClient,\n chatExtId: string,\n message: ChatMessageResponse\n): void {\n queryClient.setQueryData<ChatResponse[]>(arbiQueryKeys.chatList(), (old) =>\n old?.map((c) =>\n c.external_id === chatExtId && c.last_message?.external_id === message.external_id\n ? { ...c, last_message: message }\n : c\n )\n )\n}\n\n/**\n * Delete one of my messages. The server keeps it in place as a tombstone (`deleted: true`), so\n * the cache marks it the same way at once — a refetch then returns exactly what is shown. Rolled\n * back (and rethrown) when the delete fails.\n */\nexport async function deleteChatMessage(\n arbi: Arbi,\n queryClient: QueryClient,\n chatExtId: string,\n messageExtId: string\n): Promise<void> {\n const thread = queryClient.getQueryData<ChatThreadData>(arbiQueryKeys.chatThread(chatExtId))\n const before = thread?.messages.find((m) => m.external_id === messageExtId)\n const listed = cachedChat(queryClient, chatExtId)\n const wasLast = listed?.last_message?.external_id === messageExtId ? listed.last_message : null\n if (before) replaceInThread(queryClient, chatExtId, chats.chatMessageTombstone(before))\n if (wasLast) patchLastMessage(queryClient, chatExtId, chats.chatMessageTombstone(wasLast))\n try {\n await arbi.chats.deleteMessage(chatExtId, messageExtId)\n } catch (err) {\n if (before) replaceInThread(queryClient, chatExtId, before)\n if (wasLast) patchLastMessage(queryClient, chatExtId, wasLast)\n throw err\n }\n}\n\n// ── Live events ─────────────────────────────────────────────────────────────\n\n/** A message changed in place (a deleted message's tombstone): update it where it stands. */\nasync function applyChangedMessage(\n arbi: Arbi,\n queryClient: QueryClient,\n chatExtId: string,\n message: ChatMessageResponse\n): Promise<void> {\n if (message.deleted) {\n replaceInThread(queryClient, chatExtId, chats.chatMessageTombstone(message))\n patchLastMessage(queryClient, chatExtId, chats.chatMessageTombstone(message))\n return\n }\n const chat = cachedChat(queryClient, chatExtId)\n if (!chat || !queryClient.getQueryData(arbiQueryKeys.chatThread(chatExtId))) return\n try {\n replaceInThread(queryClient, chatExtId, await arbi.chats.decrypt(chat, message))\n } catch (err) {\n console.error('[chats] could not decrypt a changed message', err)\n }\n}\n\n/** `chat.message`: a new message in one of my chats. */\nexport async function applyChatMessage(\n arbi: Arbi,\n queryClient: QueryClient,\n chatExtId: string,\n message: ChatMessageResponse\n): Promise<void> {\n // Not new: a tombstone replaces the message where it stands (never unread, never the preview).\n if (message.deleted) return applyChangedMessage(arbi, queryClient, chatExtId, message)\n const me = myChatId(arbi)\n const chat = cachedChat(queryClient, chatExtId)\n if (!chat) {\n // A chat I was just added to: fetch the list fresh.\n void queryClient.invalidateQueries({ queryKey: arbiQueryKeys.chatList() })\n return\n }\n const mine = message.sender_ext_id === me\n const isOpen = openChatExtId === chatExtId\n upsertChat(queryClient, {\n ...chat,\n last_message: message,\n last_message_at: message.created_at,\n unread_count: mine ? 0 : isOpen ? chat.unread_count : chat.unread_count + 1,\n members: mine\n ? chat.members.map((m) =>\n m.user.external_id === me ? { ...m, last_read_at: message.created_at } : m\n )\n : chat.members,\n })\n if (queryClient.getQueryData(arbiQueryKeys.chatThread(chatExtId))) {\n try {\n appendToThread(queryClient, chatExtId, await arbi.chats.decrypt(chat, message))\n } catch (err) {\n console.error('[chats] could not decrypt a live message', err)\n }\n }\n}\n\n/** `chat.read`: a member's watermark moved (a read receipt). */\nexport function applyChatRead(\n arbi: Arbi,\n queryClient: QueryClient,\n chatExtId: string,\n userExtId: string,\n lastReadAt: string\n): void {\n const me = myChatId(arbi)\n queryClient.setQueryData<ChatResponse[]>(arbiQueryKeys.chatList(), (old) =>\n old?.map((c) =>\n c.external_id !== chatExtId\n ? c\n : {\n ...c,\n unread_count: userExtId === me ? 0 : c.unread_count,\n members: c.members.map((m) =>\n m.user.external_id === userExtId ? { ...m, last_read_at: lastReadAt } : m\n ),\n }\n )\n )\n}\n\n/**\n * `chat.updated`: members, keys, title or a deleted message changed. Refetch the chat; when it now\n * needs a new epoch and I am still in it, seal one. A changed `message` (a deleted message's\n * tombstone) is applied in place; without one the open thread is reloaded.\n */\nexport async function applyChatUpdated(\n arbi: Arbi,\n queryClient: QueryClient,\n chatExtId: string,\n message?: ChatMessageResponse | null\n): Promise<void> {\n if (message) await applyChangedMessage(arbi, queryClient, chatExtId, message)\n let chat: ChatResponse\n try {\n chat = await arbi.chats.get(chatExtId)\n } catch {\n // Removed from it (or it is gone).\n removeChat(queryClient, chatExtId)\n return\n }\n if (chat.rotation_required) {\n // Best effort: whoever posts next rotates first.\n chat = await arbi.chats.ready(chat).catch(() => chat)\n }\n upsertChat(queryClient, chat)\n // Members, keys or a new epoch: reload the open thread. A changed message is already in place.\n if (!message)\n void queryClient.invalidateQueries({ queryKey: arbiQueryKeys.chatThread(chatExtId) })\n}\n\n/** My own agent's progress on a message I sent it (`agent_dm_status`). */\nexport type AgentDmStatus = WsAgentDmStatusMessage\n\n/** `agent_dm_status`: remember my agent's latest progress (read by `useAgentDmStatus`). */\nexport function applyAgentDmStatus(queryClient: QueryClient, status: AgentDmStatus): void {\n if (!['queued', 'working', 'done', 'failed'].includes(status.status)) return\n queryClient.setQueryData(arbiQueryKeys.agentDmStatus(status.agent_ext_id), status)\n}\n\n/** The chat events of the server's WebSocket, as `applyChatEvent` takes them. */\nexport type ChatEvent =\n | { type: 'chat.message'; chat_ext_id: string; message: ChatMessageResponse }\n | { type: 'chat.read'; chat_ext_id: string; user_ext_id: string; last_read_at: string }\n | { type: 'chat.updated'; chat_ext_id: string; message?: ChatMessageResponse | null }\n | AgentDmStatus\n\n/** Apply one chat event of the server's WebSocket to the cache. Other messages are ignored. */\nexport async function applyChatEvent(\n arbi: Arbi,\n queryClient: QueryClient,\n event: { type: string }\n): Promise<void> {\n const e = event as ChatEvent\n if (e.type === 'chat.message')\n return applyChatMessage(arbi, queryClient, e.chat_ext_id, e.message)\n if (e.type === 'chat.read')\n return applyChatRead(arbi, queryClient, e.chat_ext_id, e.user_ext_id, e.last_read_at)\n if (e.type === 'chat.updated')\n return applyChatUpdated(arbi, queryClient, e.chat_ext_id, e.message)\n if (e.type === 'agent_dm_status') applyAgentDmStatus(queryClient, e)\n}\n","import type { QueryClient } from '@tanstack/react-query'\nimport type { calls } from '@arbidocs/sdk/browser'\nimport { arbiQueryKeys, callQueryKeys } from '../query-keys'\n\nexport { callQueryKeys }\n\n/**\n * Calls are still experimental on this deployment (`useServerCallsExperimental` outside React,\n * from the cached config): unknown reads as experimental.\n */\nexport function serverCallsExperimental(queryClient: QueryClient): boolean {\n return queryClient.getQueryData<calls.CallsConfig>(callQueryKeys.config())?.experimental !== false\n}\n\n/**\n * A call started or ended (or changed): re-read it, and the activity of the thread that logs it\n * (its log card).\n */\nexport function invalidateCall(\n queryClient: QueryClient,\n message: { call_ext_id: string; log_conversation_ext_id?: string | null }\n): void {\n void queryClient.invalidateQueries({ queryKey: callQueryKeys.call(message.call_ext_id) })\n const thread = message.log_conversation_ext_id\n if (thread) {\n void queryClient.invalidateQueries({ queryKey: arbiQueryKeys.conversationActivity(thread) })\n }\n}\n","/**\n * The host's moderation of a call, as the moderated participant sees it.\n *\n * `POST /v1/calls/{id}/participants/{identity}/mute` mutes one of my tracks server-side and tells me\n * twice: a SERVER-sent LiveKit data message on {@link CALL_MODERATION_TOPIC}\n * (`{action: \"muted\", track, by_identity, by_name}`, plaintext, no content) and WS `call.muted`\n * (`{call_ext_id, track, by_name}`). Either may arrive first (or alone); one toast is shown.\n */\n\nexport const CALL_MODERATION_TOPIC = 'arbi.moderation'\n\nexport type ModeratedTrack = 'audio' | 'video'\n\nexport interface CallMutedNotice {\n track: ModeratedTrack\n byName: string | null\n}\n\nfunction trackOf(value: unknown): ModeratedTrack | null {\n return value === 'audio' || value === 'video' ? value : null\n}\n\n/** A moderation data packet's \"muted\" notice; null when malformed or another action. */\nexport function mutedNoticeFromPacket(payload: Uint8Array): CallMutedNotice | null {\n try {\n const parsed: unknown = JSON.parse(new TextDecoder().decode(payload))\n if (!parsed || typeof parsed !== 'object') return null\n const p = parsed as { action?: unknown; track?: unknown; by_name?: unknown }\n const track = trackOf(p.track)\n if (p.action !== 'muted' || !track) return null\n return { track, byName: typeof p.by_name === 'string' ? p.by_name : null }\n } catch {\n return null\n }\n}\n\n/** The toast for a notice. */\nexport function mutedNoticeText(notice: CallMutedNotice): string {\n return notice.track === 'audio' ? 'The host muted you' : 'The host turned off your camera'\n}\n\n/** Both channels report one mute within this window; a later mute of the same track toasts again. */\nexport const MUTED_DEDUPE_MS = 5_000\n\n/**\n * A notifier that shows one toast per mute: the data message and the WS event for the same track\n * within {@link MUTED_DEDUPE_MS} collapse into one.\n */\nexport function createMutedNotifier(\n show: (text: string) => void,\n now: () => number = Date.now\n): (notice: CallMutedNotice) => void {\n const last = new Map<ModeratedTrack, number>()\n return (notice) => {\n const at = now()\n const prev = last.get(notice.track)\n if (prev !== undefined && at - prev < MUTED_DEDUPE_MS) return\n last.set(notice.track, at)\n show(mutedNoticeText(notice))\n }\n}\n\n/** A WS `call.muted` as a notice for the open room; null when malformed. */\nexport function mutedNoticeFromEvent(\n raw: unknown\n): (CallMutedNotice & { callExtId: string }) | null {\n if (!raw || typeof raw !== 'object') return null\n const r = raw as { call_ext_id?: unknown; track?: unknown; by_name?: unknown }\n const track = trackOf(r.track)\n if (typeof r.call_ext_id !== 'string' || !track) return null\n return {\n callExtId: r.call_ext_id,\n track,\n byName: typeof r.by_name === 'string' ? r.by_name : null,\n }\n}\n","/**\n * The call session: the one active call of this browser tab, the queue of incoming rings, lobby\n * knocks waiting for this user's decision, and which calls the server says are live.\n *\n * Session-only and in memory: a LiveKit token and the call key never reach storage, so a reload\n * starts empty. One per tab (a tab is in at most one call), so it is a module store read with\n * {@link useCallSession} and changed with {@link callSession}'s actions, from React and from plain\n * code (call actions, the WebSocket) alike. Signing out calls {@link resetCallSession}.\n */\n\nimport { useSyncExternalStore } from 'react'\nimport type { calls } from '@arbidocs/sdk/browser'\n\n/** The devices picked before joining (the pre-join). */\nexport interface JoinChoices {\n audioEnabled: boolean\n videoEnabled: boolean\n audioDeviceId?: string\n videoDeviceId?: string\n}\n\n/** Microphone on, camera off: an answered ring joins with these (calls are audio-first). */\nexport const DEFAULT_JOIN_CHOICES: JoinChoices = { audioEnabled: true, videoEnabled: false }\n\nexport interface ActiveCall {\n callExtId: string\n /** The call's E2EE key K (32 bytes). */\n key: Uint8Array\n linkVersion: number\n /** Display label for the dock header. */\n title: string | null\n isCreator: boolean\n /** The call's E2E chat (DM, group or meeting chat), when it has one. */\n chatExtId: string | null\n /** Thread call: the thread with the log card. */\n logConversationExtId: string | null\n /** Thread call: its meeting thread, once made. */\n meetingConversationExtId: string | null\n /** Thread call: the workspace it is locked to. */\n lockedWorkspaceExtId: string | null\n /**\n * `prejoin` shows the device check, `knocking` waits in the lobby, `connected` mounts the room.\n * A webinar's audience also has `webinar-waiting` (\"Starting soon\": not live yet, joins by itself\n * once it is) and `webinar-ended` (the host ended it).\n */\n phase: 'prejoin' | 'knocking' | 'connected' | 'webinar-waiting' | 'webinar-ended'\n tokenResponse: calls.CallTokenResponse | null\n choices: JoinChoices | null\n /** While knocking: stops waiting for admission. */\n knock: { abort(): void } | null\n /** Users to ring once we join: user ext id → K sealed to them. */\n ringOnJoin: Record<string, string> | null\n /** A personal invite link's token (`/call/<id>?i=…`): redeemed for a token, no lobby. */\n inviteToken?: string | null\n minimized: boolean\n startedAt: number\n /**\n * A webinar's stage changed under us (moved to the audience, or stepped down): the server removes\n * us from the room and we join again with a new token, so that room's disconnect is not a leave.\n */\n rejoining?: boolean\n}\n\n/** A ring waiting for accept/decline. */\nexport interface IncomingCall extends Omit<calls.CallRingingMessage, 'type'> {\n receivedAt: number\n}\n\n/** Someone in the lobby of a call this user can admit people to. */\nexport interface PendingKnock {\n callExtId: string\n knockId: string\n displayName: string\n /** The signed-in user asking (everyone in a call has an account). */\n userExtId: string\n receivedAt: number\n}\n\n/** Where a live call belongs (from `call.started`). */\nexport interface LiveCall {\n chatExtId: string | null\n logConversationExtId: string | null\n meetingConversationExtId: string | null\n}\n\nexport interface CallSessionState {\n active: ActiveCall | null\n incoming: IncomingCall[]\n knocks: PendingKnock[]\n /** Calls the server says are live (`call.started` … `call.ended`), by call ext id. */\n liveCalls: Record<string, LiveCall>\n}\n\nexport type NewActiveCall = Omit<ActiveCall, 'minimized' | 'startedAt'> & { minimized?: boolean }\n\nexport const EMPTY_CALL_SESSION: CallSessionState = {\n active: null,\n incoming: [],\n knocks: [],\n liveCalls: {},\n}\n\nlet state: CallSessionState = EMPTY_CALL_SESSION\nconst listeners = new Set<() => void>()\n\nfunction update(next: (current: CallSessionState) => CallSessionState): void {\n const changed = next(state)\n if (changed === state) return\n state = changed\n for (const listener of listeners) listener()\n}\n\n/** The call session's state and actions (usable outside React). */\nexport const callSession = {\n getState: (): CallSessionState => state,\n\n subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n\n setActiveCall(next: NewActiveCall): void {\n update((s) => ({\n ...s,\n active: { ...next, minimized: next.minimized ?? false, startedAt: Date.now() },\n // Joining a call answers any ring for it; knocks belong to the previous call.\n incoming: s.incoming.filter((ring) => ring.call_ext_id !== next.callExtId),\n knocks: s.knocks.filter((k) => k.callExtId === next.callExtId),\n }))\n },\n\n /** Patch the active call (no-op when it is a different call or none). */\n updateActiveCall(callExtId: string, patch: Partial<ActiveCall>): void {\n update((s) =>\n s.active?.callExtId === callExtId ? { ...s, active: { ...s.active, ...patch } } : s\n )\n },\n\n endActiveCall(): void {\n update((s) => {\n s.active?.knock?.abort()\n return { ...s, active: null, knocks: [] }\n })\n },\n\n setCallMinimized(minimized: boolean): void {\n update((s) => (s.active ? { ...s, active: { ...s.active, minimized } } : s))\n },\n\n /** Queue a ring. A repeat ring for the same call replaces the earlier entry. */\n enqueueIncomingCall(ring: Omit<IncomingCall, 'receivedAt'>): void {\n update((s) => {\n // Already in this call — nothing to ring.\n if (s.active?.callExtId === ring.call_ext_id) return s\n return {\n ...s,\n incoming: [\n ...s.incoming.filter((r) => r.call_ext_id !== ring.call_ext_id),\n { ...ring, receivedAt: Date.now() },\n ],\n }\n })\n },\n\n removeIncomingCall(callExtId: string): void {\n update((s) => ({ ...s, incoming: s.incoming.filter((r) => r.call_ext_id !== callExtId) }))\n },\n\n addKnock(knock: Omit<PendingKnock, 'receivedAt'>): void {\n update((s) => {\n // Only prompt for the call we are in.\n if (s.active?.callExtId !== knock.callExtId) return s\n return {\n ...s,\n knocks: [\n ...s.knocks.filter((k) => k.knockId !== knock.knockId),\n { ...knock, receivedAt: Date.now() },\n ],\n }\n })\n },\n\n removeKnock(knockId: string): void {\n update((s) => ({ ...s, knocks: s.knocks.filter((k) => k.knockId !== knockId) }))\n },\n\n markCallLive(callExtId: string, where: LiveCall): void {\n update((s) => ({ ...s, liveCalls: { ...s.liveCalls, [callExtId]: where } }))\n },\n\n /** Call ended: forget it and drop any unanswered ring / knock for it. */\n markCallEnded(callExtId: string): void {\n update((s) => {\n const liveCalls = { ...s.liveCalls }\n delete liveCalls[callExtId]\n return {\n ...s,\n liveCalls,\n incoming: s.incoming.filter((r) => r.call_ext_id !== callExtId),\n knocks: s.knocks.filter((k) => k.callExtId !== callExtId),\n }\n })\n },\n}\n\n/** Forget everything (signing out). A call in progress is left by its room unmounting. */\nexport function resetCallSession(): void {\n update(() => EMPTY_CALL_SESSION)\n}\n\n/** Read the call session in React, re-rendering when the selected value changes. */\nexport function useCallSession<T>(selector: (state: CallSessionState) => T): T {\n return useSyncExternalStore(\n callSession.subscribe,\n () => selector(state),\n () => selector(state)\n )\n}\n\n/** The id of the thread's live call, if the server reports one (a thread call). */\nexport function threadLiveCallId(s: CallSessionState, conversationExtId: string): string | null {\n const found = Object.entries(s.liveCalls).find(\n ([, c]) => c.logConversationExtId === conversationExtId\n )\n return found ? found[0] : null\n}\n\n/** Whether the server reports a live call logged in this thread. */\nexport function threadHasLiveCall(s: CallSessionState, conversationExtId: string): boolean {\n return threadLiveCallId(s, conversationExtId) !== null\n}\n\n/** The id of the chat's live call (DM, group or meeting chat), if the server reports one. */\nexport function chatLiveCallId(s: CallSessionState, chatExtId: string): string | null {\n const found = Object.entries(s.liveCalls).find(([, c]) => c.chatExtId === chatExtId)\n return found ? found[0] : null\n}\n\n/** Whether the server reports a live call in this chat (DM, group or meeting chat). */\nexport function chatHasLiveCall(s: CallSessionState, chatExtId: string): boolean {\n return chatLiveCallId(s, chatExtId) !== null\n}\n","/**\n * The calls' WebSocket events, applied in one place ({@link applyCallEvent}):\n *\n * - rings, declines, lobby knocks and live/ended calls update the call session and the cache\n * (the thread log card and chat call cards re-read their call);\n * - what only an open view cares about — recording, transcription, webinar and moderation\n * changes — is relayed to whoever listens ({@link onCallEvent}: the call room, recording cards,\n * the webinar waiting screen).\n *\n * `<ArbiWebSocketProvider>` applies them to every `call.*` message; a host with its own socket\n * passes each one to `applyCallEvent`.\n */\n\nimport type { QueryClient } from '@tanstack/react-query'\nimport { calls } from '@arbidocs/sdk/browser'\nimport { notify } from '../notifications/notify-store'\nimport { callQueryKeys, invalidateCall, serverCallsExperimental } from './keys'\nimport { mutedNoticeFromEvent, type CallMutedNotice } from './moderation'\nimport { callSession } from './session'\n\n/** What each relayed event carries to its listeners. */\nexport interface CallRoomEvents {\n 'call.recording': calls.CallRecordingState & { call_ext_id: string }\n 'call.recording_deleted': { call_ext_id: string; recording_ext_id: string }\n 'call.transcription': calls.CallTranscriptionState & { call_ext_id: string }\n 'call.webinar': calls.CallWebinarMessage\n 'call.muted': CallMutedNotice & { callExtId: string }\n}\n\ntype RoomEventType = keyof CallRoomEvents\ntype Listener = (message: never) => void\nconst listeners = new Map<RoomEventType, Set<Listener>>()\n\n/** Listen to one relayed call event while a view is open. Returns the unsubscribe. */\nexport function onCallEvent<K extends RoomEventType>(\n type: K,\n listener: (message: CallRoomEvents[K]) => void\n): () => void {\n let set = listeners.get(type)\n if (!set) listeners.set(type, (set = new Set()))\n set.add(listener as Listener)\n return () => {\n set.delete(listener as Listener)\n }\n}\n\nfunction relay<K extends RoomEventType>(type: K, message: CallRoomEvents[K] | null): void {\n if (!message) return\n for (const listener of listeners.get(type) ?? []) {\n ;(listener as (m: CallRoomEvents[K]) => void)(message)\n }\n}\n\nexport interface ApplyCallEventOptions {\n /** My user id: my own ring echoed to another tab or device of mine is not a ring. */\n me?: string | null\n /**\n * Whether this person is rung. A host that keeps calls behind a switch passes false while it\n * is off: the caller then just sees no answer, and a call link still opens. Ignored where the\n * deployment made calls a regular feature (`experimental: false`): everyone is rung there.\n */\n acceptRings?: boolean\n}\n\nconst str = (value: unknown): string | null => (typeof value === 'string' && value ? value : null)\n\n/**\n * Apply one WebSocket event. Returns false for an event that is not a call event (so a host's\n * dispatcher can go on), true otherwise.\n */\nexport function applyCallEvent(\n queryClient: QueryClient,\n event: { type: string },\n options: ApplyCallEventOptions = {}\n): boolean {\n const m = event as Record<string, unknown> & { type: string }\n const callExtId = str(m.call_ext_id)\n switch (m.type) {\n case 'call.ringing': {\n const ring = m as unknown as calls.CallRingingMessage\n const optedOut = options.acceptRings === false && serverCallsExperimental(queryClient)\n if (optedOut || !callExtId || !ring.sealed_key) return true\n if (options.me && ring.caller_ext_id === options.me) return true\n callSession.enqueueIncomingCall({\n call_ext_id: ring.call_ext_id,\n room_name: ring.room_name,\n caller_ext_id: ring.caller_ext_id,\n caller_name: ring.caller_name,\n chat_ext_id: ring.chat_ext_id ?? null,\n log_conversation_ext_id: ring.log_conversation_ext_id ?? null,\n meeting_conversation_ext_id: ring.meeting_conversation_ext_id ?? null,\n workspace_ext_id: ring.workspace_ext_id ?? null,\n context: ring.context ?? null,\n sealed_key: ring.sealed_key,\n link_version: ring.link_version,\n title: ring.title ?? null,\n })\n return true\n }\n case 'call.declined':\n if (callExtId && callSession.getState().active?.callExtId === callExtId) {\n notify.info('Call declined', { record: false, ambient: false })\n }\n return true\n case 'call.started':\n if (!callExtId) return true\n callSession.markCallLive(callExtId, {\n chatExtId: str(m.chat_ext_id),\n logConversationExtId: str(m.log_conversation_ext_id),\n meetingConversationExtId: str(m.meeting_conversation_ext_id),\n })\n invalidateCall(queryClient, {\n call_ext_id: callExtId,\n log_conversation_ext_id: str(m.log_conversation_ext_id),\n })\n return true\n case 'call.ended':\n if (!callExtId) return true\n callSession.markCallEnded(callExtId)\n invalidateCall(queryClient, {\n call_ext_id: callExtId,\n log_conversation_ext_id: str(m.log_conversation_ext_id),\n })\n return true\n case 'call.knock': {\n // Lobby: someone asks to join the call we are in (we are its creator or an invitee).\n const knock = m as unknown as calls.CallKnockMessage\n if (!callExtId) return true\n callSession.addKnock({\n callExtId,\n knockId: knock.knock_id,\n displayName: knock.display_name,\n userExtId: knock.user_ext_id,\n })\n return true\n }\n case 'call.knock_resolved': {\n // Another host answered: clear the prompt here too.\n const knockId = str(m.knock_id)\n if (knockId) callSession.removeKnock(knockId)\n return true\n }\n case 'call.recording': {\n // The call's recording cards re-read their recordings.\n if (callExtId)\n void queryClient.invalidateQueries({ queryKey: callQueryKeys.recordingsOf(callExtId) })\n const state = calls.recordingStateFrom(m)\n relay('call.recording', callExtId && state ? { ...state, call_ext_id: callExtId } : null)\n return true\n }\n case 'call.recording_updated': {\n // A stopped recording's tail landed (a part committed after the stop): its cards re-read it.\n if (callExtId)\n void queryClient.invalidateQueries({ queryKey: callQueryKeys.recordingsOf(callExtId) })\n return true\n }\n case 'call.recording_deleted': {\n if (callExtId)\n void queryClient.invalidateQueries({ queryKey: callQueryKeys.recordingsOf(callExtId) })\n const recordingExtId = str(m.recording_ext_id)\n relay(\n 'call.recording_deleted',\n callExtId && recordingExtId\n ? { call_ext_id: callExtId, recording_ext_id: recordingExtId }\n : null\n )\n return true\n }\n case 'call.transcription': {\n const state = calls.transcriptionStateFrom(m)\n relay('call.transcription', callExtId && state ? { ...state, call_ext_id: callExtId } : null)\n return true\n }\n case 'call.webinar':\n relay(\n 'call.webinar',\n callExtId && typeof m.live === 'boolean'\n ? {\n type: 'call.webinar',\n call_ext_id: callExtId,\n live: m.live,\n chat: m.chat === 'everyone' ? 'everyone' : 'panelists',\n }\n : null\n )\n return true\n case 'call.muted':\n relay('call.muted', mutedNoticeFromEvent(m))\n return true\n default:\n return false\n }\n}\n","/**\n * Encryption at rest for the offline mailbox: the offline sealer of @arbidocs/client under\n * the `arbi-offline-mail-v1` namespace. Record keys and folder buckets are blind keyed hashes, so\n * folder names (an IMAP \"Clients/Acme\") or message ids never appear in plaintext either.\n */\n\nimport { createOfflineSealer } from '@arbidocs/client'\n\n/** The device has no unlocked identity key (signed out, or session storage cleared). */\nexport class OfflineMailLockedError extends Error {\n constructor() {\n super('Sign in to open the mail saved on this device')\n this.name = 'OfflineMailLockedError'\n }\n}\n\nconst sealer = createOfflineSealer('arbi-offline-mail-v1', {\n locked: () => new OfflineMailLockedError(),\n})\n\n/** Forget the derived keys (sign-out, or the offline data was removed). */\nexport const forgetOfflineMailKeys = sealer.forget\nexport const sealBytes = sealer.sealBytes\nexport const openBytes = sealer.openBytes\nexport const sealJson = sealer.sealJson\nexport const openJson = sealer.openJson\n/** A keyed hash of `parts`, used as an IndexedDB key so ids never appear in plaintext. */\nexport const blind = sealer.blind\n","/**\n * A generic encrypted-at-rest IndexedDB store: one object store of sealed records plus a small\n * plaintext `meta` store. A record's plaintext fields are only what IndexedDB needs to find and\n * evict it (a blind key, its kind, a blind bucket, a sort time, its size, last access); the content\n * is in `data`, sealed by the caller (see ./crypto). The store itself is crypto-agnostic.\n *\n * Used by the offline mailbox (`arbi-offline-mail`) and the offline calendar (`arbi-offline-calendar`).\n */\n\nconst DB_VERSION = 1\nconst RECORDS = 'records'\nconst META = 'meta'\n\nexport interface StoredRecord<K extends string = string> {\n /** Blind key (see crypto.blind). */\n key: string\n kind: K\n /** Blind group key. */\n bucket: string\n sortAt: number\n /** Ciphertext bytes, for the storage quota. */\n size: number\n /** Last read (ms), for least-recently-used eviction. */\n lastAccess: number\n /** Sealed contents. */\n data: Uint8Array\n}\n\nexport interface RecordStore<K extends string> {\n readonly dbName: string\n putRecord(record: StoredRecord<K>): Promise<void>\n getRecord(key: string): Promise<StoredRecord<K> | undefined>\n hasRecord(key: string): Promise<boolean>\n deleteRecords(keys: string[]): Promise<void>\n recordsInBucket(bucket: string): Promise<StoredRecord<K>[]>\n recordsOfKind(kind: K): Promise<StoredRecord<K>[]>\n allRecordSizes(): Promise<Array<Pick<StoredRecord<K>, 'key' | 'kind' | 'size'>>>\n /** Keys, kinds and last access (no data), for LRU eviction. */\n allRecordAccess(): Promise<Array<Pick<StoredRecord<K>, 'key' | 'kind' | 'lastAccess'>>>\n touchRecord(key: string): Promise<void>\n getMeta<T>(key: string): Promise<T | undefined>\n setMeta(key: string, value: unknown): Promise<void>\n /** Delete the whole database. */\n deleteDatabase(): Promise<void>\n}\n\nfunction promisify<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result)\n request.onerror = () => reject(request.error)\n })\n}\n\nexport function createRecordStore<K extends string>(dbName: string): RecordStore<K> {\n let dbPromise: Promise<IDBDatabase> | null = null\n\n function openDB(): Promise<IDBDatabase> {\n if (dbPromise) return dbPromise\n let request: IDBOpenDBRequest\n try {\n request = indexedDB.open(dbName, DB_VERSION)\n } catch (error) {\n // No indexedDB here, or opening it threw (e.g. blocked storage): nothing is kept, so the\n // next call tries again.\n return Promise.reject(error)\n }\n dbPromise = new Promise((resolve, reject) => {\n request.onerror = () => {\n dbPromise = null\n reject(request.error)\n }\n request.onsuccess = () => {\n const db = request.result\n // Another tab deleting the database (sign-out) must not be blocked by this handle.\n db.onversionchange = () => {\n db.close()\n dbPromise = null\n }\n resolve(db)\n }\n request.onupgradeneeded = () => {\n const db = request.result\n if (!db.objectStoreNames.contains(RECORDS)) {\n const records = db.createObjectStore(RECORDS, { keyPath: 'key' })\n records.createIndex('bucket', 'bucket')\n records.createIndex('kind', 'kind')\n }\n if (!db.objectStoreNames.contains(META)) db.createObjectStore(META, { keyPath: 'key' })\n }\n })\n return dbPromise\n }\n\n async function run<T>(\n store: string,\n mode: IDBTransactionMode,\n fn: (s: IDBObjectStore) => IDBRequest<T>\n ): Promise<T> {\n const db = await openDB()\n return promisify(fn(db.transaction(store, mode).objectStore(store)))\n }\n\n async function scan<T>(pick: (r: StoredRecord<K>) => T): Promise<T[]> {\n const db = await openDB()\n const store = db.transaction(RECORDS, 'readonly').objectStore(RECORDS)\n return new Promise((resolve, reject) => {\n const out: T[] = []\n const cursor = store.openCursor()\n cursor.onerror = () => reject(cursor.error)\n cursor.onsuccess = () => {\n const c = cursor.result\n if (!c) return resolve(out)\n out.push(pick(c.value as StoredRecord<K>))\n c.continue()\n }\n })\n }\n\n const self: RecordStore<K> = {\n dbName,\n async putRecord(record) {\n await run(RECORDS, 'readwrite', (s) => s.put(record))\n },\n async getRecord(key) {\n return (await run(RECORDS, 'readonly', (s) => s.get(key))) as StoredRecord<K> | undefined\n },\n async hasRecord(key) {\n return (await run(RECORDS, 'readonly', (s) => s.count(key))) > 0\n },\n async deleteRecords(keys) {\n if (keys.length === 0) return\n const db = await openDB()\n const tx = db.transaction(RECORDS, 'readwrite')\n const store = tx.objectStore(RECORDS)\n for (const key of keys) store.delete(key)\n await new Promise<void>((resolve, reject) => {\n tx.oncomplete = () => resolve()\n tx.onerror = () => reject(tx.error)\n tx.onabort = () => reject(tx.error)\n })\n },\n async recordsInBucket(bucket) {\n return (await run(RECORDS, 'readonly', (s) =>\n s.index('bucket').getAll(bucket)\n )) as StoredRecord<K>[]\n },\n async recordsOfKind(kind) {\n return (await run(RECORDS, 'readonly', (s) =>\n s.index('kind').getAll(kind)\n )) as StoredRecord<K>[]\n },\n allRecordSizes() {\n return scan(({ key, kind, size }) => ({ key, kind, size }))\n },\n allRecordAccess() {\n return scan(({ key, kind, lastAccess }) => ({ key, kind, lastAccess }))\n },\n async touchRecord(key) {\n const record = await self.getRecord(key)\n if (record) await self.putRecord({ ...record, lastAccess: Date.now() })\n },\n async getMeta<T>(key: string) {\n const row = (await run(META, 'readonly', (s) => s.get(key))) as { value: T } | undefined\n return row?.value\n },\n async setMeta(key, value) {\n await run(META, 'readwrite', (s) => s.put({ key, value }))\n },\n async deleteDatabase() {\n const db = await dbPromise?.catch(() => null)\n db?.close()\n dbPromise = null\n await new Promise<void>((resolve, reject) => {\n const request = indexedDB.deleteDatabase(dbName)\n request.onsuccess = () => resolve()\n request.onerror = () => reject(request.error)\n // Another tab still holds it open: it closes on versionchange, then this completes.\n request.onblocked = () => {}\n })\n },\n }\n return self\n}\n","/**\n * The offline mailbox's IndexedDB store (the generic encrypted record store, see\n * ../../offline-store/store, under the `arbi-offline-mail` database).\n *\n * Record plaintext fields are only what IndexedDB needs to find and evict a record: a blind key, its\n * kind, a blind bucket (which folder/account it belongs to), a sort time, its size and last read.\n * Everything else — subjects, addresses, bodies, attachments, drafts, queued mail and changes — is sealed in\n * `data` with the key from ./crypto. `meta` holds non-content facts: owner, sync times and counts.\n */\n\nimport { createRecordStore, type StoredRecord as GenericRecord } from '../../offline-store/store'\n\nexport const OFFLINE_MAIL_DB = 'arbi-offline-mail'\n\nexport type RecordKind =\n | 'accounts'\n | 'policy'\n | 'folders'\n | 'header'\n | 'body'\n | 'attachment'\n | 'draft'\n | 'outbox'\n | 'change'\n\nexport type StoredRecord = GenericRecord<RecordKind>\n\nconst store = createRecordStore<RecordKind>(OFFLINE_MAIL_DB)\n\nexport const putRecord = store.putRecord\nexport const getRecord = store.getRecord\nexport const hasRecord = store.hasRecord\nexport const deleteRecords = store.deleteRecords\nexport const recordsInBucket = store.recordsInBucket\nexport const recordsOfKind = store.recordsOfKind\n/** Keys + sizes only (no data), for quota accounting without loading ciphertext. */\nexport const allRecordSizes = store.allRecordSizes\nexport const touchRecord = store.touchRecord\nexport const getMeta = store.getMeta\nexport const setMeta = store.setMeta\n/** Delete the whole database (sign-out / \"Remove offline data\"). */\nexport const deleteOfflineMailDatabase = store.deleteDatabase\n","/**\n * Per-device offline mail settings and the shared change/status signal.\n *\n * Settings are a device preference, not mail content, so they live in localStorage keyed by the\n * user (two people sharing a browser keep separate choices). The mail itself is wiped on sign-out;\n * the preference survives so the mailbox re-syncs after the user signs back in on this device\n * (decided in #2187: they hold no mail, and keeping them favours the person coming back).\n *\n * By default the last 7 days of mail are kept; the person can choose a shorter or longer window,\n * or none (\"Off\": only drafts and the Outbox stay on the device).\n *\n * `onOfflineMailChange` notifies this tab and, through a BroadcastChannel, other open tabs whenever\n * the offline store changes (sync progress, Outbox, drafts), so every Mail view stays current.\n */\n\n/** How far back mail is kept on this device. */\nexport type OfflineMailScope = '1d' | '7d' | '30d' | '90d'\n\nexport interface OfflineMailSettings {\n /** Keep recent mail on this device (the window is `scope`). Off: only drafts and the Outbox. */\n enabled: boolean\n /** How far back mail is kept. */\n scope: OfflineMailScope\n /** Attachments of one message larger than this (MB, in total) are not saved. */\n attachmentCapMb: number\n /** Total space the offline mailbox may use (MB); the least recently opened attachments go first. */\n quotaMb: number\n}\n\n/** On, for the last week: recent mail can be read and answered offline unless the person opts out. */\nexport const DEFAULT_OFFLINE_MAIL_SETTINGS: OfflineMailSettings = {\n enabled: true,\n scope: '7d',\n attachmentCapMb: 25,\n quotaMb: 1024,\n}\n\nexport const SCOPE_DAYS: Record<OfflineMailScope, number> = {\n '1d': 1,\n '7d': 7,\n '30d': 30,\n '90d': 90,\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000\n\n/** The oldest receive time (ms) kept on the device under `settings`. */\nexport function offlineWindowStart(settings: OfflineMailSettings, now = Date.now()): number {\n return now - SCOPE_DAYS[settings.scope] * DAY_MS\n}\n\n/** Whether a message received at `receivedAt` belongs on the device (an undated one always does). */\nexport function inOfflineWindow(\n settings: OfflineMailSettings,\n receivedAt: string | undefined,\n now = Date.now()\n): boolean {\n const at = receivedAt ? Date.parse(receivedAt) || 0 : 0\n return settings.enabled && (!at || at >= offlineWindowStart(settings, now))\n}\n\n/**\n * Saved choices from earlier versions: a longer window (\"Last year\", \"All mail\") becomes the\n * longest one offered now, anything unknown the default.\n */\nfunction normalize(saved: Partial<OfflineMailSettings>): OfflineMailSettings {\n const merged = { ...DEFAULT_OFFLINE_MAIL_SETTINGS, ...saved }\n const scope: string = merged.scope\n if (scope in SCOPE_DAYS) return merged\n const longer = scope === '365d' || scope === 'all'\n return { ...merged, scope: longer ? '90d' : DEFAULT_OFFLINE_MAIL_SETTINGS.scope }\n}\n\nconst STORAGE_PREFIX = 'arbi-offline-mail-settings:'\n\n/** The signed-in user whose settings apply (`null`: signed out, nothing is kept). */\nexport type OfflineMailUser = string | null | undefined\n\n/** Nobody signed in (or a restored tab whose profile isn't loaded yet): keep no mail. */\nconst SIGNED_OUT: OfflineMailSettings = { ...DEFAULT_OFFLINE_MAIL_SETTINGS, enabled: false }\n\nexport function getOfflineMailSettings(userExtId: OfflineMailUser): OfflineMailSettings {\n if (!userExtId) return SIGNED_OUT\n const key = STORAGE_PREFIX + userExtId\n try {\n const raw = localStorage.getItem(key)\n return raw\n ? normalize(JSON.parse(raw) as Partial<OfflineMailSettings>)\n : DEFAULT_OFFLINE_MAIL_SETTINGS\n } catch {\n return DEFAULT_OFFLINE_MAIL_SETTINGS\n }\n}\n\nexport function setOfflineMailSettings(\n userExtId: OfflineMailUser,\n patch: Partial<OfflineMailSettings>\n): OfflineMailSettings {\n const next = { ...getOfflineMailSettings(userExtId), ...patch }\n if (userExtId) {\n try {\n localStorage.setItem(STORAGE_PREFIX + userExtId, JSON.stringify(next))\n } catch {\n // Storage full/blocked: the choice applies for this session only.\n }\n }\n notifyOfflineMailChange('settings')\n return next\n}\n\nexport type OfflineMailChange =\n 'settings' | 'mailbox' | 'outbox' | 'drafts' | 'changes' | 'status' | 'wiped'\n\nconst listeners = new Set<(change: OfflineMailChange) => void>()\nconst CHANNEL = 'arbi-offline-mail'\nlet channel: BroadcastChannel | null = null\n\nfunction getChannel(): BroadcastChannel | null {\n if (channel || typeof BroadcastChannel === 'undefined') return channel\n channel = new BroadcastChannel(CHANNEL)\n channel.onmessage = (event: MessageEvent<OfflineMailChange>) => {\n for (const listener of listeners) listener(event.data)\n }\n return channel\n}\n\nexport function notifyOfflineMailChange(change: OfflineMailChange): void {\n for (const listener of listeners) listener(change)\n try {\n getChannel()?.postMessage(change)\n } catch {\n // Channel closed (tab unloading): other tabs pick the change up on their next read.\n }\n}\n\nexport function onOfflineMailChange(listener: (change: OfflineMailChange) => void): () => void {\n getChannel()\n listeners.add(listener)\n return () => listeners.delete(listener)\n}\n","/**\n * Removing the offline mailbox: on sign-out, on \"Remove offline data\", and when a different account\n * signs in on this device.\n */\n\nimport { forgetOfflineMailKeys } from './crypto'\nimport { deleteOfflineMailDatabase, getMeta, setMeta } from './store'\nimport { notifyOfflineMailChange } from './settings'\n\nconst OWNER_KEY = 'owner'\n\n/** Delete everything the offline mailbox stored on this device (mail, drafts, Outbox, status). */\nexport async function wipeOfflineMail(): Promise<void> {\n forgetOfflineMailKeys()\n await deleteOfflineMailDatabase()\n notifyOfflineMailChange('wiped')\n}\n\n/**\n * Bind the store to the account signing in. A store written by someone else on this device is\n * removed (it is sealed to their key, so it is unreadable anyway, but it must not take space or\n * leave their queued mail behind).\n */\nexport async function claimOfflineMail(userExtId: string): Promise<void> {\n const owner = await getMeta<string>(OWNER_KEY)\n if (owner && owner !== userExtId) await wipeOfflineMail()\n await setMeta(OWNER_KEY, userExtId)\n}\n","/**\n * Profile: the signed-in user's name and picture.\n *\n * Two layers, like the rest of the package. The bound hooks (`useProfile`, `useUpdateProfile`) go\n * through the account client in scope (`useAccountClient`: the host configuration's `account`, else\n * one on its `arbi`). The controllers (`useNameEditor`, `useAvatarCrop`) own only the form\n * state and take the save as a function, so an app that has its own API layer (ARBI-frontend)\n * binds the same `ProfileCard` / `AvatarPicker` parts without a provider.\n */\n\nimport { useCallback, useEffect, useState } from 'react'\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { LoginProfile } from '@arbidocs/sdk/browser'\nimport { arbiQueryKeys } from '../hooks'\nimport { useAccountClient } from './client'\n\n// ── View model ──────────────────────────────────────────────────────────────\n\nexport interface ProfileView {\n givenName: string\n familyName: string | null\n picture: string | null\n email: string\n /** The sign-in provider manages the email (it cannot be changed here). */\n isSso: boolean\n /** Verified tick: passed an ID check under this name, not expired. */\n isIdentityVerified?: boolean\n /** Given and family name joined; empty while the profile has no name. */\n fullName: string\n}\n\n/** The profile as the UI reads it. `fullName` is the profile name, never the email. */\nexport function toProfileView(profile: LoginProfile): ProfileView {\n return {\n ...profile,\n fullName: [profile.givenName, profile.familyName]\n .map((part) => part?.trim())\n .filter(Boolean)\n .join(' '),\n }\n}\n\n// ── SDK-bound hooks ─────────────────────────────────────────────────────────\n\n/** The signed-in user's profile; `null` when the session has none (restored without a login). */\nexport function useProfile(options?: { enabled?: boolean }) {\n const { client, enabled } = useAccountClient()\n const qc = useQueryClient()\n // The profile changes outside these hooks too (another tab, an ID check, a re-login).\n useEffect(\n () => client.onProfileChange(() => qc.setQueryData(arbiQueryKeys.profile(), client.profile())),\n [client, qc]\n )\n return useQuery({\n queryKey: arbiQueryKeys.profile(),\n queryFn: (): ProfileView | null => client.profile(),\n enabled: (options?.enabled ?? true) && enabled,\n staleTime: Infinity,\n })\n}\n\nexport interface UpdateProfileVars {\n givenName?: string\n /** `''` clears it. */\n familyName?: string\n /** A data URL or URL; `''` clears it (the backend caps it at 100 KB). */\n picture?: string\n}\n\n/**\n * Save the name and/or picture (PATCH /v1/user/profile). The profile query is updated in place and\n * the workspace list (member rows carry the name) is refreshed, so every surface follows.\n */\nexport function useUpdateProfile() {\n const { client } = useAccountClient()\n const qc = useQueryClient()\n return useMutation({\n mutationFn: async (vars: UpdateProfileVars) => {\n await client.updateProfile(vars)\n return client.profile()\n },\n onSuccess: (profile) => {\n qc.setQueryData(arbiQueryKeys.profile(), profile)\n // A rename can drop (or restore) the verified tick.\n void qc.invalidateQueries({ queryKey: arbiQueryKeys.identity() })\n return qc.invalidateQueries({ queryKey: arbiQueryKeys.workspaces() })\n },\n })\n}\n\n// ── Controllers (no SDK) ────────────────────────────────────────────────────\n\nexport interface UseNameEditorOptions {\n givenName: string | null | undefined\n familyName: string | null | undefined\n /** Persist the new name. Reject to keep the editor open (see `error`). */\n save: (name: { givenName: string; familyName: string }) => Promise<unknown> | unknown\n}\n\nexport interface NameEditor {\n isEditing: boolean\n givenName: string\n familyName: string\n onGivenNameChange: (value: string) => void\n onFamilyNameChange: (value: string) => void\n onStartEdit: () => void\n onSave: () => void\n onCancel: () => void\n isSaving: boolean\n /** The last failed save. */\n error: unknown\n}\n\n/**\n * State of the inline name editor of `ProfileCard`: start edit (seeded from the current name),\n * save (trimmed; a blank given name is not saved), cancel. Spread it into `ProfileCard`'s `name`\n * with `display`.\n */\nexport function useNameEditor({ givenName, familyName, save }: UseNameEditorOptions): NameEditor {\n const [isEditing, setIsEditing] = useState(false)\n const [given, setGiven] = useState('')\n const [family, setFamily] = useState('')\n const [isSaving, setIsSaving] = useState(false)\n const [error, setError] = useState<unknown>(null)\n\n const onStartEdit = useCallback(() => {\n setGiven(givenName ?? '')\n setFamily(familyName ?? '')\n setError(null)\n setIsEditing(true)\n }, [givenName, familyName])\n\n const onSave = useCallback(() => {\n const trimmedGiven = given.trim()\n if (!trimmedGiven) return\n setIsSaving(true)\n setError(null)\n void Promise.resolve()\n .then(() => save({ givenName: trimmedGiven, familyName: family.trim() }))\n .then(() => setIsEditing(false))\n .catch((e: unknown) => setError(e))\n .finally(() => setIsSaving(false))\n }, [given, family, save])\n\n return {\n isEditing,\n givenName: given,\n familyName: family,\n onGivenNameChange: setGiven,\n onFamilyNameChange: setFamily,\n onStartEdit,\n onSave,\n onCancel: () => setIsEditing(false),\n isSaving,\n error,\n }\n}\n\n/** Largest picture file accepted before cropping. */\nexport const MAX_AVATAR_FILE_BYTES = 5 * 1024 * 1024\n\nexport type AvatarFileProblem = 'not-image' | 'too-large' | 'unreadable'\n\nexport interface UseAvatarCropOptions {\n /** Persist the cropped picture (a data URL). */\n upload: (dataUrl: string) => Promise<unknown> | unknown\n /** Report a rejected file or a failed read; default is silent (the picker just does not open). */\n onProblem?: (problem: AvatarFileProblem) => void\n maxBytes?: number\n}\n\nexport interface AvatarCrop {\n /** Pass to `AvatarPicker.onFileChosen`. */\n onFileChosen: (file: File) => void\n /** The chosen image awaiting a crop (a data URL), else `null`. */\n pendingImage: string | null\n /** Pass to the crop dialog's `onComplete`. */\n onCropComplete: (result: { dataUrl: string }) => void\n onCropCancel: () => void\n isUploading: boolean\n}\n\n/**\n * The choose, validate, crop, upload sequence behind `AvatarPicker`: the picker hands over a file,\n * this checks it (image, size) and reads it for the crop dialog, then uploads the cropped data URL.\n */\nexport function useAvatarCrop({\n upload,\n onProblem,\n maxBytes = MAX_AVATAR_FILE_BYTES,\n}: UseAvatarCropOptions): AvatarCrop {\n const [pendingImage, setPendingImage] = useState<string | null>(null)\n const [isUploading, setIsUploading] = useState(false)\n\n const onFileChosen = useCallback(\n (file: File) => {\n if (!file.type.startsWith('image/')) return onProblem?.('not-image')\n if (file.size > maxBytes) return onProblem?.('too-large')\n const reader = new FileReader()\n reader.onload = () => setPendingImage(reader.result as string)\n reader.onerror = () => onProblem?.('unreadable')\n reader.readAsDataURL(file)\n },\n [maxBytes, onProblem]\n )\n\n const onCropComplete = useCallback(\n (result: { dataUrl: string }) => {\n setPendingImage(null)\n setIsUploading(true)\n void Promise.resolve()\n .then(() => upload(result.dataUrl))\n .catch(() => {\n // The host's `upload` reports its own failure; the picker just stops spinning.\n })\n .finally(() => setIsUploading(false))\n },\n [upload]\n )\n\n return {\n onFileChosen,\n pendingImage,\n onCropComplete,\n onCropCancel: () => setPendingImage(null),\n isUploading,\n }\n}\n","/**\n * The account client the account hooks and the settings blocks talk to.\n *\n * An {@link AccountClient} is the signed-in user's account: profile, email, password, sessions,\n * sign-out, deletion and the ID check. {@link arbiAccountClient} builds the default one from an\n * `Arbi` (used when the host configuration has no `account`). A host with its own session and API\n * client (ARBI-frontend) gives its own as the host configuration's `account`, as it does for `mail`.\n */\n\nimport { useMemo } from 'react'\nimport type { components } from '@arbidocs/client'\nimport type { Arbi } from '@arbidocs/sdk/browser'\nimport { useArbiHost } from '../host/config'\nimport { clearSealedDrafts } from '../session/drafts'\nimport { clearChatArtifacts } from '../chat/chat-artifacts'\nimport { wipeOfflineMail } from '../mail/offline/wipe'\nimport type { IdentityVerificationApi } from './identity'\nimport type { PasswordChangeVars } from './password'\nimport { toProfileView, type ProfileView, type UpdateProfileVars } from './profile'\n\nexport type AccountSession = components['schemas']['SessionInfoResponse']\n\nexport interface AccountSignOutOptions {\n /** End every session on the server first, this device's included. */\n everywhere?: boolean\n /** Why: a person's choice, or the account was just closed. */\n reason?: 'manual' | 'account-deleted'\n}\n\nexport interface AccountClient {\n /** The signed-in user's profile; `null` when there is none. */\n profile(): ProfileView | null\n /** Be told when {@link profile} changes. Returns the unsubscribe. */\n onProfileChange(listener: () => void): () => void\n /** Save the name and/or picture; the profile follows. */\n updateProfile(vars: UpdateProfileVars): Promise<void>\n /** Send the verification code to the new address (not for SSO accounts). */\n requestEmailChange(newEmail: string): Promise<void>\n /** Confirm the new address with the emailed code; the profile follows. */\n confirmEmailChange(newEmail: string, code: string): Promise<void>\n /** Change (or, in recovery mode, set) the master password. */\n changePassword(vars: PasswordChangeVars): Promise<void>\n /** Available only when the deployment permits local sign-in. */\n requestSsoDisconnectCode?(newEmail: string): Promise<void>\n disconnectSso?(vars: { newEmail: string; code?: string; password: string }): Promise<void>\n listSessions(): Promise<AccountSession[]>\n /** The id this device's session is listed under, or `null`. */\n currentSessionId(): string | null\n /** End one of the user's sessions at once (not this device's: that is {@link signOut}). */\n terminateSession(sessionId: string): Promise<void>\n /** Sign this device out of the host (and, `everywhere`, every other session first). */\n signOut(options?: AccountSignOutOptions): Promise<void>\n /** Close the account. Rejects with the backend's refusal when something blocks it. */\n requestDeletion(): Promise<unknown>\n /** The ID check behind the verified tick. */\n identity: IdentityVerificationApi\n}\n\nexport interface AccountClientContextValue {\n client: AccountClient\n /** Account reads run only while this is true (signed in). */\n enabled: boolean\n}\n\nfunction arbiProfile(arbi: Arbi): ProfileView | null {\n const profile = arbi.user.profile()\n return profile ? toProfileView(profile) : null\n}\n\n/**\n * The account client over an `Arbi`. `overrides` replace any of its calls: a host that keeps its\n * own signed-in state passes `signOut` (and `profile`) so the SDK and the host stay in step.\n */\nexport function arbiAccountClient(\n arbi: Arbi,\n overrides: Partial<AccountClient> = {}\n): AccountClient {\n return {\n profile: () => arbiProfile(arbi),\n onProfileChange: (listener) => arbi.user.onProfileChange(() => listener()),\n updateProfile: async (vars) => {\n await arbi.profile.update({\n ...(vars.givenName !== undefined && { given_name: vars.givenName.trim() }),\n ...(vars.familyName !== undefined && { family_name: vars.familyName.trim() }),\n ...(vars.picture !== undefined && { picture: vars.picture }),\n })\n },\n requestEmailChange: (newEmail) => arbi.account.requestEmailChange(newEmail),\n confirmEmailChange: async (newEmail, code) => {\n await arbi.account.confirmEmailChange(newEmail, code)\n },\n changePassword: (vars) => arbi.account.changePassword(vars),\n listSessions: () => arbi.sessions.list(),\n currentSessionId: () => arbi.sessions.currentId(),\n terminateSession: (sessionId) => arbi.sessions.terminate(sessionId),\n signOut: async ({ everywhere } = {}) => {\n try {\n if (everywhere) await arbi.sessions.logoutAll()\n else await arbi.logout()\n } finally {\n // The drafts and mail kept on the device never outlive the session.\n clearSealedDrafts()\n clearChatArtifacts()\n await wipeOfflineMail().catch(() => {})\n }\n },\n requestDeletion: () => arbi.account.requestDeletion(),\n identity: {\n status: () => arbi.identity.status(),\n purchase: () => arbi.identity.purchase(),\n startSession: () => arbi.identity.startSession(),\n refresh: () => arbi.identity.refresh(),\n acceptName: () => arbi.identity.acceptName(),\n remove: () => arbi.identity.remove(),\n },\n ...overrides,\n }\n}\n\nconst NO_ACCOUNT_ERROR = 'The account needs an <ArbiHost> with `account` or an `arbi`'\nconst refuse = () => Promise.reject(new Error(NO_ACCOUNT_ERROR))\n\nconst NO_ACCOUNT: AccountClientContextValue = {\n client: {\n profile: () => null,\n onProfileChange: () => () => {},\n updateProfile: refuse,\n requestEmailChange: refuse,\n confirmEmailChange: refuse,\n changePassword: refuse,\n listSessions: refuse,\n currentSessionId: () => null,\n terminateSession: refuse,\n signOut: refuse,\n requestDeletion: refuse,\n identity: {\n status: refuse,\n purchase: refuse,\n startSession: refuse,\n refresh: refuse,\n acceptName: refuse,\n remove: refuse,\n },\n },\n enabled: false,\n}\n\nconst ALWAYS = () => true\n\n/**\n * The account client in scope: the host configuration's `account`, else one on its `arbi` session\n * (`arbiAccountClient`).\n */\nexport function useAccountClient(): AccountClientContextValue {\n const { account, arbi } = useArbiHost()\n const fallback = useMemo(() => (arbi ? arbiAccountClient(arbi) : null), [arbi])\n // The host's hook is fixed for its configuration's life.\n const useEnabled = account?.useEnabled ?? ALWAYS\n const enabled = useEnabled()\n const client = account?.client\n const provided = useMemo(() => (client ? { client, enabled } : null), [client, enabled])\n if (provided) return provided\n // Neither: reads stay off and writes are refused rather than breaking the screen.\n if (!arbi || !fallback) return NO_ACCOUNT\n return { client: fallback, enabled: arbi.isLoggedIn }\n}\n","/**\n * Feature entitlements: native mail, the calendar and hosting calls are paid features (Pro on the hosted\n * service; internal users on a Box). Joining a call someone else hosts is never gated. The API refuses gated\n * routes with 403 and the same `reason`, so a UI hides what the API would refuse.\n */\n\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { account } from '@arbidocs/sdk/browser'\nimport { useArbiHost } from '../host/config'\nimport { arbiQueryKeys } from '../hooks'\n\nexport type FeatureEntitlements = account.FeatureEntitlements\nexport type EntitledFeature = account.EntitledFeature\nexport type FeatureRefusal = account.FeatureRefusal\n\n/**\n * The person's entitlements; `data` is undefined until known (treat that as not entitled), and\n * stays so under a host with no `arbi` (e.g. one that brings only its own mail client).\n */\nexport function useEntitlements() {\n const { arbi } = useArbiHost()\n return useQuery({\n queryKey: arbiQueryKeys.entitlements(),\n queryFn: () => arbi!.account.entitlements(),\n enabled: !!arbi?.isLoggedIn,\n // A plan change shows on the next read; the API enforces it at once either way.\n staleTime: 5 * 60 * 1000,\n })\n}\n\n/**\n * Read the entitlements again: after something that changes them (Pro bought, a plan change), so\n * the paid pages open at once instead of waiting out the cached copy.\n */\nexport function useInvalidateEntitlements(): () => Promise<void> {\n const qc = useQueryClient()\n return () => qc.invalidateQueries({ queryKey: arbiQueryKeys.entitlements() })\n}\n\n/** Whether the person may use `feature` (false while unknown, so nothing the API would refuse flashes up). */\nexport function useIsEntitled(feature: EntitledFeature): boolean {\n const { data } = useEntitlements()\n return data?.[feature] === true\n}\n\n/** Why the paid features are off, in the words a UI shows. */\nexport function entitlementRefusal(reason: FeatureRefusal | null | undefined): {\n title: string\n description: string\n} {\n switch (reason) {\n case 'external_user':\n return {\n title: 'Not available for external users',\n description:\n 'Mail, Calendar and hosting calls are for members of the firm. You can still join calls others host.',\n }\n case 'not_entitled':\n return {\n title: 'Not available on this account',\n description: 'Mail, Calendar and hosting calls are not available on this account.',\n }\n default:\n return {\n title: 'Available on Pro',\n description:\n 'Mail, Calendar and hosting calls come with a Pro plan. You can still join calls others host.',\n }\n }\n}\n","/**\n * The meeting chat of a call (ARBI `docs/meeting-chats-plan.md` §3–4).\n *\n * Every call has one place its conversation persists:\n * - an E2E `chats` row (`cht-`): the direct chat of a DM call, the group of a group call, or a\n * `meeting` chat made lazily on first use — its key is a fresh random epoch key sealed in this\n * browser to each member (creator + invitees by default), its title encrypted under it;\n * - a shared thread (`con-`) in the locked workspace of a thread call — encrypted by the server\n * with the workspace key like any thread, so @ARBI works there.\n *\n * `ensureMeetingChat` is get-or-create (`POST /v1/calls/{id}/chat`; the server serialises\n * concurrent creators, the loser gets the winner's chat).\n */\n\nimport {\n decryptCallTitle,\n encryptWithChatKey,\n generateChatKey,\n sealCallKey,\n sealChatKey,\n} from '@arbidocs/client'\nimport type { QueryClient } from '@tanstack/react-query'\nimport { chats, type Arbi, type calls } from '@arbidocs/sdk/browser'\nimport { arbiQueryKeys } from '../hooks'\nimport { cachedChats } from '../directmessages/chat-cache'\nimport { invalidateConversationLists } from '../conversation-list'\n\nexport type MeetingChatRef =\n | { kind: 'chat'; chatExtId: string }\n | { kind: 'thread'; conversationExtId: string; workspaceExtId: string }\n\n/** The meeting chat a call (or a `POST /chat` answer) already names, or null. */\nexport function meetingChatRefFrom(\n row: Pick<calls.CallResponse, 'chat_ext_id' | 'meeting_conversation_ext_id'> & {\n locked_workspace_ext_id?: string | null\n workspace_ext_id?: string | null\n }\n): MeetingChatRef | null {\n if (row.chat_ext_id) return { kind: 'chat', chatExtId: row.chat_ext_id }\n const workspaceExtId = row.workspace_ext_id ?? row.locked_workspace_ext_id\n if (row.meeting_conversation_ext_id && workspaceExtId) {\n return {\n kind: 'thread',\n conversationExtId: row.meeting_conversation_ext_id,\n workspaceExtId,\n }\n }\n return null\n}\n\n/** The keys the call row itself carries: its creator's and each invitee's. */\nexport function callMemberKeys(\n call: Pick<calls.CallResponse, 'creator_ext_id' | 'creator_encryption_public_key' | 'invitees'>\n): Map<string, string> {\n const keys = new Map<string, string>()\n if (call.creator_ext_id && call.creator_encryption_public_key) {\n keys.set(call.creator_ext_id, call.creator_encryption_public_key)\n }\n for (const i of call.invitees ?? []) {\n if (i.encryption_public_key) keys.set(i.user_ext_id, i.encryption_public_key)\n }\n return keys\n}\n\n/**\n * Public keys of the people a meeting chat is sealed to: the call row's own keys first (creator and\n * invitees — no dependence on my chat list or contacts), then, for extra members only, known chat\n * members and contacts.\n */\nasync function publicKeysFor(\n arbi: Arbi,\n queryClient: QueryClient,\n userExtIds: string[],\n known: Map<string, string>\n): Promise<Map<string, string>> {\n const wanted = new Set(userExtIds)\n const keys = new Map<string, string>([...known].filter(([id]) => wanted.has(id)))\n if (![...wanted].some((id) => !keys.has(id))) return keys\n for (const chat of cachedChats(queryClient)) {\n for (const m of chat.members) {\n if (\n wanted.has(m.user.external_id) &&\n !keys.has(m.user.external_id) &&\n m.user.encryption_public_key\n ) {\n keys.set(m.user.external_id, m.user.encryption_public_key)\n }\n }\n }\n if ([...wanted].some((id) => !keys.has(id))) {\n try {\n for (const c of await arbi.contacts.list()) {\n const u = c.user\n if (u && wanted.has(u.external_id) && u.encryption_public_key && !keys.has(u.external_id)) {\n keys.set(u.external_id, u.encryption_public_key)\n }\n }\n } catch (err) {\n console.warn('[calls] could not load contacts for the meeting chat', err)\n }\n }\n return keys\n}\n\n/**\n * The E2E body for a new meeting chat: a fresh chat key sealed to me and to each member whose\n * public key is known (the call's creator and invitees by default, with the keys the call row\n * carries; `extraMembers` beyond them also get K sealed to them, so they can always join), and the\n * call's title under the chat key. A member without a key is left out (they can be added later);\n * `members` may be empty — a chat with only me is valid.\n */\nexport async function buildMeetingChatBody(\n arbi: Arbi,\n queryClient: QueryClient,\n call: calls.CallResponse,\n callKey: Uint8Array,\n extraMembers: string[] = []\n): Promise<calls.CallChatCreate> {\n if (!arbi.isLoggedIn) throw new Error('Sign in to keep the meeting chat')\n const { externalId: me, publicKey: myPublicKey } = await arbi.chats.identity()\n const hosts = new Set<string>([\n call.creator_ext_id,\n ...(call.invitees ?? []).map((i) => i.user_ext_id),\n ])\n const memberIds = [...new Set([...hosts, ...extraMembers])].filter(\n (id) => id && id !== me && !chats.isAgentExtId(id)\n )\n const pubKeys = await publicKeysFor(arbi, queryClient, memberIds, callMemberKeys(call))\n const key = await generateChatKey()\n try {\n const members: calls.CallChatCreate['members'] = []\n for (const id of memberIds) {\n const pk = pubKeys.get(id)\n if (!pk) {\n // Nobody to seal to: they can be added to the chat later.\n console.warn('[calls] no public key for meeting-chat member', id)\n continue\n }\n members.push({\n user_ext_id: id,\n sealed_key: await sealChatKey(key, pk),\n ...(hosts.has(id) ? {} : { call_sealed_key: await sealCallKey(callKey, pk) }),\n })\n }\n const title = call.title ? await decryptCallTitle(call.title, callKey) : null\n return {\n title: await encryptWithChatKey(title?.trim() || 'Meeting', key),\n creator_sealed_key: await sealChatKey(key, myPublicKey),\n members,\n }\n } finally {\n key.fill(0)\n }\n}\n\nconst pending = new Map<string, Promise<MeetingChatRef>>()\n\n/** The call's meeting chat, made now if it has none. Concurrent callers share one request. */\nexport function ensureMeetingChat(\n arbi: Arbi,\n queryClient: QueryClient,\n callExtId: string,\n callKey: Uint8Array\n): Promise<MeetingChatRef> {\n const inFlight = pending.get(callExtId)\n if (inFlight) return inFlight\n const run = (async () => {\n const call = await arbi.calls.get(callExtId)\n const existing = meetingChatRefFrom(call)\n if (existing) return existing\n const body = call.workspace_locked\n ? undefined\n : await buildMeetingChatBody(arbi, queryClient, call, callKey)\n const answer = await arbi.calls.chat(callExtId, body)\n const ref = meetingChatRefFrom({\n ...answer,\n locked_workspace_ext_id: call.locked_workspace_ext_id,\n })\n if (!ref) throw new Error('The server did not return a meeting chat')\n // The new chat or meeting thread joins its list.\n if (ref.kind === 'chat')\n void queryClient.invalidateQueries({ queryKey: arbiQueryKeys.chatList() })\n else void invalidateConversationLists(queryClient)\n return ref\n })()\n pending.set(callExtId, run)\n // Keep a resolved ref cached for the session; forget failures so a retry can succeed.\n run.catch(() => pending.delete(callExtId))\n return run\n}\n\n/** Forget cached refs (tests; sign-out). */\nexport function clearMeetingChatCache(): void {\n pending.clear()\n}\n\n/** A call whose meeting chat is to be shown, with what is already known about it. */\nexport interface MeetingChatTarget {\n callExtId: string\n /** The call's E2E chat (DM / group / meeting), when known. */\n chatExtId?: string | null\n /** A thread call's meeting thread, when known, and its workspace. */\n meetingConversationExtId?: string | null\n workspaceExtId?: string | null\n /** K, when the caller already has it (else my sealed copy is opened). */\n callKey?: Uint8Array | null\n}\n\n/** The ref for a target whose chat is already known, or null when it must be made / looked up. */\nexport function knownMeetingChat(target: MeetingChatTarget): MeetingChatRef | null {\n if (target.chatExtId) return { kind: 'chat', chatExtId: target.chatExtId }\n if (target.meetingConversationExtId && target.workspaceExtId) {\n return {\n kind: 'thread',\n conversationExtId: target.meetingConversationExtId,\n workspaceExtId: target.workspaceExtId,\n }\n }\n return null\n}\n\n/** The target's meeting chat: the known one, else made on first use (needs K). */\nexport async function resolveMeetingChat(\n arbi: Arbi,\n queryClient: QueryClient,\n target: MeetingChatTarget\n): Promise<MeetingChatRef> {\n const known = knownMeetingChat(target)\n if (known) return known\n const key = target.callKey ?? (await arbi.calls.key(await arbi.calls.get(target.callExtId)))\n if (!key) throw new Error('You are not invited to this call')\n return ensureMeetingChat(arbi, queryClient, target.callExtId, key)\n}\n","/**\n * Reading stored call transcripts (light, so lists and message views can import it cheaply). A\n * transcript chat message's own format (`isTranscriptMessage`, `parseTranscriptContent`) is the\n * SDK's (`chats`). See `./transcription.ts` for how they are written.\n */\n\nimport { chats } from '@arbidocs/sdk/browser'\n\n/** The `transcript_segment` marker of a workspace thread message. */\nexport interface TranscriptSegmentMarker {\n segment_id: string\n speaker_ext_id: string\n started_at?: string | null\n}\n\n/**\n * Transcript messages in spoken order: by each line's `started_at` (when it was said), falling back to when it was\n * stored. Live lines are stored as they are spoken, but a recording's Transcribe stores one speaker track after\n * another, so storage order would list every line of one speaker before the next speaker's.\n */\nexport function sortTranscriptMessages<\n T extends { content?: string | null; created_at: string; decryptFailed?: boolean },\n>(messages: readonly T[]): T[] {\n const spokenAt = (m: T) => {\n const parsed = m.decryptFailed ? null : chats.parseTranscriptContent(m.content)\n const at = Date.parse(parsed?.started_at ?? m.created_at)\n return Number.isNaN(at) ? 0 : at\n }\n return messages\n .map((m, i) => ({ m, i, at: spokenAt(m) }))\n .sort((a, b) => a.at - b.at || a.i - b.i)\n .map(({ m }) => m)\n}\n\n/**\n * The `transcript_segment` marker of a thread message's `tools` (keyed by tool name, like\n * `workflow_step`), if it carries one.\n */\nexport function transcriptSegmentOf(tools: unknown): TranscriptSegmentMarker | undefined {\n if (!tools || typeof tools !== 'object') return undefined\n const record = tools as Record<string, unknown>\n const byKey = record.transcript_segment\n const candidates = byKey ? [byKey] : Object.values(record)\n for (const c of candidates) {\n if (!c || typeof c !== 'object') continue\n const t = c as Partial<TranscriptSegmentMarker> & { name?: unknown }\n if (c !== byKey && t.name !== 'transcript_segment') continue\n if (typeof t.segment_id !== 'string' || typeof t.speaker_ext_id !== 'string') continue\n return {\n segment_id: t.segment_id,\n speaker_ext_id: t.speaker_ext_id,\n started_at: typeof t.started_at === 'string' ? t.started_at : null,\n }\n }\n return undefined\n}\n\n/** One line of an exported transcript. */\nexport interface TranscriptExportLine {\n speaker: string\n /** ISO time the segment started; null when unknown. */\n at: string | null\n text: string\n}\n\nconst pad = (n: number) => String(n).padStart(2, '0')\n\n/** `hh:mm:ss` in local time, or '' for a missing/invalid time. */\nexport function clockTime(iso: string | null | undefined): string {\n if (!iso) return ''\n const d = new Date(iso)\n if (Number.isNaN(d.getTime())) return ''\n return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`\n}\n\n/**\n * A meeting transcript as ONE markdown comment: title, date, then `Speaker [hh:mm:ss]: text`\n * lines in time order (the host's \"Save transcript to thread\").\n */\nexport function formatTranscriptMarkdown(opts: {\n title: string | null | undefined\n /** The meeting's date (first line's time when omitted). */\n date?: Date | null\n lines: TranscriptExportLine[]\n}): string {\n const lines = [...opts.lines]\n .filter((l) => l.text.trim())\n .sort((a, b) => (Date.parse(a.at ?? '') || 0) - (Date.parse(b.at ?? '') || 0))\n const first = lines.find((l) => l.at)?.at\n const date = opts.date ?? (first ? new Date(first) : null)\n const heading = `## Transcript: ${opts.title?.trim() || 'Meeting'}`\n const dateLine = date\n ? `_${date.toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })}_`\n : null\n const body = lines.map((l) => {\n const time = clockTime(l.at)\n // One paragraph per line; collapse inner newlines so a line never breaks the layout.\n return `**${l.speaker}**${time ? ` [${time}]` : ''}: ${l.text.trim().replace(/\\s*\\n\\s*/g, ' ')}`\n })\n return [heading, ...(dateLine ? [dateLine] : []), ...body].join('\\n\\n')\n}\n\n/**\n * A workspace meeting thread's transcript: its `transcript_segment` comments as export lines (the\n * speaker from the members list, the text without its \"Name: \" prefix), deduped by segment id.\n */\nexport function threadTranscriptLines(\n messages: { content?: string | null; created_at?: string | null; tools?: unknown }[],\n nameOf: (speakerExtId: string) => string | null\n): TranscriptExportLine[] {\n const seen = new Set<string>()\n const lines: TranscriptExportLine[] = []\n for (const m of messages) {\n const marker = transcriptSegmentOf(m.tools)\n if (!marker || seen.has(marker.segment_id)) continue\n seen.add(marker.segment_id)\n const name = nameOf(marker.speaker_ext_id)\n lines.push({\n speaker: name || 'Former member',\n at: marker.started_at ?? m.created_at ?? null,\n text: stripSpeakerPrefix(m.content ?? '', name),\n })\n }\n return lines\n}\n\n/** A thread comment's text (\"Name: text\") without its speaker prefix. */\nexport function stripSpeakerPrefix(content: string, speakerName: string | null): string {\n if (speakerName && content.startsWith(`${speakerName}: `)) {\n return content.slice(speakerName.length + 2)\n }\n return content\n}\n\n// ── A recording's transcript supersedes the live one ────────────────────────\n\n/** The segment id of a line transcribed from a recording (`rec-<track>-…`), not live. */\nexport function isRecordingSegmentId(segmentId: string | null | undefined): boolean {\n return typeof segmentId === 'string' && segmentId.startsWith('rec-')\n}\n\n/** A recording's time window and its tracks (from `GET /v1/calls/{id}/recordings`). */\nexport interface RecordingWindow {\n trackExtIds: readonly string[]\n /** ISO start of the recording. */\n startedAt: string\n /** ISO stop of the recording; null while it records (open-ended). */\n stoppedAt: string | null\n}\n\n/** The recordings of a call as windows. */\nexport function recordingWindowsOf(\n recordings: readonly {\n started_at: string\n stopped_at?: string | null\n tracks?: readonly { external_id: string }[] | null\n }[]\n): RecordingWindow[] {\n return recordings.map((r) => ({\n trackExtIds: (r.tracks ?? []).map((t) => t.external_id),\n startedAt: r.started_at,\n stoppedAt: r.stopped_at ?? null,\n }))\n}\n\n/** What the supersede rule needs of a transcript line; null for a message that is not one. */\nexport interface TranscriptLineRef {\n segmentId: string | null | undefined\n /** ISO time the line was spoken (or stored). */\n startedAt: string | null | undefined\n}\n\n/** A transcript chat message (E2E meeting chat): its `dedupe_key` is the segment id. */\nexport function chatTranscriptRef(message: {\n kind?: string | null\n content?: string | null\n created_at: string\n dedupe_key?: string | null\n decryptFailed?: boolean\n}): TranscriptLineRef | null {\n if (!chats.isTranscriptMessage(message)) return null\n const parsed = message.decryptFailed ? null : chats.parseTranscriptContent(message.content)\n return { segmentId: message.dedupe_key, startedAt: parsed?.started_at ?? message.created_at }\n}\n\n/** A workspace meeting thread message with a `transcript_segment` marker. */\nexport function threadTranscriptRef(message: {\n created_at?: string | null\n tools?: unknown\n meta?: { tools?: unknown } | null\n}): TranscriptLineRef | null {\n const marker = transcriptSegmentOf(message.tools ?? message.meta?.tools)\n if (!marker) return null\n return { segmentId: marker.segment_id, startedAt: marker.started_at ?? message.created_at }\n}\n\n/**\n * The LIVE transcript lines a recording's transcript supersedes. Transcribing a recording stores its\n * lines (`rec-…` segment ids) beside the live ones of the same speech, so both would show. For each\n * recording that has transcript lines, the live lines spoken inside its [started_at, stopped_at]\n * window are superseded; without a recording transcript, the live lines stand.\n *\n * A `rec-` line of a recording not in `recordings` (the list did not load) still supersedes: its\n * window is then the span of those lines' own times.\n *\n * `refOf` returns null for a message that is not a transcript line (never superseded).\n */\nexport function supersededTranscriptLines<T>(\n lines: readonly T[],\n refOf: (line: T) => TranscriptLineRef | null,\n recordings: readonly RecordingWindow[] = []\n): Set<T> {\n const superseded = new Set<T>()\n const recLines: { segmentId: string; at: number }[] = []\n for (const line of lines) {\n const ref = refOf(line)\n if (ref && isRecordingSegmentId(ref.segmentId)) {\n recLines.push({ segmentId: ref.segmentId as string, at: Date.parse(ref.startedAt ?? '') })\n }\n }\n if (!recLines.length) return superseded\n\n const windows: [number, number][] = []\n const claimed = new Set<string>()\n for (const r of recordings) {\n const prefixes = r.trackExtIds.map((t) => `rec-${t}-`)\n const mine = recLines.filter((l) => prefixes.some((p) => l.segmentId.startsWith(p)))\n if (!mine.length) continue\n for (const l of mine) claimed.add(l.segmentId)\n const start = Date.parse(r.startedAt)\n const stop = r.stoppedAt ? Date.parse(r.stoppedAt) : Number.POSITIVE_INFINITY\n if (!Number.isNaN(start)) windows.push([start, Number.isNaN(stop) ? Infinity : stop])\n }\n const unclaimed = recLines.filter((l) => !claimed.has(l.segmentId) && !Number.isNaN(l.at))\n if (unclaimed.length) {\n const times = unclaimed.map((l) => l.at)\n windows.push([Math.min(...times), Math.max(...times)])\n }\n if (!windows.length) return superseded\n\n for (const line of lines) {\n const ref = refOf(line)\n if (!ref || isRecordingSegmentId(ref.segmentId)) continue\n const at = Date.parse(ref.startedAt ?? '')\n if (Number.isNaN(at)) continue\n if (windows.some(([start, stop]) => at >= start && at <= stop)) superseded.add(line)\n }\n return superseded\n}\n\n/** `lines` without the live lines a recording's transcript supersedes (see above). */\nexport function supersedeLiveTranscript<T>(\n lines: readonly T[],\n refOf: (line: T) => TranscriptLineRef | null,\n recordings: readonly RecordingWindow[] = []\n): T[] {\n const superseded = supersededTranscriptLines(lines, refOf, recordings)\n return superseded.size ? lines.filter((l) => !superseded.has(l)) : [...lines]\n}\n\n/** Whether any of these messages is a line transcribed from a recording. */\nexport function hasRecordingTranscript<T>(\n lines: readonly T[],\n refOf: (line: T) => TranscriptLineRef | null\n): boolean {\n return lines.some((l) => isRecordingSegmentId(refOf(l)?.segmentId))\n}\n","/**\n * In-call chat (ARBI `docs/meeting-chats-plan.md` §4).\n *\n * Live: each message goes over the LiveKit data channel (reliable, topic {@link CALL_CHAT_TOPIC})\n * as `base64(nonce ‖ secretbox(json, K))` — K is the call's frame key, so the SFU, which never\n * holds K, only forwards ciphertext (media E2EE does not cover data packets).\n *\n * Persisted: a signed-in member's message is also posted to the call's meeting chat\n * ({@link ensureMeetingChat}, made lazily on the first one): an E2E chat message under the chat\n * key, or a comment in the workspace meeting thread (encrypted by the server with the workspace\n * key). A workspace guest on a thread call can read the meeting chat but not post to it: the panel\n * is read-only for them.\n */\n\nimport { decryptWithChatKey, encryptWithChatKey } from '@arbidocs/client'\nimport type { QueryClient } from '@tanstack/react-query'\nimport { ArbiApiError, type Arbi } from '@arbidocs/sdk/browser'\nimport { cachedChat, type ChatResponse } from '../directmessages/chat-cache'\nimport { getUserDisplayName } from '../users/user-display'\nimport { ensureMeetingChat, meetingChatRefFrom, type MeetingChatRef } from './meeting-chat'\nimport { transcriptSegmentOf } from './transcript-format'\n\nexport const CALL_CHAT_TOPIC = 'arbi.chat'\n/** What a workspace guest sees instead of a composer in a thread call's meeting chat. */\nexport const READ_ONLY_MEETING_TEXT = 'Read only: you can’t post in this meeting chat'\n/** Longest message accepted over the data channel (well under LiveKit's reliable-packet limit). */\nexport const CALL_CHAT_MAX_CHARS = 4000\n\n/** What one data packet carries, encrypted under K. */\nexport interface CallChatPacket {\n v: 1\n /** Random id, so a sender's echo and a re-delivery deduplicate. */\n id: string\n text: string\n /** Sender clock, ms since epoch. */\n at: number\n}\n\n/** One line of the in-call chat panel. */\nexport interface CallChatLine {\n id: string\n senderIdentity: string\n senderName: string\n text: string\n at: number\n mine: boolean\n /**\n * `live`: data channel only (received from someone else, or not kept); `saving` / `saved` /\n * `unsaved`: my own message and its persistence; `history`: loaded from the meeting chat.\n */\n status: 'live' | 'saving' | 'saved' | 'unsaved' | 'history'\n}\n\nconst encoder = new TextEncoder()\nconst decoder = new TextDecoder()\n\nexport function newPacketId(): string {\n const bytes = new Uint8Array(8)\n globalThis.crypto.getRandomValues(bytes)\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\n/** Encrypt a chat packet under K for `publishData`. */\nexport async function encodeCallChatPacket(\n packet: CallChatPacket,\n callKey: Uint8Array\n): Promise<Uint8Array<ArrayBuffer>> {\n return encoder.encode(await encryptWithChatKey(JSON.stringify(packet), callKey))\n}\n\n/** Open a received packet; null when it is not ours, not under K, or malformed. */\nexport async function decodeCallChatPacket(\n payload: Uint8Array,\n callKey: Uint8Array\n): Promise<CallChatPacket | null> {\n try {\n const parsed: unknown = JSON.parse(await decryptWithChatKey(decoder.decode(payload), callKey))\n if (!parsed || typeof parsed !== 'object') return null\n const p = parsed as Partial<CallChatPacket>\n if (p.v !== 1 || typeof p.id !== 'string' || typeof p.text !== 'string') return null\n const text = p.text.slice(0, CALL_CHAT_MAX_CHARS)\n if (!text.trim()) return null\n return { v: 1, id: p.id, text, at: typeof p.at === 'number' ? p.at : Date.now() }\n } catch {\n return null\n }\n}\n\n/** One of my chats: from the cache when the list has it, else fetched. */\nexport async function chatByExtId(\n arbi: Arbi,\n queryClient: QueryClient,\n chatExtId: string\n): Promise<ChatResponse> {\n return cachedChat(queryClient, chatExtId) ?? arbi.chats.get(chatExtId)\n}\n\n/**\n * Post my message to the meeting chat, making the chat first if the call has none. Throws when it\n * cannot be kept (not a host of the call, the thread's workspace not open, …).\n */\nexport async function persistMeetingMessage(\n arbi: Arbi,\n queryClient: QueryClient,\n callExtId: string,\n callKey: Uint8Array,\n text: string\n): Promise<MeetingChatRef> {\n const ref = await ensureMeetingChat(arbi, queryClient, callExtId, callKey)\n if (ref.kind === 'chat') {\n await arbi.chats.send(await chatByExtId(arbi, queryClient, ref.chatExtId), text)\n } else {\n try {\n await arbi.conversations.postMessage({\n content: text,\n conversation_ext_id: ref.conversationExtId,\n assistant_mode: 'mentioned',\n })\n } catch (err) {\n const status = err instanceof ArbiApiError ? err.status : undefined\n const detail = err instanceof ArbiApiError ? err.detail : undefined\n throw new Error(\n `Could not save the message to the meeting thread (${status ?? '?'})${detail ? `: ${detail}` : ''}`\n )\n }\n }\n return ref\n}\n\n/** A persisted message, before it is matched against live lines. */\nexport interface HistoryMessage {\n id: string\n senderExtId: string | null\n /** Known name, or null to resolve from the room. */\n senderName: string | null\n text: string\n at: number\n}\n\nexport const HISTORY_LIMIT = 50\n\n/**\n * The meeting chat's recent messages (oldest first), decrypted, or null when the call has no\n * meeting chat yet. Never creates one.\n */\nexport async function loadMeetingHistory(\n arbi: Arbi,\n queryClient: QueryClient,\n callExtId: string\n): Promise<{ ref: MeetingChatRef; messages: HistoryMessage[] } | null> {\n const ref = meetingChatRefFrom(await arbi.calls.get(callExtId))\n if (!ref) return null\n if (ref.kind === 'chat') {\n const chat = await chatByExtId(arbi, queryClient, ref.chatExtId)\n const page = await arbi.chats.page(chat)\n const names = new Map(chat.members.map((m) => [m.user.external_id, getUserDisplayName(m.user)]))\n return {\n ref,\n messages: page.messages\n .filter((m) => m.kind === 'text' && !m.decryptFailed && m.content)\n .slice(-HISTORY_LIMIT)\n .map((m) => ({\n id: m.external_id,\n senderExtId: m.sender_ext_id ?? null,\n senderName: (m.sender_ext_id && names.get(m.sender_ext_id)) || null,\n text: m.content ?? '',\n at: Date.parse(m.created_at),\n })),\n }\n }\n // A thread with no messages yet answers 404: nothing to show.\n const data = await arbi.conversations.getThreads(ref.conversationExtId).catch(() => null)\n if (!data) return { ref, messages: [] }\n const seen = new Map<string, HistoryMessage>()\n for (const thread of data.threads) {\n for (const m of thread.history) {\n if (seen.has(m.external_id) || m.role === 'system' || !m.content) continue\n // Transcript lines are captions, not chat.\n if (transcriptSegmentOf(m.tools)) continue\n seen.set(m.external_id, {\n id: m.external_id,\n senderExtId: m.role === 'assistant' ? null : m.created_by_ext_id,\n senderName: m.role === 'assistant' ? 'ARBI' : null,\n text: m.content,\n at: Date.parse(m.created_at),\n })\n }\n }\n return {\n ref,\n messages: [...seen.values()].sort((a, b) => a.at - b.at).slice(-HISTORY_LIMIT),\n }\n}\n\n/** Live and persisted copies of one message within this window are the same message. */\nconst SAME_MESSAGE_MS = 5 * 60 * 1000\n\n/**\n * The panel's lines: history first, then live lines, minus the live lines that a member already\n * persisted (same sender, same text, close in time), ordered by time.\n */\nexport function mergeChatLines(\n history: HistoryMessage[],\n live: CallChatLine[],\n context: { myIdentity: string; nameOf(identity: string): string | null }\n): CallChatLine[] {\n const historyLines: CallChatLine[] = history.map((m) => ({\n id: `h:${m.id}`,\n senderIdentity: m.senderExtId ?? 'arbi',\n senderName: m.senderName ?? (m.senderExtId ? context.nameOf(m.senderExtId) : null) ?? 'Someone',\n text: m.text,\n at: m.at,\n mine: !!m.senderExtId && m.senderExtId === context.myIdentity,\n status: 'history',\n }))\n const used = new Set<number>()\n const liveOnly = live.filter((line) => {\n const match = history.findIndex(\n (m, i) =>\n !used.has(i) &&\n m.senderExtId === line.senderIdentity &&\n m.text === line.text &&\n Math.abs(m.at - line.at) < SAME_MESSAGE_MS\n )\n if (match < 0) return true\n used.add(match)\n return false\n })\n return [...historyLines, ...liveOnly].sort((a, b) => a.at - b.at)\n}\n","/**\n * Call recording in the browser (ARBI `docs/meeting-chats-plan.md` §6).\n *\n * Calls are end-to-end encrypted, so the server never records them: every participant's browser\n * records its OWN microphone (attribution by origin), and in \"Audio + video\" mode the host's\n * browser also records one composite of its view. The server only ever stores ciphertext.\n *\n * Keys: each track (one per speaker, plus the composite) has a random file key, wrapped under the\n * conversation key — the meeting chat's epoch key for a contacts call, the workspace key for a\n * thread call — and stored with the track (`wrapped_key`, `key_version`). Each ~30 s part is\n * SecretBox-encrypted with the file key here and uploaded like a document: presign → PUT the\n * ciphertext straight to the object store → commit (HEAD-verified server-side).\n *\n * Offsets are relative to the recording's `started_at`, so the tracks align in time.\n */\n\nimport {\n base64ToBytes,\n bytesToBase64,\n decryptFile,\n encryptFile,\n generateChatKey,\n initSodium,\n} from '@arbidocs/client'\nimport type { QueryClient } from '@tanstack/react-query'\nimport { CallApiError, calls, documents, type Arbi } from '@arbidocs/sdk/browser'\nimport { chatByExtId } from './in-call-chat'\nimport { ensureMeetingChat, meetingChatRefFrom, type MeetingChatRef } from './meeting-chat'\n\n/** Server-sent, plaintext: `{on, mode, recording_ext_id, by_identity, by_name}`. */\nexport const CALL_RECORDING_TOPIC = 'arbi.recording'\n/** Part length: every part is a standalone file of about this long. */\nexport const RECORDING_CHUNK_MS = 30_000\n/** Speaker audio: Opus ~16–24 kbps mono (~7–11 MB per speaker-hour). */\nexport const SPEAKER_AUDIO_BITS_PER_SECOND = 24_000\n/** Composite video (≤720p): ~300–600 kbps. */\nexport const COMPOSITE_VIDEO_BITS_PER_SECOND = 500_000\nexport const COMPOSITE_AUDIO_BITS_PER_SECOND = 32_000\n\n// ── Time ────────────────────────────────────────────────────────────────────\n\n/** A part's offset from the recording start, ms (never negative). */\nexport function partOffsetMs(partStartedAt: number, recordingStartedAt: string | number): number {\n const start =\n typeof recordingStartedAt === 'number' ? recordingStartedAt : Date.parse(recordingStartedAt)\n if (!Number.isFinite(start)) return 0\n return Math.max(0, Math.round(partStartedAt - start))\n}\n\n// ── Keys ────────────────────────────────────────────────────────────────────\n\n/** A fresh random 32-byte file key. */\nexport function newTrackKey(): Promise<Uint8Array> {\n return generateChatKey()\n}\n\n/** The file key under the conversation key: base64(nonce ‖ secretbox). */\nexport async function wrapTrackKey(\n fileKey: Uint8Array,\n conversationKey: Uint8Array\n): Promise<string> {\n await initSodium()\n return bytesToBase64(encryptFile(fileKey, conversationKey))\n}\n\nexport async function unwrapTrackKey(\n wrapped: string,\n conversationKey: Uint8Array\n): Promise<Uint8Array> {\n await initSodium()\n return decryptFile(base64ToBytes(wrapped), conversationKey)\n}\n\nexport async function encryptPart(bytes: Uint8Array, fileKey: Uint8Array): Promise<Uint8Array> {\n await initSodium()\n return encryptFile(bytes, fileKey)\n}\n\nexport async function decryptPart(cipher: Uint8Array, fileKey: Uint8Array): Promise<Uint8Array> {\n await initSodium()\n return decryptFile(cipher, fileKey)\n}\n\n/** The key a recording's tracks are wrapped under, and its version (null: the workspace key). */\nexport interface ConversationKey {\n key: Uint8Array\n keyVersion: number | null\n}\n\n/**\n * The conversation key of a call's recordings:\n * - a thread call: its locked workspace's key (the key direct uploads use, unwrapped here);\n * - a contacts call: the meeting chat's epoch key — the current one to record (`keyVersion`\n * omitted; the chat is made on first use, which needs the call key K), or the given version\n * to read a track.\n */\nexport async function conversationKeyForCall(\n arbi: Arbi,\n queryClient: QueryClient,\n call: Pick<\n calls.CallResponse,\n 'external_id' | 'chat_ext_id' | 'workspace_locked' | 'locked_workspace_ext_id'\n >,\n options: { callKey?: Uint8Array | null; keyVersion?: number | null } = {}\n): Promise<ConversationKey> {\n if (call.workspace_locked && call.locked_workspace_ext_id) {\n const key = await arbi.workspaces.key(call.locked_workspace_ext_id)\n return { key, keyVersion: null }\n }\n let chatExtId = call.chat_ext_id ?? null\n if (!chatExtId) {\n if (!options.callKey) throw new Error('This call has no meeting chat to keep a recording in')\n const ref = await ensureMeetingChat(arbi, queryClient, call.external_id, options.callKey)\n if (ref.kind !== 'chat') throw new Error('Expected an end-to-end meeting chat')\n chatExtId = ref.chatExtId\n }\n const chat = await arbi.chats.ready(await chatByExtId(arbi, queryClient, chatExtId))\n const ring = await arbi.chats.ring(chat)\n const version = options.keyVersion ?? ring.currentVersion\n const key = ring.keys.get(version)\n if (!key) throw new Error('No key for this recording in the meeting chat')\n return { key, keyVersion: version }\n}\n\n/** The meeting chat a call's recording transcript goes to (made on first use when K is known). */\nexport async function meetingRefForCall(\n arbi: Arbi,\n queryClient: QueryClient,\n call: calls.CallResponse,\n callKey?: Uint8Array | null\n): Promise<MeetingChatRef> {\n const ref = meetingChatRefFrom(call)\n if (ref) return ref\n if (!callKey) throw new Error('This call has no meeting chat yet')\n return ensureMeetingChat(arbi, queryClient, call.external_id, callKey)\n}\n\n// ── Upload ──────────────────────────────────────────────────────────────────\n\n/** One recorded part, as the recorder hands it over. */\nexport interface PendingPart {\n blob: Blob\n /** Wall clock, ms since epoch. */\n startedAt: number\n durationMs: number\n}\n\ntype RecordingApi = Arbi['calls']['recording']\n\nexport interface UploaderDeps {\n createTrack: RecordingApi['createTrack']\n presign: RecordingApi['presignPart']\n commit: RecordingApi['commitPart']\n getRecording: RecordingApi['get']\n put(url: string, body: Uint8Array, headers?: Record<string, string>): Promise<void>\n encrypt(bytes: Uint8Array, key: Uint8Array): Promise<Uint8Array>\n wrap(fileKey: Uint8Array, conversationKey: Uint8Array): Promise<string>\n unwrap(wrapped: string, conversationKey: Uint8Array): Promise<Uint8Array>\n newKey(): Promise<Uint8Array>\n sleep(ms: number): Promise<void>\n}\n\nconst defaultDeps = (arbi: Arbi): UploaderDeps => ({\n createTrack: (...args) => arbi.calls.recording.createTrack(...args),\n presign: (...args) => arbi.calls.recording.presignPart(...args),\n commit: (...args) => arbi.calls.recording.commitPart(...args),\n getRecording: (...args) => arbi.calls.recording.get(...args),\n put: (url, body, headers) => documents.presignedPut(url, body, undefined, headers),\n encrypt: encryptPart,\n wrap: wrapTrackKey,\n unwrap: unwrapTrackKey,\n newKey: newTrackKey,\n sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n})\n\n/** Attempts per part (and for the track) before a transient failure is given up. */\nexport const UPLOAD_ATTEMPTS = 5\nexport const UPLOAD_BACKOFF_MS = 1000\n\n/** A part number already in use: retried under the next number. */\nclass SeqTakenError extends Error {}\n\n/**\n * Network errors, 408/429, 5xx (but 507, the quota) and a commit that found no object yet\n * (409 `part_not_uploaded`: presign and PUT again — re-presigning is allowed until committed).\n */\nexport function isRetryable(err: unknown): boolean {\n if (!(err instanceof CallApiError)) return true\n if (calls.isQuotaError(err)) return false\n if (err.code === 'part_not_uploaded') return true\n const s = err.status ?? 0\n return s === 0 || s === 408 || s === 429 || (s >= 500 && s !== 507)\n}\n\n/** The recording takes no more parts: over (past its grace), gone, or not ours to add to. */\nexport function isRecordingOver(err: unknown): boolean {\n if (!(err instanceof CallApiError)) return false\n return (\n err.code === 'recording_stopped' ||\n err.code === 'composite_needs_video' ||\n err.status === 404 ||\n err.status === 410\n )\n}\n\nexport interface TrackUploaderOptions {\n /** Who uploads: the parts go through `arbi.calls.recording`. */\n arbi: Arbi\n callExtId: string\n recordingExtId: string\n kind: calls.RecordingTrackKind\n mimeType: string\n /** The conversation key to wrap the file key under (resolved once, lazily). */\n conversationKey(): Promise<ConversationKey>\n /** The recording's start (`CallRecordingState.started_at`; otherwise read from the recording). */\n recordingStartedAt?: string | null\n /** The call link version I joined with (admission, as for transcription). */\n linkVersion?: number | null\n /** The recording can take no more parts (quota): the server stopped it. */\n onStopped?(err: CallApiError): void\n /** A part could not be kept. */\n onPartLost?(err: unknown): void\n deps?: Partial<UploaderDeps>\n}\n\n/**\n * Uploads one track's parts in order: encrypt → presign → PUT → commit, retried with backoff.\n *\n * The track is created on the first part. Creation is idempotent per speaker (and for the\n * composite), so after a reconnect — or a reload — the server hands back the SAME track: its\n * existing wrapped key is unwrapped and used, and numbering resumes after its last part.\n */\nexport class TrackUploader {\n private readonly deps: UploaderDeps\n private track: { externalId: string; fileKey: Uint8Array; startedAt: string } | null = null\n private trackPromise: Promise<NonNullable<TrackUploader['track']>> | null = null\n private nextSeq = 0\n private queue: PendingPart[] = []\n private running: Promise<void> | null = null\n /** No more parts are accepted (quota, or the recording is gone). */\n stopped = false\n /** Parts committed (for tests and the UI). */\n committed = 0\n\n constructor(private readonly options: TrackUploaderOptions) {\n this.deps = { ...defaultDeps(options.arbi), ...options.deps }\n }\n\n /** Queue a part; parts upload one at a time, in order. */\n enqueue(part: PendingPart): void {\n if (this.stopped) return\n this.queue.push(part)\n this.drain()\n }\n\n /** Resolves once every queued part was uploaded (or given up). */\n async flush(): Promise<void> {\n while (this.running) await this.running\n }\n\n private drain(): void {\n if (this.running) return\n this.running = (async () => {\n try {\n while (this.queue.length && !this.stopped) {\n const part = this.queue.shift()!\n await this.uploadPart(part)\n }\n } finally {\n this.running = null\n }\n })()\n }\n\n private async withRetry<T>(label: string, run: () => Promise<T>): Promise<T> {\n let lastError: unknown\n for (let attempt = 0; attempt < UPLOAD_ATTEMPTS; attempt++) {\n try {\n return await run()\n } catch (err) {\n lastError = err\n if (!isRetryable(err) || attempt === UPLOAD_ATTEMPTS - 1) break\n console.warn(`[calls] recording ${label} failed, retrying`, err)\n await this.deps.sleep(UPLOAD_BACKOFF_MS * 2 ** attempt)\n }\n }\n throw lastError\n }\n\n private ensureTrack(): Promise<NonNullable<TrackUploader['track']>> {\n if (this.track) return Promise.resolve(this.track)\n if (!this.trackPromise) {\n this.trackPromise = this.withRetry('track', () => this.createTrack()).then(\n (track) => {\n this.track = track\n return track\n },\n (err: unknown) => {\n this.trackPromise = null\n throw err\n }\n )\n }\n return this.trackPromise\n }\n\n private async createTrack(): Promise<NonNullable<TrackUploader['track']>> {\n const { callExtId, recordingExtId, kind, mimeType } = this.options\n const conv = await this.options.conversationKey()\n const fileKey = await this.deps.newKey()\n const wrapped = await this.deps.wrap(fileKey, conv.key)\n const created: calls.RecordingTrack = await this.deps.createTrack(callExtId, recordingExtId, {\n kind,\n wrapped_key: wrapped,\n key_version: conv.keyVersion,\n mime_type: mimeType,\n ...(this.options.linkVersion ? { link_version: this.options.linkVersion } : {}),\n })\n // An existing track (reconnect): its key is the one its parts are under.\n const key =\n created.wrapped_key && created.wrapped_key !== wrapped\n ? await this.deps.unwrap(created.wrapped_key, conv.key)\n : fileKey\n let startedAt = this.options.recordingStartedAt ?? null\n let parts = created.parts\n if (!startedAt || !parts) {\n const recording: calls.CallRecording = await this.deps.getRecording(callExtId, recordingExtId)\n startedAt = startedAt ?? recording.started_at\n parts =\n parts ?? (recording.tracks ?? []).find((t) => t.external_id === created.external_id)?.parts\n }\n this.nextSeq = Math.max(this.nextSeq, nextSeqAfter(parts ?? []))\n return { externalId: created.external_id, fileKey: key, startedAt }\n }\n\n private async uploadPart(part: PendingPart): Promise<void> {\n const { callExtId, recordingExtId } = this.options\n try {\n const track = await this.ensureTrack()\n const bytes = new Uint8Array(await part.blob.arrayBuffer())\n const cipher = await this.deps.encrypt(bytes, track.fileKey)\n const offset = partOffsetMs(part.startedAt, track.startedAt)\n let seq = this.nextSeq++\n await this.withRetry('part', async () => {\n let upload\n try {\n upload = await this.deps.presign(callExtId, recordingExtId, track.externalId, {\n seq,\n offset_ms: offset,\n duration_ms: Math.max(0, Math.round(part.durationMs)),\n size_bytes: cipher.length,\n })\n } catch (err) {\n // That number is committed already (an earlier session's part): take the next one.\n if (err instanceof CallApiError && err.code === 'part_committed') {\n seq = this.nextSeq++\n throw new SeqTakenError(err.message)\n }\n throw err\n }\n await this.deps.put(upload.upload_url, cipher, upload.headers ?? undefined)\n await this.deps.commit(callExtId, recordingExtId, track.externalId, seq)\n })\n this.committed += 1\n } catch (err) {\n if (calls.isQuotaError(err)) {\n this.stopped = true\n this.queue = []\n this.options.onStopped?.(err as CallApiError)\n return\n }\n if (isRecordingOver(err)) {\n // Stopped past its grace, deleted, or a composite for an audio-only recording.\n this.stopped = true\n this.queue = []\n }\n console.warn('[calls] a recording part could not be kept', err)\n this.options.onPartLost?.(err)\n }\n }\n}\n\n/** The seq after a track's last part (0 for a new track). */\nexport function nextSeqAfter(parts: { seq: number }[]): number {\n return parts.reduce((max, p) => Math.max(max, p.seq + 1), 0)\n}\n\n/**\n * One uploader per (recording, track kind) for the whole page session, so a reconnect (the call\n * room remounting) resumes the same track and numbering instead of starting over.\n */\nconst uploaders = new Map<string, TrackUploader>()\n\nexport function trackUploaderFor(options: TrackUploaderOptions): TrackUploader {\n const key = `${options.recordingExtId}:${options.kind}`\n let uploader = uploaders.get(key)\n if (!uploader || uploader.stopped) {\n uploader = new TrackUploader(options)\n uploaders.set(key, uploader)\n }\n return uploader\n}\n\n/** Forget the uploaders (tests; sign-out). */\nexport function clearTrackUploaders(): void {\n uploaders.clear()\n}\n","/**\n * Playing and exporting a call recording in the browser (ARBI `docs/meeting-chats-plan.md` §6).\n * Everything here works on DECRYPTED part bytes; nothing leaves the browser.\n *\n * Speaker audio — mixed in sync with WebAudio: each speaker part is a standalone file placed at its\n * `offset_ms` on one AudioContext timeline. Parts are fetched, decrypted and decoded only a little\n * ahead of the playhead ({@link partsDue}) and dropped once played, so an hour-long recording never\n * sits decoded in memory.\n *\n * Composite video — the host's composite is recorded as ONE continuous WebM (MediaRecorder\n * timeslice: only the first part carries the header), so its decrypted parts concatenated in order\n * ARE the file: it plays in a plain <video> and downloads as one file. This was chosen over\n * MediaSource (codec strings and append juggling per browser) and over part-by-part playback\n * (a visible gap every 30 s) as the simplest robust option. A reconnect of the host starts a new\n * file mid-recording; {@link splitIntoFiles} cuts at each new header, so each run plays on its own.\n *\n * Download — each speaker track becomes one WAV (16 kHz mono) with its parts at their offsets, so\n * every speaker's file starts at the recording's start and they line up in any editor.\n */\n\nimport type { calls } from '@arbidocs/sdk/browser'\nimport {\n DEFAULT_SEGMENTER_CONFIG,\n INITIAL_SEGMENTER_STATE,\n flushSegmenter,\n levelFromRms,\n stepSegmenter,\n type SegmenterConfig,\n type SegmenterEvent,\n type SegmenterState,\n} from '../documents/audio-segmenter'\n\n// ── Timeline ────────────────────────────────────────────────────────────────\n\nexport interface TimelinePart {\n trackExtId: string\n seq: number\n offsetMs: number\n durationMs: number\n}\n\nexport const partKey = (p: Pick<TimelinePart, 'trackExtId' | 'seq'>) => `${p.trackExtId}:${p.seq}`\n\n/** Every part of the given tracks, in time order. */\nexport function timelineOf(\n tracks: Pick<calls.RecordingTrack, 'external_id' | 'parts'>[]\n): TimelinePart[] {\n return tracks\n .flatMap((t) =>\n (t.parts ?? []).map((p: calls.RecordingPart) => ({\n trackExtId: t.external_id,\n seq: p.seq,\n offsetMs: p.offset_ms,\n durationMs: p.duration_ms,\n }))\n )\n .sort((a, b) => a.offsetMs - b.offsetMs || a.seq - b.seq)\n}\n\n/** The recording's length: the end of its last part. */\nexport function timelineDurationMs(parts: TimelinePart[]): number {\n return parts.reduce((max, p) => Math.max(max, p.offsetMs + p.durationMs), 0)\n}\n\n/** How far ahead of the playhead parts are fetched and decoded. */\nexport const PLAYBACK_LOOKAHEAD_MS = 20_000\n\n/** Parts not yet scheduled that play within the window `[playhead, playhead + lookahead)`. */\nexport function partsDue(\n parts: TimelinePart[],\n playheadMs: number,\n lookaheadMs: number,\n scheduled: ReadonlySet<string>\n): TimelinePart[] {\n return parts.filter(\n (p) =>\n !scheduled.has(partKey(p)) &&\n p.offsetMs < playheadMs + lookaheadMs &&\n p.offsetMs + p.durationMs > playheadMs\n )\n}\n\n/**\n * When to start a decoded part on the AudioContext clock. Playback began at context time\n * `ctxStart` from recording position `seekMs`, so position `t` plays at\n * `ctxStart + (t − seekMs)/1000`. A part whose start already passed (a seek into it, or it decoded\n * late) starts now, `offset` seconds into its buffer. Null: nothing of it is left to play.\n */\nexport function schedulePart(params: {\n partOffsetMs: number\n bufferDurationS: number\n ctxStart: number\n seekMs: number\n ctxNow: number\n}): { when: number; offset: number } | null {\n const { partOffsetMs, bufferDurationS, ctxStart, seekMs, ctxNow } = params\n let when = ctxStart + (partOffsetMs - seekMs) / 1000\n let offset = 0\n if (when < ctxNow) {\n offset = ctxNow - when\n when = ctxNow\n }\n if (offset >= bufferDurationS) return null\n return { when, offset }\n}\n\n/** The recording position (ms) the AudioContext clock is at. */\nexport function playheadAt(ctxNow: number, ctxStart: number, seekMs: number): number {\n return seekMs + Math.max(0, ctxNow - ctxStart) * 1000\n}\n\n// ── Files ───────────────────────────────────────────────────────────────────\n\nconst EBML_MAGIC = [0x1a, 0x45, 0xdf, 0xa3]\nconst FTYP = [0x66, 0x74, 0x79, 0x70]\n\n/** Whether these bytes start a media file (a WebM/Matroska EBML header or an MP4 `ftyp` box). */\nexport function startsMediaFile(bytes: Uint8Array): boolean {\n if (EBML_MAGIC.every((b, i) => bytes[i] === b)) return true\n return FTYP.every((b, i) => bytes[4 + i] === b)\n}\n\n/** Consecutive parts grouped into files: a new file at each part that starts with a header. */\nexport function splitIntoFiles(parts: Uint8Array[]): Uint8Array[][] {\n const files: Uint8Array[][] = []\n for (const part of parts) {\n if (!files.length || startsMediaFile(part)) files.push([part])\n else files[files.length - 1].push(part)\n }\n return files\n}\n\n/** File extension for a recorded MIME type. */\nexport function extensionFor(mimeType: string): string {\n if (mimeType.includes('mp4')) return mimeType.startsWith('video') ? 'mp4' : 'm4a'\n if (mimeType.includes('ogg')) return 'ogg'\n return 'webm'\n}\n\n/** A safe file name piece. */\nexport function fileSafe(name: string): string {\n return (\n name\n .replace(/[^\\p{L}\\p{N} ._-]+/gu, '')\n .trim()\n .replace(/\\s+/g, '-')\n .slice(0, 60) || 'recording'\n )\n}\n\n// ── WAV ─────────────────────────────────────────────────────────────────────\n\nexport const EXPORT_SAMPLE_RATE = 16_000\n\n/** Copy decoded samples (−1…1) into a 16-bit timeline at `offsetSamples`, clipped. */\nexport function writeSamples(\n target: Int16Array,\n samples: Float32Array,\n offsetSamples: number\n): void {\n const start = Math.max(0, Math.round(offsetSamples))\n const n = Math.min(samples.length, target.length - start)\n for (let i = 0; i < n; i++) {\n const s = Math.max(-1, Math.min(1, samples[i]))\n target[start + i] = s < 0 ? s * 0x8000 : s * 0x7fff\n }\n}\n\n/** A mono 16-bit PCM WAV file. */\nexport function encodeWav(samples: Int16Array, sampleRate: number): Uint8Array<ArrayBuffer> {\n const dataBytes = samples.length * 2\n const out = new Uint8Array(44 + dataBytes)\n const view = new DataView(out.buffer)\n const ascii = (offset: number, text: string) => {\n for (let i = 0; i < text.length; i++) out[offset + i] = text.charCodeAt(i)\n }\n ascii(0, 'RIFF')\n view.setUint32(4, 36 + dataBytes, true)\n ascii(8, 'WAVE')\n ascii(12, 'fmt ')\n view.setUint32(16, 16, true)\n view.setUint16(20, 1, true) // PCM\n view.setUint16(22, 1, true) // mono\n view.setUint32(24, sampleRate, true)\n view.setUint32(28, sampleRate * 2, true)\n view.setUint16(32, 2, true)\n view.setUint16(34, 16, true)\n ascii(36, 'data')\n view.setUint32(40, dataBytes, true)\n for (let i = 0; i < samples.length; i++) view.setInt16(44 + i * 2, samples[i], true)\n return out\n}\n\n/** Bytes as a standalone ArrayBuffer-backed view (Blob/decodeAudioData want one). */\nexport function standalone(bytes: Uint8Array): Uint8Array<ArrayBuffer> {\n return new Uint8Array(bytes) as Uint8Array<ArrayBuffer>\n}\n\n/** Decoded mono samples as a 16-bit WAV. */\nexport function floatToWav(samples: Float32Array, sampleRate: number): Uint8Array<ArrayBuffer> {\n const pcm = new Int16Array(samples.length)\n writeSamples(pcm, samples, 0)\n return encodeWav(pcm, sampleRate)\n}\n\n// ── Cutting at pauses (transcription) ───────────────────────────────────────\n\n/** One piece of a part, in samples. */\nexport interface AudioPiece {\n start: number\n end: number\n}\n\n/** A piece of a whole track: absolute sample positions on the track's timeline, and its audio. */\nexport interface TrackPiece extends AudioPiece {\n samples: Float32Array\n /** Voiced time in the piece. */\n voiceMs: number\n}\n\n/** Meter frame for the segmenter (the live recorder's meter ticks every 50 ms). */\nexport const SEGMENT_FRAME_MS = 50\n/** Kept before a piece's first voiced frame, so a word's onset is not clipped. */\nexport const SEGMENT_PRE_ROLL_MS = 200\n\n/**\n * The segmenter for a RECORDED track (transcribed after the call). Nothing waits on it, so it cuts\n * only at a real turn: 1.5 s without voice (live uses 700 ms for latency). A speaker's own track is\n * silent for seconds while others talk, but a quiet syllable mid-sentence can read as a short\n * \"pause\" — the 3-person demo split \"…two hundred and fifty / thousand pounds…\" at 700 ms and the\n * cut half came back as \"$250,000\". A piece runs to 25 s before it is cut (a 25 s 16 kHz WAV is\n * ~0.8 MB, well under the 2 MB cap), and a piece needs only 300 ms of voice: a short closing line\n * (\"Perfect. Thank you both.\") is speech, not a click.\n */\nexport const RECORDING_SEGMENTER: Partial<SegmenterConfig> = {\n maxMs: 25_000,\n minVoiceMs: 300,\n silenceMs: 1_500,\n}\n/**\n * A recorded piece with too little voice is held, and joined to the next piece when that starts\n * within this gap (\"Perfect.\" … \"Thank you both.\"); otherwise it is dropped.\n */\nexport const SHORT_PIECE_MERGE_GAP_MS = 1000\n\nexport interface TrackCutter {\n /**\n * Feed the next decoded part, placed at `offsetMs` on the track's timeline (a gap before it is\n * silence; an overlap with what came before is skipped). Returns the pieces it completed.\n */\n push(offsetMs: number, samples: Float32Array): TrackPiece[]\n /** The end of the track: closes the open piece. Returns the pieces it completed. */\n finish(): TrackPiece[]\n}\n\n/**\n * Cut a whole track at its pauses, part after part, with the SAME voice-activity segmenter as live\n * transcription (P5): RMS per 50 ms frame → meter level → `stepSegmenter`. The parts are placed on\n * ONE continuous timeline by their offsets, so a sentence spoken across a part boundary stays one\n * piece. Only the open piece (≤ `maxMs`) is kept in memory, never the whole track.\n *\n * `mergeShort`: a piece with less voice than `minVoiceMs` is held and joined to the next one if it\n * starts within {@link SHORT_PIECE_MERGE_GAP_MS}, instead of being dropped at once.\n */\nexport function createTrackCutter(\n sampleRate: number,\n config: Partial<SegmenterConfig> = {},\n options: { mergeShort?: boolean } = {}\n): TrackCutter {\n const cfg = { ...DEFAULT_SEGMENTER_CONFIG, ...config }\n const frame = Math.max(1, Math.round((sampleRate * SEGMENT_FRAME_MS) / 1000))\n const preRoll = Math.round((SEGMENT_PRE_ROLL_MS / 1000) * sampleRate)\n const toSample = (ms: number) => Math.round((ms / 1000) * sampleRate)\n const toMs = (sample: number) => (sample / sampleRate) * 1000\n\n // Retained audio: `buf[0..len)` holds the absolute samples `[bufStart, bufStart + len)`.\n let buf = new Float32Array(sampleRate * 4)\n let bufStart = 0\n let len = 0\n /** Absolute index of the first sample of the timeline (null before the first part). */\n let origin: number | null = null\n /** Absolute index just past the last sample fed. */\n let cursor = 0\n /** Absolute index of the next meter frame. */\n let nextFrame = 0\n let state: SegmenterState = INITIAL_SEGMENTER_STATE\n /** A short piece waiting for the next one (mergeShort). */\n let pending: { start: number; end: number; voiceMs: number; at: number } | null = null\n let out: TrackPiece[] = []\n\n const append = (samples: Float32Array) => {\n if (len + samples.length > buf.length) {\n const grown = new Float32Array(Math.max(buf.length * 2, len + samples.length))\n grown.set(buf.subarray(0, len))\n buf = grown\n }\n buf.set(samples, len)\n len += samples.length\n cursor += samples.length\n }\n\n const emit = (piece: { start: number; end: number; voiceMs: number }) => {\n const from = Math.max(piece.start, bufStart)\n if (piece.end <= from) return\n out.push({\n start: from,\n end: piece.end,\n voiceMs: piece.voiceMs,\n samples: buf.slice(from - bufStart, piece.end - bufStart),\n })\n }\n\n const onEvents = (events: SegmenterEvent[]) => {\n for (const e of events) {\n if (e.type === 'start') {\n // Too long a pause after a held short piece: it stands alone, too short to keep.\n if (pending && e.at - pending.at > SHORT_PIECE_MERGE_GAP_MS) pending = null\n continue\n }\n let piece = {\n start: Math.max(origin ?? 0, toSample(e.startedAt) - preRoll),\n end: Math.min(cursor, toSample(e.at)),\n voiceMs: e.voiceMs,\n at: e.at,\n }\n if (pending) {\n if (piece.end - pending.start <= toSample(cfg.maxMs) + preRoll) {\n piece = { ...piece, start: pending.start, voiceMs: piece.voiceMs + pending.voiceMs }\n }\n pending = null\n }\n if (piece.voiceMs >= cfg.minVoiceMs) emit(piece)\n else if (options.mergeShort && piece.end > piece.start) pending = piece\n }\n }\n\n const step = (from: number, n: number) => {\n let sum = 0\n const base = from - bufStart\n for (let j = 0; j < n; j++) sum += buf[base + j] * buf[base + j]\n const result = stepSegmenter(state, levelFromRms(Math.sqrt(sum / n)), toMs(from), cfg)\n state = result.state\n onEvents(result.events)\n }\n\n /** Run the meter over every whole frame fed so far, then drop audio no piece can reach. */\n const run = () => {\n while (nextFrame + frame <= cursor) {\n step(nextFrame, frame)\n nextFrame += frame\n }\n let keepFrom = cursor - preRoll\n if (state.phase === 'voice') keepFrom = Math.min(keepFrom, toSample(state.startedAt) - preRoll)\n if (pending) keepFrom = Math.min(keepFrom, pending.start)\n keepFrom = Math.min(Math.max(keepFrom, bufStart), nextFrame)\n if (keepFrom > bufStart) {\n buf.copyWithin(0, keepFrom - bufStart, len)\n len -= keepFrom - bufStart\n bufStart = keepFrom\n }\n }\n\n const take = () => {\n const done = out\n out = []\n return done\n }\n\n return {\n push(offsetMs, samples) {\n const at = toSample(Math.max(0, offsetMs))\n if (origin === null) {\n origin = at\n bufStart = cursor = nextFrame = at\n }\n if (at > cursor) {\n // A gap between parts (a reconnect): silence, a second at a time.\n const second = new Float32Array(sampleRate)\n while (cursor < at) {\n append(second.subarray(0, Math.min(sampleRate, at - cursor)))\n run()\n }\n }\n // Overlapping the previous part: skip what is already on the timeline.\n const fresh = at < cursor ? samples.subarray(Math.min(samples.length, cursor - at)) : samples\n if (fresh.length) {\n append(fresh)\n run()\n }\n return take()\n },\n finish() {\n if (origin !== null) {\n if (nextFrame < cursor) {\n step(nextFrame, cursor - nextFrame)\n nextFrame = cursor\n }\n onEvents(flushSegmenter(state, toMs(cursor), cfg).events)\n state = INITIAL_SEGMENTER_STATE\n }\n pending = null\n return take()\n },\n }\n}\n\n/**\n * Cut decoded speech at its pauses with the SAME voice-activity segmenter as live transcription\n * (P5): RMS per 50 ms frame → meter level → `stepSegmenter` (a piece ends after ~700 ms of\n * silence, is cut at `maxMs`, and is dropped with too little voice). A whole 30 s part sent at\n * once made the transcriber drop repeated sentences; short pieces do not.\n */\nexport function cutAtPauses(\n samples: Float32Array,\n sampleRate: number,\n config: Partial<SegmenterConfig> = {}\n): AudioPiece[] {\n const cutter = createTrackCutter(sampleRate, config)\n return [...cutter.push(0, samples), ...cutter.finish()].map(({ start, end }) => ({ start, end }))\n}\n","/**\n * Live call transcription (ARBI `docs/meeting-chats-plan.md` §5): captions over the data channel\n * and transcript persistence into the call's meeting chat.\n *\n * Attribution is by origin: a participant's browser transcribes only its own microphone, so the\n * speaker is whoever sent the audio — there is no diarization.\n *\n * Captions: the speaker's client broadcasts `{segment_id, text, identity, name, t}` on topic\n * {@link CALL_CAPTION_TOPIC}, sealed under the call key K exactly like an in-call chat packet, so\n * the SFU only forwards ciphertext. The transcription state itself is announced by the SERVER on\n * {@link CALL_TRANSCRIPTION_TOPIC} (plaintext; it carries no content).\n *\n * Persistence: the speaker stores its own segments. A speaker who cannot post to the meeting chat (a\n * workspace guest on a thread call: read-only) marks its captions `proxy`, and every member that can\n * post stores them instead — idempotent by the segment id (`dedupe_key` in a chat, the `transcript`\n * marker in a thread), so each is stored once whichever member wins.\n */\n\nimport { decryptWithChatKey, encryptWithChatKey } from '@arbidocs/client'\nimport type { QueryClient } from '@tanstack/react-query'\nimport type { Arbi, calls, chats } from '@arbidocs/sdk/browser'\nimport { chatByExtId } from './in-call-chat'\nimport { ensureMeetingChat, type MeetingChatRef } from './meeting-chat'\n\nexport const CALL_CAPTION_TOPIC = 'arbi.caption'\n/** Server-sent: `{on, by_identity, by_name}`. */\nexport const CALL_TRANSCRIPTION_TOPIC = 'arbi.transcription'\nexport const CAPTION_MAX_CHARS = 2000\n/**\n * Voice-activity tuning for live call speech: the shared 700 ms pause rule, but a segment needs only\n * 400 ms of voice, and a segment still too short waits 1 s more for the rest of a short phrase\n * (\"Perfect.\" … \"Thank you both.\") so a closing line is not dropped as two blips.\n */\nexport const LIVE_SEGMENTER = { minVoiceMs: 400, holdShortMs: 1_000 } as const\n\n/** One caption over the data channel, encrypted under K. */\nexport interface CaptionPacket {\n v: 1\n segment_id: string\n text: string\n /** The speaker's LiveKit identity (checked against the packet's sender on receipt). */\n identity: string\n name: string\n /** When the segment started, ms since epoch. */\n t: number\n /** The speaker cannot post to the meeting chat: members store this segment for them. */\n proxy?: boolean\n}\n\n/** A caption on screen. */\nexport interface CaptionLine {\n segmentId: string\n identity: string\n name: string\n text: string\n /** Segment start, ms since epoch. */\n t: number\n mine: boolean\n /** Local receipt time (drives the fade). */\n receivedAt: number\n}\n\n/** A transcript segment to store. */\nexport interface TranscriptSegment {\n segmentId: string\n text: string\n speakerIdentity: string\n speakerName: string\n /** ISO start of the segment. */\n startedAt: string | null\n}\n\nconst encoder = new TextEncoder()\nconst decoder = new TextDecoder()\nexport const SEGMENT_ID_PATTERN = /^[A-Za-z0-9_-]{8,64}$/\n\n/** A fresh segment id (`^[A-Za-z0-9_-]{8,64}$`). */\nexport function newSegmentId(): string {\n const bytes = new Uint8Array(12)\n globalThis.crypto.getRandomValues(bytes)\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\n// ── Captions ────────────────────────────────────────────────────────────────\n\nexport async function encodeCaptionPacket(\n packet: CaptionPacket,\n callKey: Uint8Array\n): Promise<Uint8Array<ArrayBuffer>> {\n return encoder.encode(await encryptWithChatKey(JSON.stringify(packet), callKey))\n}\n\n/** Open a received caption; null when it is not under K or malformed. */\nexport async function decodeCaptionPacket(\n payload: Uint8Array,\n callKey: Uint8Array\n): Promise<CaptionPacket | null> {\n try {\n const parsed: unknown = JSON.parse(await decryptWithChatKey(decoder.decode(payload), callKey))\n if (!parsed || typeof parsed !== 'object') return null\n const p = parsed as Partial<CaptionPacket>\n if (\n p.v !== 1 ||\n typeof p.segment_id !== 'string' ||\n !SEGMENT_ID_PATTERN.test(p.segment_id) ||\n typeof p.text !== 'string' ||\n typeof p.identity !== 'string'\n ) {\n return null\n }\n const text = p.text.trim().slice(0, CAPTION_MAX_CHARS)\n if (!text) return null\n return {\n v: 1,\n segment_id: p.segment_id,\n text,\n identity: p.identity,\n name: typeof p.name === 'string' ? p.name : p.identity,\n t: typeof p.t === 'number' ? p.t : Date.now(),\n ...(p.proxy === true ? { proxy: true } : {}),\n }\n } catch {\n return null\n }\n}\n\n// ── Persistence routing ─────────────────────────────────────────────────────\n\n/**\n * Who stores a segment:\n * - `own`: my own segment, and I can post to the meeting chat;\n * - `proxy`: someone else's segment marked `proxy` (they cannot post: a workspace guest), and I\n * can post — every such member stores it, the server dedupes by segment id;\n * - null: the speaker stores it themselves, or I cannot post (read-only, or no call key to open\n * the meeting chat with). My own segment then goes out marked `proxy` for members to store.\n */\nexport function transcriptPersistRole(params: {\n myIdentity: string\n /** I can post to the meeting chat (not a workspace guest of a thread call). */\n canPost: boolean\n hasCallKey: boolean\n speakerIdentity: string\n /** The caption was marked `proxy` by its speaker. */\n speakerNeedsProxy: boolean\n}): 'own' | 'proxy' | null {\n if (!params.canPost || !params.hasCallKey) return null\n if (params.speakerIdentity === params.myIdentity) return 'own'\n if (params.speakerNeedsProxy) return 'proxy'\n return null\n}\n\n/** The thread comment text of a segment (\"Name: text\"). */\nexport function threadTranscriptText(segment: TranscriptSegment): string {\n return `${segment.speakerName}: ${segment.text}`\n}\n\n/**\n * Store one segment in the call's meeting chat (made on first use): an E2E chat message of kind\n * `transcript` under the chat key, or a comment with a transcript marker in a workspace meeting\n * thread. Idempotent per segment id. Returns the chat it went to.\n *\n * In a thread the marker's `speaker_ext_id` is the speaker: the server accepts it from the speaker,\n * or from a member when the speaker is a guest of the workspace (422 `speaker_mismatch` otherwise).\n */\nexport async function persistTranscriptSegment(\n arbi: Arbi,\n queryClient: QueryClient,\n callExtId: string,\n callKey: Uint8Array,\n segment: TranscriptSegment\n): Promise<MeetingChatRef> {\n const ref = await ensureMeetingChat(arbi, queryClient, callExtId, callKey)\n await storeTranscriptSegment(arbi, queryClient, ref, segment)\n return ref\n}\n\n/**\n * Store one segment in a known meeting chat (see {@link persistTranscriptSegment}); also used for\n * the lines transcribed from a recording (segment ids `rec-<track>-<seq>`).\n */\nexport async function storeTranscriptSegment(\n arbi: Arbi,\n queryClient: QueryClient,\n ref: MeetingChatRef,\n segment: TranscriptSegment\n): Promise<void> {\n if (ref.kind === 'chat') {\n const chat = await arbi.chats.ready(await chatByExtId(arbi, queryClient, ref.chatExtId))\n const ring = await arbi.chats.ring(chat)\n const key = ring.keys.get(ring.currentVersion)\n if (!key) throw new Error('No key for the meeting chat')\n const content: chats.TranscriptContent = {\n text: segment.text,\n speaker_identity: segment.speakerIdentity,\n speaker_name: segment.speakerName,\n started_at: segment.startedAt,\n }\n await arbi.calls.transcription.postChatLine(ref.chatExtId, {\n content: await encryptWithChatKey(JSON.stringify(content), key),\n key_version: ring.currentVersion,\n dedupe_key: segment.segmentId,\n })\n return\n }\n const transcript: calls.TranscriptSegmentRef = {\n segment_id: segment.segmentId,\n speaker_ext_id: segment.speakerIdentity,\n started_at: segment.startedAt,\n }\n // A comment in the workspace meeting thread, idempotent per segment. The server takes it from its\n // speaker, or from a member for a speaker with the workspace guest role.\n await arbi.conversations.postMessage({\n content: threadTranscriptText(segment),\n conversation_ext_id: ref.conversationExtId,\n assistant_mode: 'mentioned',\n transcript,\n })\n}\n","/**\n * A stored call recording, opened in the browser: keys unwrapped, parts downloaded and decrypted\n * on demand, exported as files, and transcribed (ARBI `docs/meeting-chats-plan.md` §6).\n * See `recording-playback.ts` for the playback and export formats.\n */\n\nimport type { QueryClient } from '@tanstack/react-query'\nimport { continuityPrompt, type Arbi, type calls } from '@arbidocs/sdk/browser'\nimport { conversationKeyForCall, decryptPart, unwrapTrackKey } from './recording'\nimport {\n EXPORT_SAMPLE_RATE,\n RECORDING_SEGMENTER,\n createTrackCutter,\n encodeWav,\n floatToWav,\n extensionFor,\n fileSafe,\n splitIntoFiles,\n standalone,\n timelineDurationMs,\n timelineOf,\n writeSamples,\n type TrackPiece,\n} from './recording-playback'\nimport { storeTranscriptSegment, type TranscriptSegment } from './transcription'\nimport type { MeetingChatRef } from './meeting-chat'\n\nexport interface OpenedRecording {\n recording: calls.CallRecording\n /** File key per track ext id (a track whose key does not open is absent). */\n keys: Map<string, Uint8Array>\n}\n\n/** Read a recording and unwrap its tracks' file keys under the call's conversation key. */\nexport async function openRecording(\n arbi: Arbi,\n queryClient: QueryClient,\n call: calls.CallResponse,\n recordingExtId: string,\n loaded?: calls.CallRecording\n): Promise<OpenedRecording> {\n const recording = loaded ?? (await arbi.calls.recording.get(call.external_id, recordingExtId))\n const convKeys = new Map<string, Promise<Uint8Array>>()\n const keys = new Map<string, Uint8Array>()\n for (const track of recording.tracks ?? []) {\n const version = track.key_version ?? null\n const id = String(version)\n if (!convKeys.has(id)) {\n convKeys.set(\n id,\n conversationKeyForCall(arbi, queryClient, call, { keyVersion: version }).then((c) => c.key)\n )\n }\n try {\n keys.set(track.external_id, await unwrapTrackKey(track.wrapped_key, await convKeys.get(id)!))\n } catch (err) {\n console.warn('[calls] a recording track key does not open', err)\n }\n }\n return { recording, keys }\n}\n\nexport function speakerTracks(recording: calls.CallRecording): calls.RecordingTrack[] {\n return (recording.tracks ?? []).filter((t) => t.kind === 'speaker')\n}\n\nexport function compositeTrack(recording: calls.CallRecording): calls.RecordingTrack | null {\n return (recording.tracks ?? []).find((t) => t.kind === 'composite') ?? null\n}\n\nexport function sortedParts(track: calls.RecordingTrack): calls.RecordingPart[] {\n return [...(track.parts ?? [])].sort((a, b) => a.seq - b.seq)\n}\n\n/** One part's plaintext. */\nexport async function fetchPart(\n arbi: Arbi,\n part: calls.RecordingPart,\n fileKey: Uint8Array\n): Promise<Uint8Array> {\n if (!part.download_url) throw new Error('This recording’s media was deleted')\n return decryptPart(await arbi.calls.recording.downloadPart(part.download_url), fileKey)\n}\n\n/** The composite as playable files (normally one). */\nexport async function compositeFiles(\n arbi: Arbi,\n track: calls.RecordingTrack,\n fileKey: Uint8Array\n): Promise<Blob[]> {\n const parts: Uint8Array[] = []\n for (const part of sortedParts(track)) parts.push(await fetchPart(arbi, part, fileKey))\n const type = track.mime_type.split(';')[0] || 'video/webm'\n return splitIntoFiles(parts).map((file) => new Blob(file.map(standalone), { type }))\n}\n\n/** Decode a standalone audio part to mono samples at 16 kHz (decodeAudioData resamples). */\nexport async function decodeMono16k(bytes: Uint8Array): Promise<Float32Array> {\n const ctx = new OfflineAudioContext(1, 1, EXPORT_SAMPLE_RATE)\n const buffer = await ctx.decodeAudioData(standalone(bytes).buffer)\n return buffer.getChannelData(0)\n}\n\n/** A speaker track as ONE WAV (16 kHz mono), its parts at their offsets from the recording start. */\nexport async function speakerWav(\n arbi: Arbi,\n track: calls.RecordingTrack,\n fileKey: Uint8Array,\n onPart?: () => void\n): Promise<Blob> {\n const parts = sortedParts(track)\n const totalMs = timelineDurationMs(timelineOf([track]))\n const samples = new Int16Array(Math.ceil((totalMs / 1000) * EXPORT_SAMPLE_RATE) + 1)\n // decodeAudioData resamples to the context's rate.\n for (const part of parts) {\n const bytes = await fetchPart(arbi, part, fileKey)\n try {\n writeSamples(\n samples,\n await decodeMono16k(bytes),\n (part.offset_ms / 1000) * EXPORT_SAMPLE_RATE\n )\n } catch (err) {\n console.warn('[calls] a recording part does not decode', err)\n }\n onPart?.()\n }\n return new Blob([encodeWav(samples, EXPORT_SAMPLE_RATE)], { type: 'audio/wav' })\n}\n\n/** Every decrypted file of a recording: one WAV per speaker, the composite as one video. */\nexport async function recordingFiles(\n arbi: Arbi,\n opened: OpenedRecording,\n title: string | null | undefined,\n onProgress?: (done: number, total: number) => void\n): Promise<File[]> {\n const { recording, keys } = opened\n const base = fileSafe(title || 'Meeting')\n const date = recording.started_at.slice(0, 10)\n const total = (recording.tracks ?? []).reduce((n, t) => n + (t.parts?.length ?? 0), 0)\n let done = 0\n const tick = () => onProgress?.(++done, total)\n const files: File[] = []\n for (const track of speakerTracks(recording)) {\n const key = keys.get(track.external_id)\n if (!key || !track.parts?.length) continue\n const wav = await speakerWav(arbi, track, key, tick)\n const who = fileSafe(track.speaker_name || track.speaker_ext_id || 'speaker')\n files.push(new File([wav], `${base}-${date}-${who}.wav`, { type: 'audio/wav' }))\n }\n const composite = compositeTrack(recording)\n const compositeKey = composite && keys.get(composite.external_id)\n if (composite && compositeKey && composite.parts?.length) {\n const videos = await compositeFiles(arbi, composite, compositeKey)\n done += composite.parts.length\n onProgress?.(done, total)\n const ext = extensionFor(composite.mime_type)\n videos.forEach((blob, i) =>\n files.push(\n new File([blob], `${base}-${date}-video${videos.length > 1 ? `-${i + 1}` : ''}.${ext}`, {\n type: blob.type,\n })\n )\n )\n }\n return files\n}\n\n/** A track's previous text, kept for the continuity prompt (a little more than it sends). */\nconst PROMPT_HISTORY_CHARS = 400\n\n/**\n * The host's \"Transcribe\": each speaker track is decrypted and decoded here part by part, its parts\n * placed on ONE continuous timeline by their offsets (gaps are silence) and cut at the track's\n * pauses ({@link createTrackCutter} with {@link RECORDING_SEGMENTER}: the live 700 ms pause rule,\n * pieces up to 25 s), so no sentence is split at a part boundary. Each piece (a 16 kHz mono WAV) is\n * sent through the shared transcription helper WITHOUT a part `seq`: its index across the track\n * (`segment`) and its start from the recording start (`segment_offset_ms`) → segment id\n * `rec-<track>-s<n>`. The user's language and the speaker's previous text (continuity prompt) go\n * with every piece.\n *\n * Each line is stored as an attributed transcript entry exactly like a live one (P5) — an E2E chat\n * message of kind `transcript` with `dedupe_key` = the segment id, or a thread comment with the\n * transcript marker — so running it twice stores each line once; the recording's lines then\n * supersede the live ones of the same window. A part this browser cannot decode is sent whole\n * (`rec-<track>-<seq>`) and is silence on the timeline.\n */\nexport async function transcribeRecording(params: {\n arbi: Arbi\n queryClient: QueryClient\n callExtId: string\n opened: OpenedRecording\n ref: MeetingChatRef\n onProgress?(done: number, total: number): void\n /** Decoder (tests); default WebAudio at 16 kHz. */\n decode?(bytes: Uint8Array): Promise<Float32Array>\n /** ISO-639-1 spoken language, when known; otherwise the transcriber detects it. */\n language?: string\n}): Promise<{ stored: number; failed: number }> {\n const {\n arbi,\n queryClient,\n callExtId,\n opened,\n ref,\n onProgress,\n decode = decodeMono16k,\n language,\n } = params\n const { recording, keys } = opened\n const tracks = speakerTracks(recording).filter((t) => keys.has(t.external_id))\n const total = tracks.reduce((n, t) => n + (t.parts?.length ?? 0), 0)\n const start = Date.parse(recording.started_at)\n let done = 0\n let stored = 0\n let failed = 0\n\n for (const track of tracks) {\n const key = keys.get(track.external_id)!\n const cutter = createTrackCutter(EXPORT_SAMPLE_RATE, RECORDING_SEGMENTER, { mergeShort: true })\n let previous = ''\n let nextSegment = 0\n\n const send = async (request: {\n audio: Blob\n seq?: number\n segment?: number\n /** From the recording start (a piece), or from the part start (a whole part: 0). */\n offsetMs: number\n }) => {\n try {\n const result = await arbi.calls.recording.transcribe(callExtId, recording.external_id, {\n audio: request.audio,\n trackExtId: track.external_id,\n seq: request.seq ?? null,\n segment: request.segment ?? null,\n segmentOffsetMs: request.seq == null ? request.offsetMs : 0,\n language,\n prompt: continuityPrompt(previous),\n })\n const text = result.text.trim()\n if (!text) return\n previous = `${previous} ${text}`.slice(-PROMPT_HISTORY_CHARS)\n const segment: TranscriptSegment = {\n segmentId:\n result.segment_id ||\n (request.seq != null\n ? `rec-${track.external_id}-${request.seq}`\n : `rec-${track.external_id}-s${request.segment}`),\n text,\n speakerIdentity: result.speaker_ext_id ?? track.speaker_ext_id ?? '',\n speakerName: result.speaker_name ?? track.speaker_name ?? 'Speaker',\n startedAt: Number.isFinite(start)\n ? new Date(start + (result.offset_ms ?? Math.round(request.offsetMs))).toISOString()\n : null,\n }\n await storeTranscriptSegment(arbi, queryClient, ref, segment)\n stored += 1\n } catch (err) {\n failed += 1\n console.warn('[calls] a recording piece could not be transcribed', err)\n }\n }\n const sendPieces = async (pieces: TrackPiece[]) => {\n for (const piece of pieces) {\n await send({\n audio: new Blob([floatToWav(piece.samples, EXPORT_SAMPLE_RATE)], { type: 'audio/wav' }),\n segment: nextSegment++,\n offsetMs: (piece.start / EXPORT_SAMPLE_RATE) * 1000,\n })\n }\n }\n\n for (const part of sortedParts(track)) {\n let bytes: Uint8Array\n try {\n bytes = await fetchPart(arbi, part, key)\n } catch (err) {\n failed += 1\n console.warn('[calls] a recording part could not be read', err)\n onProgress?.(++done, total)\n continue\n }\n let pcm: Float32Array | null = null\n try {\n pcm = await decode(bytes)\n } catch (err) {\n console.warn('[calls] a recording part does not decode; sending it whole', err)\n }\n if (pcm) {\n await sendPieces(cutter.push(part.offset_ms, pcm))\n } else {\n await send({\n audio: new Blob([standalone(bytes)], { type: track.mime_type }),\n seq: part.seq,\n offsetMs: part.offset_ms,\n })\n }\n onProgress?.(++done, total)\n }\n // The end of the track: the last piece (a closing line) is sent too.\n await sendPieces(cutter.finish())\n }\n return { stored, failed }\n}\n","/**\n * Call data hooks over `arbi.calls`: the deployment's calls config and a call by id. They need no\n * LiveKit; the room hooks live in the `@arbidocs/react/calls` entry.\n *\n * Requires an `ArbiHost` (with an `arbi`) and a React Query client above it.\n */\n\nimport { useCallback, useMemo } from 'react'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { calls } from '@arbidocs/sdk/browser'\nimport { useArbi } from '../useArbi'\nimport { callQueryKeys } from './keys'\nimport { meetingRefForCall } from './recording'\nimport { openRecording, transcribeRecording, type OpenedRecording } from './recording-media'\nimport { recordingWindowsOf, type RecordingWindow } from './transcript-format'\n\n/**\n * The deployment's calls config. Any error (404 on a backend without calls, 5xx) reads as\n * \"disabled\", so call buttons simply stay hidden.\n */\nexport function useCallsConfig(): calls.CallsConfig | undefined {\n const arbi = useArbi()\n // A plain query (no abort signal): the header buttons remount while the app boots, and an\n // aborted request must not stick as \"disabled\".\n const { data } = useQuery({\n queryKey: callQueryKeys.config(),\n queryFn: () => arbi.calls.config(),\n enabled: arbi.isLoggedIn,\n staleTime: 5 * 60_000,\n retry: false,\n })\n return data\n}\n\n/** Calls are available on this deployment (the server has LiveKit configured). */\nexport function useServerCallsEnabled(): boolean {\n return useCallsConfig()?.enabled === true\n}\n\n/**\n * Calls are still experimental on this deployment (the default): the call UI shows only to people\n * who opted in with the host's switch. A deployment that made calls a regular feature\n * (`CALLS_EXPERIMENTAL=false`, `experimental: false`) shows it to everyone. Unknown reads as\n * experimental.\n */\nexport function useServerCallsExperimental(): boolean {\n return useCallsConfig()?.experimental !== false\n}\n\n/** Whether this deployment has a transcriber. Any error reads as \"no\": the controls stay hidden. */\nexport function useCallTranscriptionAvailable(): boolean {\n return useCallsConfig()?.transcription_available === true\n}\n\n/** A call by id (the thread log card, a chat's call card); refreshed by `call.started/ended`. */\nexport function useCall(callExtId: string | null | undefined) {\n const arbi = useArbi()\n return useQuery({\n queryKey: callQueryKeys.call(callExtId ?? ''),\n queryFn: () => arbi.calls.get(callExtId!),\n enabled: !!callExtId && arbi.isLoggedIn,\n staleTime: 60_000,\n retry: false,\n })\n}\n\n/**\n * A call's recordings (readers only: members of its meeting chat). Kept fresh by the recording\n * events (`applyCallEvent`).\n */\nexport function useCallRecordings(callExtId: string | null | undefined, enabled = true) {\n const arbi = useArbi()\n return useQuery({\n queryKey: callQueryKeys.recordings(callExtId ?? ''),\n queryFn: () => arbi.calls.recording.list(callExtId!),\n enabled: !!callExtId && enabled && arbi.isLoggedIn,\n retry: false,\n })\n}\n\n/** One recording of a call. */\nexport function useCallRecording(\n callExtId: string | null | undefined,\n recordingExtId: string | null | undefined\n) {\n const arbi = useArbi()\n return useQuery({\n queryKey: callQueryKeys.recording(callExtId ?? '', recordingExtId ?? ''),\n queryFn: () => arbi.calls.recording.get(callExtId!, recordingExtId!),\n enabled: !!callExtId && !!recordingExtId && arbi.isLoggedIn,\n retry: false,\n })\n}\n\nconst NO_WINDOWS: RecordingWindow[] = []\n\n/**\n * The call's recordings as windows: a recording's transcript supersedes the live lines spoken while\n * it recorded. Fetched only when `enabled` (the transcript has lines from a recording), sharing the\n * recordings list's cache. Empty while loading or when the list cannot be read.\n */\nexport function useRecordingWindows(\n callExtId: string | null | undefined,\n enabled: boolean\n): RecordingWindow[] {\n const { data } = useCallRecordings(callExtId, enabled)\n return useMemo(() => (data ? recordingWindowsOf(data) : NO_WINDOWS), [data])\n}\n\n/** The personal invite links sent for a call (the host's people picker). */\nexport function useCallInviteLinks(callExtId: string | null | undefined) {\n const arbi = useArbi()\n return useQuery({\n queryKey: callQueryKeys.inviteLinks(callExtId ?? ''),\n queryFn: () => arbi.calls.inviteLinks.list(callExtId!),\n enabled: !!callExtId && arbi.isLoggedIn,\n retry: false,\n })\n}\n\n/**\n * What a recording card does with a call's recordings: re-read them, open one (its track keys,\n * unwrapped under the meeting chat's or workspace's key), and transcribe one into the meeting chat.\n */\nexport function useRecordingActions(call: calls.CallResponse) {\n const arbi = useArbi()\n const queryClient = useQueryClient()\n return useMemo(\n () => ({\n refresh: () =>\n void queryClient.invalidateQueries({\n queryKey: callQueryKeys.recordingsOf(call.external_id),\n }),\n open: (recording: calls.CallRecording): Promise<OpenedRecording> =>\n openRecording(arbi, queryClient, call, recording.external_id, recording),\n transcribe: async (\n opened: OpenedRecording,\n onProgress: (done: number, total: number) => void\n ) =>\n transcribeRecording({\n arbi,\n queryClient,\n callExtId: call.external_id,\n opened,\n ref: await meetingRefForCall(arbi, queryClient, call),\n onProgress,\n }),\n }),\n [arbi, queryClient, call]\n )\n}\n\n/** Revoke a personal invite link of a call I host; the list re-reads. */\nexport function useRevokeCallInviteLink(callExtId: string) {\n const arbi = useArbi()\n const queryClient = useQueryClient()\n return useCallback(\n async (inviteExtId: string) => {\n await arbi.calls.inviteLinks.revoke(callExtId, inviteExtId)\n void queryClient.invalidateQueries({ queryKey: callQueryKeys.inviteLinks(callExtId) })\n },\n [arbi, queryClient, callExtId]\n )\n}\n","/**\n * The host's \"Save … to thread\": one comment in the picked thread, or a new thread titled after the\n * meeting when no thread is picked (`conversationExtId` null). Resolves to the thread it went to.\n */\n\nimport { useMemo } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { documents, type Arbi } from '@arbidocs/sdk/browser'\nimport { useArbi } from '../useArbi'\nimport { invalidateConversationLists } from '../conversation-list'\nimport { formatTranscriptMarkdown, type TranscriptExportLine } from './transcript-format'\n\nasync function postToThread(\n arbi: Arbi,\n conversationExtId: string | null,\n newThreadTitle: string,\n content: string\n): Promise<string | null> {\n const posted = await arbi.conversations.postMessage({\n content,\n assistant_mode: 'mentioned',\n ...(conversationExtId ? { conversation_ext_id: conversationExtId } : { title: newThreadTitle }),\n })\n return posted?.conversation_ext_id ?? null\n}\n\n/** The (locally decrypted) transcript as ONE comment. */\nexport function postTranscriptToThread(\n arbi: Arbi,\n params: {\n conversationExtId: string | null\n title: string | null | undefined\n lines: TranscriptExportLine[]\n }\n): Promise<string | null> {\n return postToThread(\n arbi,\n params.conversationExtId,\n `Transcript: ${params.title?.trim() || 'Meeting'}`,\n formatTranscriptMarkdown({ title: params.title, lines: params.lines })\n )\n}\n\n/** A recording's uploaded documents, listed as `doc-…` pills in ONE comment. */\nexport function postRecordingDocsToThread(\n arbi: Arbi,\n params: {\n conversationExtId: string | null\n title: string | null | undefined\n docExtIds: string[]\n }\n): Promise<string | null> {\n const meeting = params.title?.trim() || 'Meeting'\n const content = [\n `**Recording: ${meeting}**`,\n '',\n ...params.docExtIds.map((id) => `- \\`${id}\\``),\n ].join('\\n')\n return postToThread(arbi, params.conversationExtId, `Recording: ${meeting}`, content)\n}\n\n/**\n * A recording's decrypted files, uploaded as shared documents into the call's workspace\n * (`workspaceExtId`) and listed in ONE comment. They are encrypted here under that workspace's key\n * and sent straight to storage (`uploadDirect`, whose init and commit name the workspace); without\n * a key of mine for it they go through the server, named. Never into whichever workspace happens\n * to be open.\n */\nexport async function saveRecordingFilesToThread(\n arbi: Arbi,\n params: {\n workspaceExtId: string\n files: File[]\n conversationExtId: string | null\n title: string | null | undefined\n /** The upload starts (after the files are decrypted). */\n onUploading?: () => void\n }\n): Promise<string | null> {\n const workspaceId = params.workspaceExtId\n // A workspace I hold no key for (public, a temporary grant, a guest's) goes through the server,\n // which encrypts; the upload names the workspace either way.\n const workspaceKey = await arbi.workspaces.key(workspaceId).catch(() => null)\n params.onUploading?.()\n const result = workspaceKey\n ? await arbi.documents.uploadDirect(\n workspaceId,\n workspaceKey,\n await Promise.all(\n params.files.map(async (file) => ({\n name: file.name,\n data: new Uint8Array(await file.arrayBuffer()),\n contentType: file.type || documents.mimeFromName(file.name),\n }))\n ),\n { shared: true }\n )\n : await arbi.documents.upload(params.files, { shared: true, workspaceExtId: workspaceId })\n const docExtIds = result.doc_ext_ids ?? []\n const skipped = result.skipped ?? []\n if (!docExtIds.length) {\n throw new Error(`The files were not uploaded${skipped[0] ? `: ${skipped[0].reason}` : ''}`)\n }\n return postRecordingDocsToThread(arbi, {\n conversationExtId: params.conversationExtId,\n title: params.title,\n docExtIds,\n })\n}\n\n/** The \"Save … to thread\" actions on the signed-in session; the thread lists are re-read after. */\nexport function useSaveToThread() {\n const arbi = useArbi()\n const queryClient = useQueryClient()\n return useMemo(() => {\n const thenRefresh = async <T>(run: Promise<T>): Promise<T> => {\n const result = await run\n void invalidateConversationLists(queryClient)\n return result\n }\n return {\n transcript: (params: Parameters<typeof postTranscriptToThread>[1]) =>\n thenRefresh(postTranscriptToThread(arbi, params)),\n recording: (params: Parameters<typeof saveRecordingFilesToThread>[1]) =>\n thenRefresh(saveRecordingFilesToThread(arbi, params)),\n }\n }, [arbi, queryClient])\n}\n","/**\n * Call actions — usable outside React (buttons, notification actions, the WS\n * ringer). One call system: every call has a random key K made in the\n * creator's browser (see `@arbidocs/client` call-keys):\n *\n * - start (thread / chat / contacts): generate K, create the call with K\n * sealed to ourselves and to the people picked, show the pre-join; on\n * join, ring them. Nobody else is rung: a thread call rings only the\n * members picked, never the whole workspace. Unregistered contacts get a\n * personal invite link by e-mail instead (register, then straight into the\n * call — there are no anonymous guests);\n * - accept a ring: open K from the ring's sealed blob and join at once;\n * - open a call (link / calendar event / notification): K from the link fragment,\n * else our sealed copy on the server.\n *\n * A thread call is get-or-create: if the thread already has a running call we\n * hold the key for, we join that one instead of starting another.\n */\n\nimport { buildCallLink, type CallPeer } from '@arbidocs/client'\nimport { CallApiError, type Arbi, type calls } from '@arbidocs/sdk/browser'\nimport { notify } from '../notifications/notify-store'\nimport {\n callSession,\n DEFAULT_JOIN_CHOICES,\n type ActiveCall,\n type IncomingCall,\n type JoinChoices,\n} from './session'\n\nexport type { CallPeer }\n\n/** `https://<host>/call/<id>#k=…&v=…` — the shareable link. */\nexport function callLink(callExtId: string, key: Uint8Array, linkVersion: number): string {\n return buildCallLink(window.location.origin, callExtId, key, linkVersion)\n}\n\nfunction alreadyInCall(callExtId?: string): boolean {\n const active = callSession.getState().active\n if (!active || active.callExtId === callExtId) return false\n notify.warning('You are already in a call. Leave it first.')\n return true\n}\n\nfunction fail(err: unknown, what = 'start the call') {\n console.error(`[calls] failed to ${what}`, err)\n const message = err instanceof Error ? err.message : 'Unknown error'\n notify.error(`Could not ${what}: ${message}`)\n}\n\n/** Where a call belongs, for the active call (and the in-call chat). */\nfunction callContext(\n call: Pick<\n calls.CallResponse,\n | 'chat_ext_id'\n | 'log_conversation_ext_id'\n | 'meeting_conversation_ext_id'\n | 'locked_workspace_ext_id'\n >\n): Pick<\n ActiveCall,\n 'chatExtId' | 'logConversationExtId' | 'meetingConversationExtId' | 'lockedWorkspaceExtId'\n> {\n return {\n chatExtId: call.chat_ext_id ?? null,\n logConversationExtId: call.log_conversation_ext_id ?? null,\n meetingConversationExtId: call.meeting_conversation_ext_id ?? null,\n lockedWorkspaceExtId: call.locked_workspace_ext_id ?? null,\n }\n}\n\n/** An unregistered contact who gets a personal invite link by e-mail. */\nexport interface CallInviteContact {\n contactExtId: string\n /** The name they are invited under; encrypted under K. */\n name: string\n}\n\n/**\n * Mail each contact a personal invite link: an ARBI registration link that, once they have an\n * account, opens `/call/<id>?i=<token>#k=…` and admits them straight into the call. Best effort: a\n * failure for one contact is reported and does not stop the others. Resolves to how many went out.\n */\nasync function sendInviteLinks(\n arbi: Arbi,\n callExtId: string,\n key: Uint8Array,\n linkVersion: number,\n invites: CallInviteContact[]\n): Promise<number> {\n let sent = 0\n for (const invite of invites) {\n try {\n await arbi.calls.inviteLinks.create(callExtId, {\n contact_ext_id: invite.contactExtId,\n link: callLink(callExtId, key, linkVersion),\n })\n sent += 1\n } catch (err) {\n fail(err, `invite ${invite.name || 'a contact'}`)\n }\n }\n if (sent) notify.success(`Sent ${sent} ${sent === 1 ? 'invitation' : 'invitations'}`)\n return sent\n}\n\n/**\n * Create a call and open its pre-join. `ring` are invited now (K sealed to each) and rung when we\n * join; `invites` get a personal invite link by e-mail straight away.\n */\nasync function startCall(\n arbi: Arbi,\n opts: {\n context: { chatExtId?: string | null; threadExtId?: string | null }\n title?: string | null\n ring: CallPeer[]\n invites?: CallInviteContact[]\n }\n): Promise<boolean> {\n if (alreadyInCall()) return false\n try {\n const {\n call,\n key,\n title,\n sealedKeys: ringOnJoin,\n } = await arbi.calls.start({ ...opts.context, title: opts.title, invitees: opts.ring })\n callSession.setActiveCall({\n callExtId: call.external_id,\n key,\n linkVersion: call.link_version,\n title,\n isCreator: call.is_creator,\n ...callContext(call),\n phase: 'prejoin',\n tokenResponse: null,\n choices: null,\n knock: null,\n ringOnJoin: Object.keys(ringOnJoin).length ? ringOnJoin : null,\n })\n if (opts.invites?.length) {\n void sendInviteLinks(arbi, call.external_id, key, call.link_version, opts.invites)\n }\n return true\n } catch (err) {\n fail(err)\n return false\n }\n}\n\n/**\n * A thread call (shared threads only): locked to the thread's workspace and logged in the thread.\n * Get-or-create: a running call on the thread is joined. Only `ring` (the members picked) are rung.\n */\nexport function startThreadCall(\n arbi: Arbi,\n opts: {\n threadExtId: string\n title?: string | null\n ring: CallPeer[]\n }\n): Promise<boolean> {\n return startCall(arbi, {\n context: { threadExtId: opts.threadExtId },\n title: opts.title,\n ring: opts.ring,\n })\n}\n\n/** A chat's call (a DM or a group): the chat is its meeting chat. `ring` are rung when we join. */\nexport function startChatCall(\n arbi: Arbi,\n opts: {\n chatExtId: string\n title?: string | null\n ring: CallPeer[]\n }\n): Promise<boolean> {\n return startCall(arbi, {\n context: { chatExtId: opts.chatExtId },\n title: opts.title,\n ring: opts.ring,\n })\n}\n\n/** Call a person: the call of our direct chat (made if missing), ringing them. */\nexport async function startDmCall(\n arbi: Arbi,\n peer: CallPeer & { name?: string | null }\n): Promise<boolean> {\n if (alreadyInCall()) return false\n if (!peer.encryption_public_key) {\n fail(new Error('This person cannot receive encrypted calls'))\n return false\n }\n let chatExtId: string\n try {\n chatExtId = (await arbi.chats.openDirect(peer.external_id)).external_id\n } catch (err) {\n fail(err)\n return false\n }\n return startChatCall(arbi, { chatExtId, title: peer.name ?? null, ring: [peer] })\n}\n\n/**\n * A contacts call (\"New call\"): registered contacts are rung, unregistered ones get a personal invite\n * link. Its meeting chat is made on first use.\n */\nexport function startContactsCall(\n arbi: Arbi,\n opts: {\n title?: string | null\n ring: CallPeer[]\n invites: CallInviteContact[]\n }\n): Promise<boolean> {\n return startCall(arbi, { context: {}, title: opts.title, ring: opts.ring, invites: opts.invites })\n}\n\n/**\n * Open a call by id (link, upcoming list, notification) into the pre-join.\n * `key` comes from the link fragment; without it we use our sealed copy.\n * `inviteToken` (a personal invite link's `?i=`) is redeemed on join: straight in, no lobby.\n */\nexport async function openCall(\n arbi: Arbi,\n opts: {\n callExtId: string\n key?: Uint8Array | null\n linkVersion?: number | null\n inviteToken?: string | null\n }\n): Promise<boolean> {\n if (alreadyInCall(opts.callExtId)) return false\n try {\n const { call, key, title } = await arbi.calls.open(opts.callExtId, opts.key)\n callSession.setActiveCall({\n callExtId: call.external_id,\n key,\n linkVersion: opts.linkVersion ?? call.link_version,\n title,\n isCreator: call.is_creator,\n ...callContext(call),\n phase: 'prejoin',\n tokenResponse: null,\n choices: null,\n knock: null,\n ringOnJoin: null,\n inviteToken: opts.inviteToken ?? null,\n })\n return true\n } catch (err) {\n fail(err, 'open the call')\n return false\n }\n}\n\n/** Answer a ring: open K from the ring and join straight away (no pre-join). */\nexport async function acceptRing(arbi: Arbi, ring: IncomingCall): Promise<boolean> {\n // Accepting while in another call switches to the new one.\n const current = callSession.getState().active\n if (current && current.callExtId !== ring.call_ext_id) callSession.endActiveCall()\n try {\n const { key, token, title } = await arbi.calls.answer(ring)\n callSession.setActiveCall({\n callExtId: ring.call_ext_id,\n key,\n linkVersion: ring.link_version,\n title: title || ring.caller_name,\n isCreator: false,\n chatExtId: ring.chat_ext_id ?? null,\n logConversationExtId: ring.log_conversation_ext_id ?? null,\n meetingConversationExtId: ring.meeting_conversation_ext_id ?? null,\n lockedWorkspaceExtId: ring.context === 'thread' ? (ring.workspace_ext_id ?? null) : null,\n phase: 'connected',\n tokenResponse: token,\n choices: DEFAULT_JOIN_CHOICES,\n knock: null,\n ringOnJoin: null,\n })\n return true\n } catch (err) {\n fail(err, 'join the call')\n callSession.removeIncomingCall(ring.call_ext_id)\n return false\n }\n}\n\nexport function declineRing(arbi: Arbi, ring: IncomingCall): void {\n callSession.removeIncomingCall(ring.call_ext_id)\n arbi.calls\n .decline(ring.call_ext_id)\n .catch((err: unknown) => console.warn('[calls] decline failed', err))\n}\n\nconst KNOCK_POLL_MS = 2_000\n\n/**\n * Wait in the lobby: poll a knock until it is admitted (→ token), denied or\n * expired (→ throws). `signal` aborts the wait (the user gave up).\n */\nasync function waitForAdmission(\n arbi: Arbi,\n callExtId: string,\n knock: Pick<calls.CallKnockResponse, 'knock_id'>,\n signal: AbortSignal\n): Promise<calls.CallTokenResponse> {\n for (;;) {\n if (signal.aborted) throw new DOMException('Stopped waiting', 'AbortError')\n await new Promise((resolve) => setTimeout(resolve, KNOCK_POLL_MS))\n if (signal.aborted) throw new DOMException('Stopped waiting', 'AbortError')\n const state = await arbi.calls.pollKnock(callExtId, knock.knock_id)\n if (state.status === 'admitted' && state.token) return state.token\n if (state.status === 'denied') throw new CallApiError('The host declined your request.', 403)\n if (state.status === 'expired') throw new CallApiError('Nobody let you in. Try again.', 410)\n }\n}\n\n/**\n * Pre-join done: get a token (or knock when the policy says so), connect, and\n * ring whoever this call was started for.\n */\nexport async function joinFromPreJoin(arbi: Arbi, choices: JoinChoices): Promise<void> {\n const active = callSession.getState().active\n if (!active) return\n const { callExtId, linkVersion, inviteToken } = active\n const update = (patch: Partial<ActiveCall>) => callSession.updateActiveCall(callExtId, patch)\n try {\n let tokenResponse: calls.CallTokenResponse\n try {\n // A personal invite link admits its account straight in (no lobby).\n tokenResponse = await arbi.calls.token(callExtId, linkVersion, inviteToken)\n } catch (err) {\n if (!(err instanceof CallApiError && err.code === 'knock_required')) throw err\n // Knock as the signed-in user (the server names me to the hosts from my account); the\n // token comes at once when the policy lets us straight in.\n const knock = await arbi.calls.knock(callExtId, linkVersion)\n if (knock.status === 'admitted' && knock.token) tokenResponse = knock.token\n else {\n const controller = new AbortController()\n update({ phase: 'knocking', choices, knock: { abort: () => controller.abort() } })\n tokenResponse = await waitForAdmission(arbi, callExtId, knock, controller.signal)\n }\n }\n if (callSession.getState().active?.callExtId !== callExtId) return // left meanwhile\n update({ phase: 'connected', tokenResponse, choices, knock: null })\n const ring = active.ringOnJoin\n if (ring && Object.keys(ring).length) {\n update({ ringOnJoin: null })\n await arbi.calls\n .ring(callExtId, ring, linkVersion)\n .catch((err: unknown) => console.warn('[calls] ring failed', err))\n }\n } catch (err) {\n if (err instanceof DOMException && err.name === 'AbortError') return\n // A webinar's audience before go-live: \"Starting soon\", joining by itself once it is live.\n if (err instanceof CallApiError && err.code === 'webinar_not_live') {\n update({ phase: 'webinar-waiting', choices, knock: null })\n return\n }\n fail(err, 'join the call')\n if (callSession.getState().active?.callExtId === callExtId) {\n callSession.endActiveCall()\n }\n }\n}\n","/**\n * The webinar's wire formats on the client: the server's LiveKit data messages (topic\n * `arbi.webinar`, trusted only when server-sent) and the Q&A text, end-to-end under the call key K\n * in the same secretbox format as the call title (`encryptCallTitle` / `decryptCallTitle`), and\n * joining a webinar again without leaving it.\n */\n\nimport type { Arbi, calls } from '@arbidocs/sdk/browser'\nimport { decryptCallTitle, encryptCallTitle } from '@arbidocs/client'\nimport { joinFromPreJoin } from './actions'\nimport { callSession, type JoinChoices } from './session'\n\nexport const CALL_WEBINAR_TOPIC = 'arbi.webinar'\n\n/** The audience cannot publish: no microphone, no camera, no device check. */\nexport const AUDIENCE_CHOICES: JoinChoices = { audioEnabled: false, videoEnabled: false }\n\ntype Hand = NonNullable<calls.WebinarState['hands']>[number]\n\nexport type WebinarPacket =\n | { type: 'state'; live: boolean; live_at: string | null; chat: calls.WebinarChat }\n | { type: 'hands'; hands: Hand[] }\n | { type: 'questions' }\n | { type: 'promoted' }\n | { type: 'demoted' }\n\nconst isHand = (h: unknown): h is Hand =>\n !!h &&\n typeof h === 'object' &&\n typeof (h as Hand).identity === 'string' &&\n typeof (h as Hand).raised_at === 'string'\n\n/**\n * A data packet on {@link CALL_WEBINAR_TOPIC}, or null. `fromParticipant` is whether a participant\n * sent it: only the server speaks on this topic, so anything from a participant is dropped.\n */\nexport function parseWebinarPacket(\n payload: Uint8Array,\n fromParticipant: boolean\n): WebinarPacket | null {\n if (fromParticipant) return null\n let raw: unknown\n try {\n raw = JSON.parse(new TextDecoder().decode(payload))\n } catch {\n return null\n }\n if (!raw || typeof raw !== 'object') return null\n const p = raw as Record<string, unknown>\n switch (p.type) {\n case 'state':\n if (typeof p.live !== 'boolean') return null\n return {\n type: 'state',\n live: p.live,\n live_at: typeof p.live_at === 'string' ? p.live_at : null,\n chat: p.chat === 'everyone' ? 'everyone' : 'panelists',\n }\n case 'hands':\n return {\n type: 'hands',\n hands: Array.isArray(p.hands)\n ? p.hands.filter(isHand).map((h) => ({ ...h, name: String(h.name ?? '') }))\n : [],\n }\n case 'questions':\n case 'promoted':\n case 'demoted':\n return { type: p.type }\n default:\n return null\n }\n}\n\n/** A question with its text opened under K (`null` when it does not open: shown as such). */\nexport interface OpenQuestion extends calls.CallQuestion {\n text: string\n answerText: string | null\n}\n\nconst UNREADABLE = '(This question could not be decrypted.)'\n\nexport function sealQuestionText(text: string, key: Uint8Array): Promise<string> {\n return encryptCallTitle(text, key)\n}\n\nexport async function openQuestion(q: calls.CallQuestion, key: Uint8Array): Promise<OpenQuestion> {\n const [text, answerText] = await Promise.all([\n decryptCallTitle(q.body, key),\n q.answer ? decryptCallTitle(q.answer, key) : Promise.resolve(null),\n ])\n return { ...q, text: text ?? UNREADABLE, answerText }\n}\n\n// ── Joining again ───────────────────────────────────────────────────────────\n//\n// After a moderator moved me to the audience (or I stepped down, or declined the stage) the server\n// removes me from the room, and I come straight back with a new token — hidden and listen-only,\n// because the session's stage state says so. No pre-join and no error screen: the same join path\n// as the pre-join's, with the audience's choices.\n\n/** The room I am in is about to drop me for a rejoin: its disconnect is not a leave. */\nexport function markRejoining(callExtId: string, on = true): void {\n callSession.updateActiveCall(callExtId, { rejoining: on })\n}\n\n/** Join again as the audience (the room that dropped me has already gone). */\nexport async function rejoinAsAudience(arbi: Arbi, callExtId: string): Promise<void> {\n if (callSession.getState().active?.callExtId !== callExtId) return\n markRejoining(callExtId)\n try {\n await joinFromPreJoin(arbi, AUDIENCE_CHOICES)\n } finally {\n markRejoining(callExtId, false)\n }\n}\n","/**\n * Workspace guests may join a thread call (and are transcribed) but its meeting chat is read-only\n * for them: no in-call chat composer, no meeting-thread composer. Their transcript lines are stored\n * by a member's client (see `transcriptPersistRole`).\n */\n\nimport { useWorkspaces } from '../hooks'\nimport { useArbi } from '../useArbi'\n\ninterface WorkspaceRoster {\n external_id: string\n users?: { role?: string | null; user: { external_id: string } }[] | null\n}\n\n/** Whether I am a guest of `workspaceExtId` (pure, for tests). */\nexport function isWorkspaceGuestOf(\n workspaces: readonly WorkspaceRoster[] | undefined,\n workspaceExtId: string | null | undefined,\n me: string | null | undefined\n): boolean {\n if (!workspaceExtId || !me || !workspaces) return false\n const workspace = workspaces.find((w) => w.external_id === workspaceExtId)\n return workspace?.users?.find((wu) => wu.user.external_id === me)?.role === 'guest'\n}\n\n/** True when I am a guest of the workspace a thread call is locked to (null: not a thread call). */\nexport function useMeetingReadOnly(lockedWorkspaceExtId: string | null | undefined): boolean {\n const arbi = useArbi()\n const me = arbi.isLoggedIn ? (arbi.user.identity()?.externalId ?? null) : null\n const { data: workspaces } = useWorkspaces({\n enabled: !!lockedWorkspaceExtId && arbi.isLoggedIn,\n })\n return isWorkspaceGuestOf(\n workspaces as readonly WorkspaceRoster[] | undefined,\n lockedWorkspaceExtId,\n me\n )\n}\n","/**\n * The call-UI gate. The call UI (call buttons, the incoming-call ringer, upcoming calls) shows only\n * when the server has calls enabled AND the host's `calls` feature is on (`useFeature('calls')`:\n * ARBI's Settings → Experimental toggle; a host that does not list it, on). The switch is an\n * experimental opt-in: on a deployment where calls are not experimental (`experimental: false`) the\n * call UI shows whatever it says. A call link opens whenever the server has calls enabled.\n */\n\nimport { useFeature } from '../host/features'\nimport { useIsEntitled } from '../account/entitlements'\nimport { useServerCallsEnabled, useServerCallsExperimental } from './hooks'\n\n/** The call UI shows: the server has calls AND (they are not experimental there OR the host's switch is on). */\nexport function useCallsEnabled(): boolean {\n const serverEnabled = useServerCallsEnabled()\n const experimental = useServerCallsExperimental()\n const switchedOn = useFeature('calls')\n return serverEnabled && (switchedOn || !experimental)\n}\n\n/**\n * Whether the person may HOST calls (start or schedule one, record one): the call UI is on and they\n * are entitled (Pro on the hosted service, an internal user on a Box). Joining a call someone else\n * hosts needs only `useCallsEnabled`.\n */\nexport function useCanHostCalls(): boolean {\n const callsEnabled = useCallsEnabled()\n const entitled = useIsEntitled('host_calls')\n return callsEnabled && entitled\n}\n","/**\n * \"Open meeting chat\" for every link that has one (a thread's call log card, a calendar event): a\n * known chat or meeting thread opens directly; otherwise the call's meeting chat is made on first\n * use. It opens where the host shows meeting chats (`calls.openMeetingChat`).\n */\n\nimport { useCallback } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { useArbi } from '../useArbi'\nimport { useArbiHost } from '../host/config'\nimport { notify } from '../notifications/notify-store'\nimport { resolveMeetingChat, type MeetingChatTarget } from './meeting-chat'\n\nexport function useOpenCallMeetingChat(): (target: MeetingChatTarget) => Promise<void> {\n const arbi = useArbi()\n const queryClient = useQueryClient()\n const open = useArbiHost().calls?.openMeetingChat\n return useCallback(\n async (target) => {\n try {\n const ref = await resolveMeetingChat(arbi, queryClient, target)\n open?.(ref)\n } catch (err) {\n console.warn('[calls] could not open the meeting chat', err)\n notify.error(\n `Could not open the meeting chat: ${err instanceof Error ? err.message : 'unknown error'}`\n )\n }\n },\n [arbi, queryClient, open]\n )\n}\n"]}
|