@ai-matrx/meet 0.11.51 → 0.11.53

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/core.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/ai.ts","../src/core/errors.ts","../src/core/connection-supervisor.ts","../src/core/leave.ts","../src/core/tokens.ts","../src/core/note-taker.ts","../src/core/transcript.ts","../src/core/channels.ts","../src/core/reasons.ts","../src/core/phase.ts","../src/core/types.ts","../src/core/calls.ts","../src/core/repository.ts","../src/core/app-panel.ts","../src/core/collab.ts","../src/core/names.ts","../src/core/recording.ts","../src/core/live-policy.ts","../src/core/attendance.ts","../src/core/store.ts","../src/core/room-events.ts","../src/core/devices.ts","../src/core/format.ts","../src/core/invitations.ts","../src/core/layout.ts","../src/core/capabilities.ts","../src/core/room-driver.ts","../src/core/network-health.ts","../src/core/livekit-driver.ts","../src/core/meetings.ts","../src/core/api.ts","../src/core/after.ts","../src/core/management.ts","../src/core/media-supervisor.ts","../src/core/room.ts","../src/core/effects.ts","../src/core/in-room.ts","../src/core/record.ts","../src/core/record-cache.ts","../src/core/reactions.ts","../src/core/collab-state.ts","../src/core/observation.ts","../src/core/speaking-detector.ts"],"sourcesContent":["/**\n * AI-NATIVE, ON BY DEFAULT — and SERVER-OWNED as of 0.3.0 (R4, D2, D16).\n *\n * 🚨 **THE PACKAGE NO LONGER KNOWS WHO ANSWERS.** Through 0.2.1 a host injected\n * `MeetAgents` — four database row ids — and this module ran the agent itself,\n * shipping the transcript up as `variables`. Both halves of that are gone:\n *\n * 1. **No agent id crosses this boundary, and none is injected.** Reasoning runs\n * behind Mandates whose Holder is resolved from the database at run time\n * (`meet.live_notes`, `meet.live_intelligence`, `meet.wrap_up`), so an\n * organization rebinds the job in the Mandate admin with no release of this\n * package and no change in the host. A host that had to supply an id was a\n * host that could supply the WRONG id, and a capability that silently\n * disappeared when nobody had filled a config line.\n * 2. **No transcript crosses this boundary either.** `POST /v1/meet/intelligence/ask`\n * takes `{meeting_id, question}` and NOTHING else; the answer is built\n * server-side from durable rows. Captions are hints and this client holds\n * only hints (D16) — sending them would be sending the model our guesses.\n *\n * What is left here is small and honest: ask a question and render the answer\n * as it streams, summon or dismiss the note-taker, end the meeting for\n * everyone, and report the server's refusal in the server's own words.\n *\n * 🚨 **THE USER-INPUT LAW still holds** and is now the server's to keep:\n * `question` is the one genuine human utterance in this module, and it is the\n * only free text this package sends anywhere near a model.\n */\n\nimport { readMatrxNdjsonStream } from \"@ai-matrx/agents\";\nimport type { MeetApi } from \"./api\";\nimport { MeetError, SERVER_FAILED_SENTENCE, isRawServerText } from \"./errors\";\nimport type { NoteTakerRefusal } from \"./note-taker\";\nimport type { MeetStore } from \"./store\";\nimport { MEET_ROUTES, MEETING_PASS_HEADER } from \"./tokens\";\nimport type { MeetingId, RoomName } from \"./types\";\n\nexport interface MeetAiOptions {\n /** The ONE org-aware aidream client (MRI-A16). Owns base url and headers. */\n api: MeetApi;\n store: MeetStore;\n /**\n * Resolves the current meeting's room pass for a guest Q&A request. The\n * provider owns this because it alone has both the active room and its\n * persisted guest pass. `null` leaves the header absent for signed-in record\n * Q&A, where the session remains the credential.\n */\n resolveRoomToken?: (() => string | null) | undefined;\n}\n\n/** What the answer was built from. The server measures it; we never guess it. */\nexport interface MeetAnswerCoverage {\n readonly segmentsUsed: number;\n readonly segmentsTotal: number;\n readonly truncated: boolean;\n readonly speakers: readonly string[];\n}\n\nexport interface MeetAiAnswer {\n readonly text: string;\n readonly coverage: MeetAnswerCoverage | null;\n}\n\nexport interface AskArgs {\n readonly meetingId: MeetingId;\n readonly question: string;\n readonly signal?: AbortSignal | undefined;\n /** Called with the answer so far, every time it grows. Rendering is live. */\n readonly onText?: ((text: string) => void) | undefined;\n /** The server's phase, verbatim, so a slow answer is never a bare spinner. */\n readonly onPhase?: ((phase: string) => void) | undefined;\n}\n\nexport interface NoteTakerDispatch {\n readonly dispatched: boolean;\n /** The server's sentence. Never empty on a refusal. */\n readonly detail: string;\n}\n\nexport interface MeetAi {\n /** In-meeting Q&A. The ONE human utterance this package sends (D16). */\n ask(args: AskArgs): Promise<MeetAiAnswer>;\n /** Ask aidream to dispatch the note-taker INTO the room. Host-only. */\n startNoteTaker(args: { meetingId: MeetingId; roomName: RoomName }): Promise<NoteTakerDispatch>;\n /**\n * Make sure the note-taker (the speech-to-text) is in the room because THIS viewer turned\n * captions on. Open to everyone in the meeting, guests included. Never throws: a refusal\n * is stored as `captionsRefusal` in the server's own words.\n */\n requestCaptions(args: { meetingId: MeetingId }): Promise<void>;\n /** Dismiss it. Nothing automatic brings a dismissed note-taker back (D9). */\n stopNoteTaker(args: { meetingId: MeetingId }): Promise<NoteTakerDispatch>;\n}\n\n/** The event vocabulary `/intelligence/ask` emits, pinned by the server's FEATURE.md. */\nexport const MEET_ANSWER_EVENTS = {\n chunk: \"chunk\",\n info: \"info\",\n phase: \"phase\",\n error: \"error\",\n end: \"end\",\n} as const;\n\n/** The `info` event carrying what the answer was built from. */\nexport const MEET_ANSWER_CONTEXT_EVENT = \"meet_intelligence_context\";\n\n/**\n * The coverage numbers, wherever the canonical emitter put them.\n *\n * 🚨 THE LIVE WIRE NESTS THEM UNDER `metadata`. `InfoPayload` is\n * `{code, system_message, user_message, metadata}`, so the numbers arrive at\n * `data.metadata.segments_used` — not at `data.segments_used`, which is what\n * this function read until it was walked against the real route on 2026-09-08\n * and quietly reported no coverage at all. Every shape is read now, and the\n * fixture in `ai.test.ts` is a VERBATIM line off the production stream.\n */\nfunction readCoverage(data: unknown): MeetAnswerCoverage | null {\n if (typeof data !== \"object\" || data === null) return null;\n const row = data as Record<string, unknown>;\n const candidates: Record<string, unknown>[] = [row];\n for (const key of [\"metadata\", \"data\"]) {\n const nested = row[key];\n if (typeof nested === \"object\" && nested !== null && !Array.isArray(nested)) {\n candidates.push(nested as Record<string, unknown>);\n }\n }\n const source =\n candidates.find(\n (entry) =>\n typeof entry[\"segments_used\"] === \"number\" ||\n typeof entry[\"segments_total\"] === \"number\",\n ) ?? null;\n if (source === null) return null;\n const used = source[\"segments_used\"];\n const total = source[\"segments_total\"];\n const speakers = source[\"speakers\"];\n return {\n segmentsUsed: typeof used === \"number\" ? used : 0,\n segmentsTotal: typeof total === \"number\" ? total : 0,\n truncated: source[\"transcript_truncated\"] === true,\n speakers: Array.isArray(speakers)\n ? speakers.filter((entry): entry is string => typeof entry === \"string\")\n : [],\n };\n}\n\nfunction readText(data: unknown): string {\n if (typeof data === \"string\") return data;\n if (typeof data !== \"object\" || data === null) return \"\";\n const row = data as Record<string, unknown>;\n // `phase` and `system_message` are on this list because the live emitter\n // sends `{\"phase\":\"generating\"}` and `{\"system_message\":\"Reading the meeting\n // transcript\"}`. Without them a phase rendered as \"[object Object]\" —\n // found by walking the real route, not by any test written from the docs.\n for (const key of [\"text\", \"content\", \"delta\", \"t\", \"phase\", \"system_message\"]) {\n const value = row[key];\n if (typeof value === \"string\") return value;\n }\n return \"\";\n}\n\nexport { SERVER_FAILED_SENTENCE, isRawServerText } from \"./errors\";\n\n/**\n * THE ONE REFUSAL → MeetError (0.7.8). A non-OK aidream response becomes an\n * error whose `message` is the server's sentence for a PERSON and whose\n * `remedy` is the server's remedy (or the caller's fallback) — never the\n * operation name, a status code alone, or an exception class. Every door that\n * calls aidream builds its refusal here, so no door can drift back.\n */\nexport async function refusalFromResponse(\n response: Response,\n operation: string,\n fallbackRemedy: string,\n): Promise<MeetError> {\n let body: unknown = null;\n try {\n body = await response.json();\n } catch {\n body = null;\n }\n const said = readServerRefusal(body);\n const code =\n response.status === 403 || response.status === 401\n ? \"forbidden\"\n : response.status === 404\n ? \"not-found\"\n : response.status === 409\n ? \"conflict\"\n : \"transport\";\n const error = new MeetError(\n code,\n said?.reason ?? SERVER_FAILED_SENTENCE,\n said !== null && said.remedy.length > 0 ? said.remedy : fallbackRemedy,\n );\n (error as { operation?: string; status?: number }).operation = operation;\n (error as { operation?: string; status?: number }).status = response.status;\n return error;\n}\n\ninterface ServerRefusal {\n readonly reason: string;\n readonly remedy: string;\n}\n\n/**\n * THE SERVER'S REFUSAL, READ RATHER THAN RE-WRITTEN.\n *\n * aidream answers a refused dispatch with a body carrying its own `reason` and\n * `remedy` — \"no worker is registered under matrx-note-taker\", and what to do\n * about it. FastAPI nests a raised `HTTPException` under `detail`, and a\n * validation failure produces a list. All three shapes are read here so the\n * screen shows the system's own words instead of \"something went wrong\".\n */\nexport function readServerRefusal(body: unknown): ServerRefusal | null {\n if (typeof body === \"string\") {\n return body.trim().length === 0 ? null : { reason: body.trim(), remedy: \"\" };\n }\n if (typeof body !== \"object\" || body === null) return null;\n const row = body as Record<string, unknown>;\n const detail = row[\"detail\"];\n if (Array.isArray(detail)) {\n // FastAPI's validation shape: `{msg, loc}`. The FIELD is in `loc` and the\n // field is the whole point — \"Field required\" alone names nothing.\n const messages = detail\n .map((entry) => {\n if (typeof entry !== \"object\" || entry === null) return String(entry);\n const row = entry as Record<string, unknown>;\n const message = String(row[\"msg\"] ?? \"\");\n const where = Array.isArray(row[\"loc\"])\n ? row[\"loc\"].filter((part): part is string => typeof part === \"string\").join(\".\")\n : \"\";\n return where.length > 0 ? `${message} (${where})` : message;\n })\n .filter((entry) => entry.length > 0);\n return messages.length === 0 ? null : { reason: messages.join(\"; \"), remedy: \"\" };\n }\n if (typeof detail === \"object\" && detail !== null) return readServerRefusal(detail);\n // 🚨 NEVER A CLASS NAME. aidream's crash body is `message: \"Internal server\n // error: UnsupportedClientFeatureError\"` beside `user_message` for a person;\n // through 0.7.7 this read `message` whatever it said, so a crash reached a\n // host's toast as a Python class name (verifier, 2026-09-27). A raw message\n // now falls through to `user_message`. A HUMAN `message` still wins: Meet's\n // own sentences carry the specific reason and its \"Fix:\", which a generic\n // `user_message` wrapper would bury.\n const humanOrNull = (value: unknown): string | null =>\n typeof value === \"string\" && value.trim().length > 0 && !isRawServerText(value)\n ? value.trim()\n : null;\n const reason =\n humanOrNull(row[\"reason\"]) ??\n humanOrNull(row[\"message\"]) ??\n humanOrNull(row[\"user_message\"]) ??\n (typeof detail === \"string\" ? humanOrNull(detail) : null) ??\n (typeof row[\"message\"] === \"string\" ||\n typeof row[\"error\"] === \"string\" ||\n typeof detail === \"string\"\n ? SERVER_FAILED_SENTENCE\n : null);\n if (reason === null) return null;\n const remedy = typeof row[\"remedy\"] === \"string\" ? row[\"remedy\"] : \"\";\n if (remedy.length > 0) return { reason, remedy };\n // THE SERVER WRITES ITS REMEDY INLINE. Meet's own errors read\n // \"<what happened>. Fix: <what to do>.\" — splitting it here is what puts the\n // fix on its own line in the panel instead of burying it mid-paragraph.\n const fix = reason.indexOf(\"Fix:\");\n if (fix > 0) {\n return {\n reason: reason.slice(0, fix).trim(),\n remedy: reason.slice(fix + \"Fix:\".length).trim(),\n };\n }\n return { reason, remedy };\n}\n\nexport function createMeetAi(options: MeetAiOptions): MeetAi {\n /**\n * Every request here goes through the ONE org-aware client (MRI-A16), which\n * puts `x-organization-id` on it — the 0.1.3 lesson, now structural rather\n * than remembered. A refused session is translated into THIS surface's own\n * sentence, because \"sign in\" means something specific for the note-taker.\n */\n async function send(input: {\n path: string;\n body: Record<string, unknown>;\n operation: string;\n requireAuth: boolean;\n headers?: Record<string, string> | undefined;\n signal?: AbortSignal | undefined;\n }): Promise<Response> {\n try {\n return await options.api.request({\n path: input.path,\n method: \"POST\",\n requireAuth: input.requireAuth,\n operation: input.operation,\n body: input.body,\n ...(input.headers !== undefined ? { headers: input.headers } : {}),\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n });\n } catch (error) {\n if ((error as { code?: string }).code === \"session-unavailable\") {\n throw new MeetError(\n \"forbidden\",\n `${input.operation}: this action needs a signed-in host`,\n \"A guest cannot summon or dismiss the note-taker, and the control is hidden for \" +\n \"them. Sign in as the meeting's host to use it.\",\n );\n }\n throw error;\n }\n }\n\n async function refusalFor(response: Response, operation: string): Promise<NoteTakerRefusal> {\n let body: unknown = null;\n try {\n body = await response.json();\n } catch {\n try {\n body = await response.text();\n } catch {\n body = null;\n }\n }\n const parsed = readServerRefusal(body);\n // 🚨 A NAMED SERVER GAP, ANNOUNCED RATHER THAN PAPERED OVER. 0.3.0 stopped\n // sending `agent_id` because which agent transcribes is the server's\n // business (D2), but aidream's `NoteTakerStartRequest` still DECLARES that\n // field, so a server that has not landed the MRI-A2 side answers FastAPI's\n // validation error — which reads like a client bug to the person clicking.\n // This translates it into what is actually true. It never invents an id.\n if (response.status === 422 && (parsed?.reason ?? \"\").includes(\"agent_id\")) {\n return {\n status: 422,\n reason:\n \"This server still expects the caller to name the note-taker agent, and this \" +\n \"version of @ai-matrx/meet deliberately does not.\",\n remedy:\n \"The note-taker's identity is a Mandate binding the server resolves (D2). Update \" +\n \"aidream so `/v1/meet/agent/start` resolves the agent itself — register item \" +\n \"MRI-A2/A6. Until then the note-taker cannot be summoned from this surface, and \" +\n \"nothing about this meeting is being transcribed.\",\n };\n }\n return {\n status: response.status,\n reason:\n parsed?.reason ??\n `${operation}: the server answered ${response.status} with no reason.`,\n remedy:\n parsed?.remedy !== undefined && parsed.remedy.length > 0\n ? parsed.remedy\n : \"The server refused and said nothing more. The meeting continues WITHOUT the \" +\n \"note-taker and this surface says so — a silent failure here is a meeting \" +\n \"everyone believes is being minuted.\",\n };\n }\n\n async function dispatch(\n route: string,\n body: Record<string, unknown>,\n operation: string,\n ): Promise<NoteTakerDispatch> {\n // The session check happens BEFORE the request, so a guest's refusal is a\n // package sentence rather than a round trip the server would reject anyway.\n const response = await send({ path: route, body, operation, requireAuth: true });\n if (!response.ok) {\n const refusal = await refusalFor(response, operation);\n // THE REFUSAL IS STATE, not only an exception: every surface renders the\n // server's sentence until the next attempt, rather than a control that\n // looks like it did nothing.\n options.store.patch({ noteTakerRefusal: refusal });\n throw new MeetError(\n response.status === 403 ? \"forbidden\" : \"transport\",\n refusal.reason,\n refusal.remedy,\n );\n }\n options.store.patch({ noteTakerRefusal: null });\n let payload: unknown = null;\n try {\n payload = await response.json();\n } catch {\n payload = null;\n }\n const row =\n typeof payload === \"object\" && payload !== null\n ? (payload as Record<string, unknown>)\n : {};\n return {\n dispatched: row[\"dispatched\"] === true,\n detail: typeof row[\"detail\"] === \"string\" ? row[\"detail\"] : \"\",\n };\n }\n\n return {\n async ask({ meetingId, question, signal, onText, onPhase }) {\n const trimmed = question.trim();\n if (trimmed.length === 0) {\n throw new MeetError(\n \"misconfigured\",\n \"meet.ai.ask: an empty question\",\n \"Type a question. Nothing is sent until there is one.\",\n );\n }\n const roomToken = options.resolveRoomToken?.() ?? null;\n const response = await send({\n path: MEET_ROUTES.intelligenceAsk,\n // THE WHOLE BODY. No transcript, no context, no agent id (D16).\n body: { meeting_id: meetingId, question: trimmed },\n operation: \"meet.ai.ask\",\n // A GUEST MAY ASK. The server decides whether it can answer.\n requireAuth: false,\n ...(roomToken !== null ? { headers: { [MEETING_PASS_HEADER]: roomToken } } : {}),\n ...(signal !== undefined ? { signal } : {}),\n });\n if (!response.ok) {\n // Both refusals happen BEFORE the stream opens, by the server's design,\n // so this is an honest status rather than a stream that dies mid-answer.\n const refusal = await refusalFor(response, \"meet.ai.ask\");\n throw new MeetError(\n response.status === 403 || response.status === 401\n ? \"forbidden\"\n : response.status === 404\n ? \"not-found\"\n : \"transport\",\n refusal.reason,\n refusal.remedy,\n );\n }\n const body = response.body;\n if (body === null || body === undefined) {\n throw new MeetError(\n \"invalid-response\",\n \"meet.ai.ask: the answer arrived with no stream to read\",\n \"The route streams NDJSON. A response with no body means a proxy buffered or \" +\n \"stripped it — check for a gateway that does not pass streaming responses.\",\n );\n }\n let text = \"\";\n let coverage: MeetAnswerCoverage | null = null;\n let failure: ServerRefusal | null = null;\n for await (const envelope of readMatrxNdjsonStream(\n body,\n signal !== undefined ? { signal } : {},\n )) {\n if (envelope.event === MEET_ANSWER_EVENTS.chunk) {\n const piece = readText(envelope.data);\n if (piece.length > 0) {\n // THE ANSWER IS THE CONCATENATED CHUNKS — the server deliberately\n // sends no second copy in a terminal payload, so this accumulation\n // IS the answer, not an optimization.\n text += piece;\n onText?.(text);\n }\n continue;\n }\n if (envelope.event === MEET_ANSWER_EVENTS.info) {\n const parsed = readCoverage(envelope.data);\n if (parsed !== null) coverage = parsed;\n continue;\n }\n if (envelope.event === MEET_ANSWER_EVENTS.phase) {\n const phase = readText(envelope.data);\n onPhase?.(phase.length > 0 ? phase : String(envelope.data));\n continue;\n }\n if (envelope.event === MEET_ANSWER_EVENTS.error) {\n failure = readServerRefusal(envelope.data);\n }\n }\n if (failure !== null) {\n throw new MeetError(\n \"transport\",\n failure.reason,\n failure.remedy.length > 0\n ? failure.remedy\n : \"The answer stopped mid-stream and the server said why. Ask again once the \" +\n \"named cause is cleared.\",\n );\n }\n if (text.trim().length === 0) {\n throw new MeetError(\n \"invalid-response\",\n \"meet.ai.ask: the stream ended with no answer in it\",\n \"Nothing was returned to show. Check the meeting has a Holder bound to \" +\n \"`meet.live_intelligence` at /mandates, then ask again.\",\n );\n }\n return { text: text.trim(), coverage };\n },\n\n startNoteTaker: ({ meetingId, roomName }) =>\n dispatch(\n MEET_ROUTES.noteTakerStart,\n {\n // NO agent_id. Which agent transcribes is the server's business\n // (D2) — this package could not name one if it wanted to.\n meeting_id: meetingId,\n room_name: roomName,\n organization_id: options.api.organizationId,\n },\n \"meet.ai.startNoteTaker\",\n ),\n\n requestCaptions: async ({ meetingId }) => {\n const roomToken = options.resolveRoomToken?.() ?? null;\n const operation = \"meet.ai.requestCaptions\";\n try {\n const response = await send({\n path: MEET_ROUTES.noteTakerCaptions,\n body: { meeting_id: meetingId },\n operation,\n requireAuth: roomToken === null,\n ...(roomToken !== null ? { headers: { [MEETING_PASS_HEADER]: roomToken } } : {}),\n });\n if (!response.ok) {\n options.store.patch({ captionsRefusal: await refusalFor(response, operation) });\n return;\n }\n let payload: unknown = null;\n try {\n payload = await response.json();\n } catch {\n payload = null;\n }\n const row =\n typeof payload === \"object\" && payload !== null ? (payload as Record<string, unknown>) : {};\n const detail = typeof row[\"detail\"] === \"string\" ? row[\"detail\"] : \"\";\n // `dispatched: false` is also \"already in the room\"; the hook decides from the\n // note-taker's real state whether this sentence matters.\n options.store.patch({\n captionsRefusal: row[\"dispatched\"] === true ? null : { status: 200, reason: detail, remedy: \"\" },\n });\n } catch (error) {\n options.store.patch({\n captionsRefusal: {\n status: 0,\n reason: (error as Error).message,\n remedy: (error as { remedy?: string }).remedy ?? \"\",\n },\n });\n }\n },\n\n stopNoteTaker: ({ meetingId }) =>\n dispatch(\n MEET_ROUTES.noteTakerStop,\n { meeting_id: meetingId },\n \"meet.ai.stopNoteTaker\",\n ),\n };\n}\n","/**\n * ONE error classification for the whole package (C22).\n *\n * The banned alternative is a host catching a LiveKit or PostgREST error and\n * deciding for itself whether the camera is busy, the token expired, or the\n * meeting is locked — which is how the same five-branch `if` gets copy-pasted\n * into every consumer and drifts. Everything this package throws is a\n * `MeetError` with a stable `code` and a `remedy` SENTENCE. Nothing fails\n * silently, and nothing fails without saying what to do about it.\n *\n * The device codes exist because \"it doesn't work\" in a video product is almost\n * always one of four specific, differently-remediable things: permission\n * denied, no such device, device already in use by another app, or insecure\n * context (http, where `getUserMedia` does not exist at all). A single\n * \"media-error\" would make the UI say the wrong sentence three times out of\n * four.\n */\n\nexport type MeetErrorCode =\n | \"session-unavailable\"\n | \"forbidden\"\n | \"not-found\"\n | \"conflict\"\n | \"token\"\n | \"transport\"\n | \"invalid-response\"\n | \"misconfigured\"\n | \"permission-denied\"\n | \"device-not-found\"\n | \"device-in-use\"\n | \"insecure-context\"\n | \"media-unsupported\"\n | \"room-locked\"\n | \"lobby-denied\"\n | \"capacity\"\n /** Meet wave 1: the meeting was cancelled — its link resolves, its room never opens. */\n | \"cancelled\"\n | \"unknown\";\n\nexport class MeetError extends Error {\n readonly code: MeetErrorCode;\n /** What to DO. Rendered to the user for the device codes. */\n readonly remedy: string;\n override readonly cause?: unknown;\n /**\n * The join gate's reason code (CORE-DESIGN §2.5) when the server named one: `not_found`,\n * `cancelled`, `ended`, `locked`, `removed`, `lobby_rejects`, `not_invited`… The phase\n * renders `refused:<reason>` (or `ended`) from it — never from the HTTP status.\n */\n readonly reason: string | null;\n /**\n * A refusal that lifts by itself says when: whole seconds from the moment the server answered\n * (a denial's cooldown, `reknock_cooldown_seconds`). Null for every refusal that does not.\n */\n readonly retryAfterSeconds: number | null;\n\n constructor(\n code: MeetErrorCode,\n message: string,\n remedy: string,\n cause?: unknown,\n reason?: string | null,\n retryAfterSeconds?: number | null,\n ) {\n super(message);\n this.name = \"MeetError\";\n this.code = code;\n this.remedy = remedy;\n this.reason = reason ?? null;\n this.retryAfterSeconds = retryAfterSeconds ?? null;\n if (cause !== undefined) this.cause = cause;\n }\n\n /** True when retrying after the host re-establishes a session is the fix. */\n get isRetryable(): boolean {\n return (\n this.code === \"session-unavailable\" ||\n this.code === \"transport\" ||\n this.code === \"token\"\n );\n }\n\n /** True when the user, not the code, must act — the UI shows `remedy`. */\n get isUserActionable(): boolean {\n return (\n this.code === \"permission-denied\" ||\n this.code === \"device-not-found\" ||\n this.code === \"device-in-use\" ||\n this.code === \"insecure-context\"\n );\n }\n}\n\n/** What a person reads when the server failed without a sentence of its own. */\nexport const SERVER_FAILED_SENTENCE =\n \"The server hit an unexpected problem and nothing was changed.\";\n\n/**\n * A developer's text, not a person's: `Internal server error: SomeError`, a\n * bare `SomeError` class name, or a machine code like `internal_error`.\n */\nexport function isRawServerText(value: string): boolean {\n const text = value.trim();\n return (\n /^internal server error\\b/i.test(text) ||\n /^[A-Z][A-Za-z0-9]*(Error|Exception)$/.test(text) ||\n /^[a-z][a-z0-9]*(_[a-z0-9]+)+$/.test(text)\n );\n}\n\n/**\n * A database message a PERSON can read: our RPCs raise plain sentences (\"Only\n * the host can remove a guest.\"); Postgres's own errors (\"permission denied\n * for table …\", \"violates foreign key …\") and class names are not.\n */\nfunction humanDatabaseSentence(message: string): string | null {\n const text = message.trim();\n if (text.length === 0 || isRawServerText(text)) return null;\n if (\n /permission denied for|violates|relation \"|does not exist|syntax error|column \"|jwt|pgrst|duplicate key|null value in/i.test(\n text,\n )\n )\n return null;\n return text;\n}\n\ninterface ErrorLike {\n name?: string | undefined;\n message?: string | undefined;\n code?: string | undefined;\n}\n\nconst FORBIDDEN_CODES = new Set([\"42501\", \"PGRST301\", \"PGRST302\"]);\n// P0002 is how `platform.refuse_not_found` answers through PostgREST (\"no meeting for that\n// link\"): the one honest not-found, and the reason the join gate's `not_found` screen needs.\nconst NOT_FOUND_CODES = new Set([\"PGRST116\", \"PGRST205\", \"P0002\"]);\n// 40001: the row changed since the caller read it (meet_update_meeting's version\n// compare-and-swap). 55000: the object is in a state that refuses the change (a\n// cancelled meeting). Both are conflicts a person resolves by reloading.\nconst CONFLICT_CODES = new Set([\"23505\", \"40001\", \"55000\"]);\n\nconst SESSION_MARKERS = [\n \"auth session missing\",\n \"jwt expired\",\n \"no api key found\",\n \"refresh_token_not_found\",\n \"invalid claim: missing sub claim\",\n];\n\n/**\n * THE ONE PLACE a raw database/transport error becomes a typed one. Called at\n * every repository boundary; nothing else in the package inspects a raw error,\n * and no consumer should ever have to.\n */\nexport function normalizeMeetError(error: unknown, operation: string): MeetError {\n if (error instanceof MeetError) return error;\n\n const raw = (error ?? {}) as ErrorLike;\n const message = typeof raw.message === \"string\" ? raw.message : String(error);\n const code = typeof raw.code === \"string\" ? raw.code : undefined;\n const lowered = message.toLowerCase();\n\n if (raw.name === \"SessionUnavailableError\" || SESSION_MARKERS.some((m) => lowered.includes(m))) {\n return new MeetError(\n \"session-unavailable\",\n `${operation}: no Supabase session available (${message})`,\n \"Normal during sign-in and token refresh. The package retries once after the host's \" +\n \"session source resolves; log it as a warning, never as an error.\",\n error,\n );\n }\n if (code !== undefined && FORBIDDEN_CODES.has(code)) {\n // A PERSON reads `message` and `remedy` (toasts, panels); the database's\n // own words stay on `cause` for the developer. Through 0.7.8 this read\n // \"meet.x: denied by the database (42501: …)\" and a remedy about RLS.\n return new MeetError(\n \"forbidden\",\n humanDatabaseSentence(message) ?? \"You don't have access to do that in this meeting.\",\n \"Ask the meeting's host to do it, or to give you access.\",\n error,\n );\n }\n if (code !== undefined && NOT_FOUND_CODES.has(code)) {\n return new MeetError(\n \"not-found\",\n `${operation}: not found (${code}: ${message})`,\n \"The row is gone, or RLS hides it. If this is PGRST205 the communication.meet_* tables \" +\n \"are not deployed yet — see MEET_SCHEMA in repository.ts for the exact contract.\",\n error,\n \"not_found\",\n );\n }\n if (code !== undefined && CONFLICT_CODES.has(code)) {\n return new MeetError(\n \"conflict\",\n humanDatabaseSentence(message) ?? \"This meeting changed a moment ago, so nothing was saved.\",\n \"Reload to see the latest, then try again.\",\n error,\n );\n }\n const hint = (error as { hint?: unknown } | null)?.hint;\n return new MeetError(\n \"transport\",\n /failed to fetch|networkerror|load failed|network request failed/i.test(message)\n ? \"The server could not be reached.\"\n : (humanDatabaseSentence(message) ?? SERVER_FAILED_SENTENCE),\n typeof hint === \"string\" && humanDatabaseSentence(hint) !== null\n ? hint.trim()\n : \"Nothing was changed — retry in a moment; if it keeps happening, reload the page.\",\n error,\n );\n}\n\n/**\n * THE DEVICE ERROR TRANSLATOR — the second-most-copied block in every video app.\n *\n * `getUserMedia` rejects with DOMException names, and every one of them means\n * something a user could fix if we told them. This is the whole reason the\n * device layer lives in the package (C22 test 3: less-than-perfect unless done\n * perfectly).\n */\nexport function normalizeDeviceError(error: unknown, operation: string): MeetError {\n if (error instanceof MeetError) return error;\n const raw = (error ?? {}) as ErrorLike;\n const name = raw.name ?? \"\";\n const message = typeof raw.message === \"string\" ? raw.message : String(error);\n\n switch (name) {\n case \"NotAllowedError\":\n case \"SecurityError\":\n return new MeetError(\n \"permission-denied\",\n `${operation}: camera/microphone permission denied`,\n \"Allow camera and microphone access for this site, then rejoin. On macOS also check \" +\n \"System Settings › Privacy & Security — a browser denied there shows the same error.\",\n error,\n );\n case \"NotFoundError\":\n case \"OverconstrainedError\":\n return new MeetError(\n \"device-not-found\",\n `${operation}: no matching camera/microphone (${message})`,\n \"The selected device is gone — usually an unplugged headset. Pick another device in \" +\n \"the pre-join panel; the package falls back to the system default automatically.\",\n error,\n );\n case \"NotReadableError\":\n case \"AbortError\":\n return new MeetError(\n \"device-in-use\",\n `${operation}: the device is already in use (${message})`,\n \"Another application (or another tab) holds the camera. Close it and try again — the \" +\n \"hardware, not the meeting, is what refused.\",\n error,\n );\n case \"TypeError\":\n return new MeetError(\n \"insecure-context\",\n `${operation}: media devices are unavailable in this context (${message})`,\n \"getUserMedia only exists on https or localhost. Serve the app over https.\",\n error,\n );\n default:\n return new MeetError(\n \"media-unsupported\",\n `${operation}: ${message}`,\n \"This browser refused the media request for a reason the package does not recognize. \" +\n \"Report it with this message so the class gets a named remedy rather than a shrug.\",\n error,\n );\n }\n}\n\n/**\n * SCREEN SHARE IS NOT A CAMERA. `getDisplayMedia` rejects with `NotAllowedError`\n * when the person simply closes the picker, and that is a CHOICE, not a\n * permission problem — so it must never wear the camera/microphone sentence\n * `normalizeDeviceError` gives that same error name.\n *\n * WHAT BROWSERS ACTUALLY REPORT (the reason there are three outcomes, not two):\n * - Chrome / Edge: a closed picker is `NotAllowedError` \"Permission denied\"; an\n * OS-level refusal (macOS Screen Recording not granted) is `NotAllowedError`\n * \"Permission denied by system\"; a permissions-policy / enterprise block is\n * `NotAllowedError` naming the policy or `display-capture`. The MESSAGE is the\n * only difference, so it is what we read.\n * - Firefox and Safari: a closed picker, a denied OS permission and a policy\n * block are ALL `NotAllowedError` with the same generic message (\"The request\n * is not allowed by the user agent or the platform…\"). They CANNOT be told\n * apart. That is the `ambiguous` outcome: the first one stays silent (a person\n * who closed the picker wants no scolding), and the SECOND one in a row shows\n * the notice, so nobody is left clicking a dead button with no explanation.\n */\nexport type ScreenShareDenial = \"blocked\" | \"ambiguous\" | \"other\";\n\nconst SYSTEM_BLOCK = /by system|permissions?[- ]policy|display-capture|disallowed|not allowed by the system/i;\n\nexport function classifyScreenShareError(error: unknown): ScreenShareDenial {\n const read = (value: unknown, key: \"name\" | \"message\"): string => {\n const v = (value as ErrorLike | null | undefined)?.[key];\n return typeof v === \"string\" ? v : \"\";\n };\n const cause = error instanceof MeetError ? error.cause : undefined;\n const denied = [error, cause].some((value) => {\n const name = read(value, \"name\");\n return name === \"NotAllowedError\" || name === \"AbortError\" || name === \"SecurityError\";\n });\n if (denied) {\n const text = [read(error, \"message\"), read(cause, \"message\")].join(\" \");\n return SYSTEM_BLOCK.test(text) || [error, cause].some((v) => read(v, \"name\") === \"SecurityError\")\n ? \"blocked\"\n : \"ambiguous\";\n }\n // A MeetError we made ourselves for a plain denial (no browser error inside).\n if (error instanceof MeetError && error.code === \"permission-denied\" && cause === undefined) {\n return \"ambiguous\";\n }\n return \"other\";\n}\n\n/** True only for a denial that MAY be the person closing the picker. */\nexport function isScreenShareCancel(error: unknown): boolean {\n return classifyScreenShareError(error) === \"ambiguous\";\n}\n\nexport const SCREEN_SHARE_BLOCKED_REMEDY =\n \"Your browser or computer is blocking screen sharing. On a Mac, open System Settings > \" +\n \"Privacy & Security > Screen Recording, turn it on for your browser, then restart the \" +\n \"browser. On a work computer, ask your administrator. You are still in the call.\";\n\nexport function normalizeScreenShareError(error: unknown, operation: string): MeetError {\n if (error instanceof MeetError) return error;\n const raw = (error ?? {}) as ErrorLike;\n const message = typeof raw.message === \"string\" ? raw.message : String(error);\n const kind = classifyScreenShareError(error);\n if (kind === \"blocked\") {\n return new MeetError(\n \"permission-denied\",\n `${operation}: screen sharing is blocked (${message})`,\n SCREEN_SHARE_BLOCKED_REMEDY,\n error,\n );\n }\n if (kind === \"ambiguous\") {\n return new MeetError(\n \"permission-denied\",\n `${operation}: screen sharing was cancelled or refused`,\n \"Choose a screen, window or tab in the picker to share it. If no picker appeared, \" +\n SCREEN_SHARE_BLOCKED_REMEDY,\n error,\n );\n }\n return new MeetError(\n \"media-unsupported\",\n `${operation}: screen sharing is not available (${message})`,\n \"This browser or device could not share a screen. You are still in the call; try a \" +\n \"desktop browser to present.\",\n error,\n );\n}\n\n/** A response whose SHAPE is wrong — the ingress boundary, not the wire. */\nexport function invalidResponse(operation: string, detail: string): MeetError {\n return new MeetError(\n \"invalid-response\",\n `${operation}: ${detail}`,\n \"The server returned a shape this package does not accept. Rendering it would put a lie \" +\n \"on the screen, so it is refused here. Fix the endpoint's return contract.\",\n );\n}\n","/**\n * CS — THE CONNECTION SUPERVISOR (CORE-DESIGN §3.3, step S6b).\n *\n * The one owner of \"is this tab still connected, and if not, why, and what happens next\". It\n * never touches a browser global itself — the network signal and the reachability probe arrive\n * as ports (`NetworkPort`, `probe`), with web implementations below for the provider to hand in —\n * and it never moves the phase except through `store.dispatch` (via the engine's `autoRejoin`).\n *\n * What it decides:\n *\n * 1. **The `connection` region** (§3.2): `stable · signal_reconnecting · reconnecting · offline ·\n * server_unreachable`. `offline` = the device has no network (`navigator.onLine` false);\n * `server_unreachable` = online, but our API or the media server does not answer the probe.\n * The two get different screens because they have different fixes.\n * 2. **The banner debounce** (`reconnect_banner_delay_ms`): the region is honest at once (the\n * contract attribute), the banner appears only if the blip outlasts the delay (`net-blip`).\n * 3. **Auto-rejoin after the SDK gives up** (`auto_rejoin`, `auto_rejoin_max_attempts`): from\n * `disconnected`, when the network is back AND the probe answers, rejoin with a fresh mint into\n * the same identity — the server restores role / hand / admission from the person row. Attempts\n * are only spent while reachable; a long offline spell costs none. Manual Rejoin always works.\n * 4. **The reload-after-offline marker**: while in the call (or dropped from it) the tab keeps a\n * per-tab marker, so a page that Chrome replaced with its own offline page and then reloaded\n * comes back INTO the call instead of to the pre-join (`resume`). A deliberate exit clears it.\n * 5. **The jittered reconnect policy** handed to LiveKit (`createReconnectPolicy`): jitter from\n * attempt 0 (the SDK's default has none on its first two attempts — 650 people reconnecting\n * on the same millisecond is the thundering-herd issue), with the give-up limits as options.\n *\n * Others' drops (`net-others-see-drop`) are the pure ghost helpers at the bottom: an unannounced\n * departure keeps a `reconnecting` tile for `reconnect_grace_seconds`.\n */\n\nimport type { DriverConnectionState, DriverParticipant } from \"./room-driver\";\nimport type { JoinRequest } from \"./room\";\nimport type { ConnectionRegion, MeetStore } from \"./store\";\nimport type { MeetPhase } from \"./phase\";\n\n/* ------------------------------------------------------------------ policy (knobs) */\n\n/** The server-delivered connection knobs (CORE-DESIGN §4.2/§4.3), resolved per meeting. */\nexport interface ConnectionPolicy {\n /** `reconnect_banner_delay_ms`: how long a drop must last before the banner shows. */\n readonly bannerDelayMs: number;\n /** `auto_rejoin`: rejoin by itself after the SDK gives up. */\n readonly autoRejoin: boolean;\n /** `auto_rejoin_max_attempts`: rejoin attempts (while reachable) before only Rejoin is left. */\n readonly autoRejoinMaxAttempts: number;\n /** `reconnect_grace_seconds`: how long others keep a dropped person's `reconnecting` tile. */\n readonly reconnectGraceSeconds: number;\n /**\n * `video_auto_off_after_poor_seconds`: a poor path that lasts this long pauses the person's\n * camera so their audio survives (`net-video-auto-off`); 0 = never.\n */\n readonly videoAutoOffAfterPoorSeconds: number;\n /**\n * `captions_start_timeout_seconds`: how long \"Starting captions\" may last before the person is\n * told, honestly, that captions are unavailable (and offered Try again).\n */\n readonly captionsStartTimeoutSeconds: number;\n}\n\n/** The knob defaults (§4.3) — what an older server that sends nothing gets. */\nexport const DEFAULT_CONNECTION_POLICY: ConnectionPolicy = {\n bannerDelayMs: 5000,\n autoRejoin: true,\n autoRejoinMaxAttempts: 5,\n reconnectGraceSeconds: 30,\n videoAutoOffAfterPoorSeconds: 20,\n captionsStartTimeoutSeconds: 20,\n};\n\nfunction clampInt(value: unknown, min: number, max: number, fallback: number): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return fallback;\n return Math.min(max, Math.max(min, Math.round(value)));\n}\n\n/** The token's `connection_policy` object → the typed policy; anything missing is the default. */\nexport function connectionPolicyFrom(raw: unknown): ConnectionPolicy {\n if (typeof raw !== \"object\" || raw === null) return DEFAULT_CONNECTION_POLICY;\n const row = raw as Record<string, unknown>;\n const d = DEFAULT_CONNECTION_POLICY;\n return {\n bannerDelayMs: clampInt(row[\"reconnect_banner_delay_ms\"], 0, 10_000, d.bannerDelayMs),\n autoRejoin: typeof row[\"auto_rejoin\"] === \"boolean\" ? row[\"auto_rejoin\"] : d.autoRejoin,\n autoRejoinMaxAttempts: clampInt(row[\"auto_rejoin_max_attempts\"], 0, 20, d.autoRejoinMaxAttempts),\n reconnectGraceSeconds: clampInt(row[\"reconnect_grace_seconds\"], 0, 600, d.reconnectGraceSeconds),\n videoAutoOffAfterPoorSeconds: clampInt(row[\"video_auto_off_after_poor_seconds\"], 0, 600, d.videoAutoOffAfterPoorSeconds),\n captionsStartTimeoutSeconds: clampInt(row[\"captions_start_timeout_seconds\"], 1, 600, d.captionsStartTimeoutSeconds),\n };\n}\n\n/* ------------------------------------------------------------------ LiveKit policy */\n\n/** LiveKit's `ReconnectPolicy` shape, restated so this module never imports the SDK. */\nexport interface ReconnectPolicyLike {\n nextRetryDelayInMs(context: { readonly retryCount: number; readonly elapsedMs: number }): number | null;\n}\n\nexport interface ReconnectPolicyOptions {\n /** Base delay per attempt (ms); the last one repeats. Default: LiveKit's own schedule. */\n readonly delaysMs?: readonly number[];\n /** Stop after this long since the drop (ms). Default 45 000 — about the SDK's own ~44 s. */\n readonly giveUpMs?: number;\n /** Stop after this many attempts. Default 12. */\n readonly maxAttempts?: number;\n readonly random?: () => number;\n}\n\n/** livekit-client 2.22 `DEFAULT_RETRY_DELAYS_IN_MS` (verified in the SDK source, 2026-10-08). */\nexport const LIVEKIT_BASE_DELAYS_MS: readonly number[] = [0, 300, 1200, 2700, 4800, 7000];\n\n/**\n * THE JITTERED RECONNECT POLICY. Every attempt — attempt 0 included — gets a random spread of at\n * least 500 ms (half the base delay, capped at 2 s), so a room whose SFU blinks does not hear\n * every client come back on the same millisecond.\n */\nexport function createReconnectPolicy(options: ReconnectPolicyOptions = {}): ReconnectPolicyLike {\n const delays = options.delaysMs !== undefined && options.delaysMs.length > 0 ? options.delaysMs : LIVEKIT_BASE_DELAYS_MS;\n const giveUpMs = options.giveUpMs ?? 45_000;\n const maxAttempts = options.maxAttempts ?? 12;\n const random = options.random ?? Math.random;\n return {\n nextRetryDelayInMs({ retryCount, elapsedMs }) {\n if (retryCount >= maxAttempts || elapsedMs >= giveUpMs) return null;\n const base = delays[Math.min(retryCount, delays.length - 1)] ?? 0;\n const spread = Math.min(2000, Math.max(500, base / 2));\n return Math.round(base + random() * spread);\n },\n };\n}\n\n/* ------------------------------------------------------------------ ports */\n\n/** Whether the device has a network, and when that changes (the platform port's `network`). */\nexport interface NetworkPort {\n online(): boolean;\n subscribe(listener: (online: boolean) => void): () => void;\n}\n\n/** `navigator.onLine` + `online`/`offline` events; outside a browser it is always online. */\nexport function createWebNetworkPort(): NetworkPort {\n const scope = globalThis as unknown as {\n navigator?: { onLine?: boolean };\n addEventListener?: (type: string, fn: () => void) => void;\n removeEventListener?: (type: string, fn: () => void) => void;\n };\n return {\n online: () => scope.navigator?.onLine !== false,\n subscribe(listener) {\n if (typeof scope.addEventListener !== \"function\") return () => undefined;\n const on = (): void => listener(true);\n const off = (): void => listener(false);\n scope.addEventListener(\"online\", on);\n scope.addEventListener(\"offline\", off);\n return () => {\n scope.removeEventListener?.(\"online\", on);\n scope.removeEventListener?.(\"offline\", off);\n };\n },\n };\n}\n\n/**\n * THE REACHABILITY PROBE: does each URL answer at all? An opaque (`no-cors`) GET resolves for any\n * HTTP answer and rejects only when the network path is down — exactly the question, with no\n * CORS dependency. All URLs must answer; a timeout is a \"no\".\n */\nexport function createReachabilityProbe(urls: () => readonly string[], timeoutMs = 4000): () => Promise<boolean> {\n return async () => {\n const targets = urls().filter((url) => url.length > 0);\n if (targets.length === 0 || typeof fetch !== \"function\") return true;\n const one = async (url: string): Promise<boolean> => {\n const controller = typeof AbortController === \"function\" ? new AbortController() : null;\n const timer = setTimeout(() => controller?.abort(), timeoutMs);\n try {\n await fetch(url, {\n method: \"GET\",\n mode: \"no-cors\",\n cache: \"no-store\",\n credentials: \"omit\",\n ...(controller !== null ? { signal: controller.signal } : {}),\n });\n return true;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n };\n const answers = await Promise.all(targets.map(one));\n return answers.every(Boolean);\n };\n}\n\n/** `wss://x.livekit.cloud` → `https://x.livekit.cloud` (what the probe can GET). */\nexport function httpUrlOf(serverUrl: string): string {\n return serverUrl.replace(/^ws(s?):\\/\\//i, \"http$1://\");\n}\n\n/* ------------------------------------------------------------------ resume marker */\n\n/** Per-tab storage for the reload-after-offline marker (session storage on the web). */\nexport interface ResumeStorage {\n load(roomName: string): ResumeMarker | null;\n save(roomName: string, marker: ResumeMarker): void;\n clear(roomName: string): void;\n}\n\nexport interface ResumeMarker {\n readonly request: JoinRequest;\n /** Epoch ms of the last time this tab was in (or dropped from) the call. */\n readonly at: number;\n}\n\n/** How long after the last in-call moment a reload still goes straight back in. */\nexport const RESUME_WINDOW_MS = 15 * 60_000;\n\nconst RESUME_KEY = \"mx-meet:resume:\";\n\nexport function createWebResumeStorage(): ResumeStorage | undefined {\n const storage = (globalThis as unknown as { sessionStorage?: Storage }).sessionStorage;\n if (storage === undefined) return undefined;\n return {\n load(roomName) {\n try {\n const raw = storage.getItem(RESUME_KEY + roomName);\n if (raw === null) return null;\n const parsed = JSON.parse(raw) as ResumeMarker;\n return typeof parsed?.at === \"number\" && typeof parsed.request?.roomName === \"string\" ? parsed : null;\n } catch {\n return null;\n }\n },\n save(roomName, marker) {\n try {\n storage.setItem(RESUME_KEY + roomName, JSON.stringify(marker));\n } catch {\n // Storage full or blocked: a reload lands on the pre-join, as before. Not fatal.\n }\n },\n clear(roomName) {\n try {\n storage.removeItem(RESUME_KEY + roomName);\n } catch {\n // Already gone.\n }\n },\n };\n}\n\n/** Phases a deliberate or server-decided exit lands on: the marker is cleared, no resume. */\nconst CLEARS_RESUME = (phase: MeetPhase): boolean =>\n phase === \"left\" ||\n phase === \"removed\" ||\n phase === \"superseded\" ||\n phase === \"inactive\" ||\n phase === \"ended\" ||\n phase === \"record\" ||\n phase === \"prejoin\" ||\n phase === \"denied\" ||\n phase === \"knock_expired\" ||\n phase.startsWith(\"refused:\");\n\n/**\n * `wr-refresh-while-waiting`: a reload while waiting goes straight back to waiting (the same\n * person key, the same knock — the server keeps its place), never to the name or device steps.\n */\nconst WAITING_RESUMES = (phase: MeetPhase): boolean => phase.startsWith(\"waiting:\");\n\n/* ------------------------------------------------------------------ the supervisor */\n\nexport interface ConnectionSupervisorOptions {\n readonly store: MeetStore;\n readonly network: NetworkPort;\n /** Do our API and the media server answer? */\n readonly probe: () => Promise<boolean>;\n readonly policy: () => ConnectionPolicy;\n /** Rejoin from `disconnected` with a fresh mint (the engine's `autoRejoin`). */\n readonly autoRejoin: () => Promise<void>;\n /** The request to resume with (current media state folded in), or null. */\n readonly currentRequest: () => JoinRequest | null;\n readonly resume?: ResumeStorage | undefined;\n readonly now?: () => number;\n readonly random?: () => number;\n /** How often to re-probe while not stable (ms). Default 3000. */\n readonly probeEveryMs?: number;\n readonly report?: (level: \"info\" | \"warn\", message: string) => void;\n}\n\nexport interface ConnectionSupervisor {\n /** LiveKit's connection state, as the driver reports it. */\n driverState(state: DriverConnectionState): void;\n /** The region to show beside `disconnected` when the server's state read failed. */\n exitConnection(): Exclude<ConnectionRegion, \"stable\" | \"reconnecting\" | \"signal_reconnecting\">;\n /** The resume marker for `roomName` if it is fresh, else null (and a stale one is cleared). */\n resumable(roomName: string): ResumeMarker | null;\n dispose(): void;\n}\n\n/** Backoff before auto-rejoin attempt `n` (0-based): 1 s, 2 s, 4 s … capped at 15 s, jittered ±50%. */\nexport function autoRejoinDelayMs(attempt: number, random: () => number = Math.random): number {\n const base = Math.min(15_000, 1000 * 2 ** Math.max(0, attempt));\n return Math.round(base * (0.5 + random()));\n}\n\nexport function createConnectionSupervisor(options: ConnectionSupervisorOptions): ConnectionSupervisor {\n const { store, network } = options;\n const now = options.now ?? (() => Date.now());\n const random = options.random ?? Math.random;\n const probeEveryMs = options.probeEveryMs ?? 3000;\n\n /** What LiveKit last said, independent of the network: null = connected / not in a room. */\n let driverRegion: \"reconnecting\" | \"signal_reconnecting\" | null = null;\n let probeTimer: ReturnType<typeof setTimeout> | null = null;\n let probing = false;\n let bannerTimer: ReturnType<typeof setTimeout> | null = null;\n let rejoinTimer: ReturnType<typeof setTimeout> | null = null;\n let rejoinAttempts = 0;\n let lastPhase: MeetPhase = store.snapshot().phase;\n let lastSavedAt = 0;\n let disposed = false;\n\n const inCall = (): boolean => store.snapshot().phase === \"in_call\";\n const dropped = (): boolean => store.snapshot().phase === \"disconnected\";\n\n function setRegion(next: ConnectionRegion): void {\n if (store.snapshot().connection !== next) store.patch({ connection: next });\n }\n\n /** The region the facts say, given the last probe answer (true/false) or none (null). */\n function regionFor(reachable: boolean | null): ConnectionRegion {\n if (!network.online()) return \"offline\";\n if (reachable === false) return \"server_unreachable\";\n if (dropped()) return reachable === true ? \"stable\" : store.snapshot().connection;\n return driverRegion ?? \"stable\";\n }\n\n function needsWatch(): boolean {\n const snap = store.snapshot();\n if (dropped()) return true;\n return inCall() && (driverRegion !== null || snap.connection !== \"stable\");\n }\n\n function stopProbe(): void {\n if (probeTimer !== null) clearTimeout(probeTimer);\n probeTimer = null;\n }\n\n function scheduleProbe(delayMs: number): void {\n if (disposed || probeTimer !== null) return;\n probeTimer = setTimeout(() => {\n probeTimer = null;\n void runProbe();\n }, delayMs);\n }\n\n async function runProbe(): Promise<void> {\n if (disposed || probing) return;\n if (!needsWatch()) return;\n if (!network.online()) {\n setRegion(\"offline\");\n scheduleProbe(probeEveryMs);\n return;\n }\n probing = true;\n let reachable = false;\n try {\n reachable = await options.probe();\n } catch {\n reachable = false;\n } finally {\n probing = false;\n }\n if (disposed) return;\n if (inCall() || dropped()) setRegion(regionFor(reachable));\n if (dropped() && reachable) maybeRejoin();\n if (needsWatch()) scheduleProbe(Math.round(probeEveryMs * (0.75 + random() * 0.5)));\n }\n\n function syncBanner(): void {\n const snap = store.snapshot();\n const unstable = snap.phase === \"in_call\" && snap.connection !== \"stable\";\n if (!unstable) {\n if (bannerTimer !== null) clearTimeout(bannerTimer);\n bannerTimer = null;\n if (snap.connectionBanner) store.patch({ connectionBanner: false });\n return;\n }\n if (snap.connectionBanner || bannerTimer !== null) return;\n const delay = options.policy().bannerDelayMs;\n if (delay <= 0) {\n store.patch({ connectionBanner: true });\n return;\n }\n bannerTimer = setTimeout(() => {\n bannerTimer = null;\n const latest = store.snapshot();\n if (latest.phase === \"in_call\" && latest.connection !== \"stable\" && !latest.connectionBanner) {\n store.patch({ connectionBanner: true });\n }\n }, delay);\n }\n\n function stopRejoin(): void {\n if (rejoinTimer !== null) clearTimeout(rejoinTimer);\n rejoinTimer = null;\n }\n\n /** From `disconnected`, once reachable: one backoff-delayed attempt (if the policy allows). */\n function maybeRejoin(): void {\n if (disposed || rejoinTimer !== null || !dropped()) return;\n const policy = options.policy();\n if (!policy.autoRejoin) return;\n if (rejoinAttempts >= policy.autoRejoinMaxAttempts) return;\n if (!network.online()) return;\n const attempt = rejoinAttempts;\n rejoinTimer = setTimeout(() => {\n rejoinTimer = null;\n if (disposed || !dropped() || !network.online()) return;\n rejoinAttempts = attempt + 1;\n options.report?.(\"info\", `meet: rejoining by itself (attempt ${rejoinAttempts} of ${policy.autoRejoinMaxAttempts})`);\n void options.autoRejoin().catch(() => undefined);\n }, autoRejoinDelayMs(attempt, random));\n }\n\n function saveMarker(force: boolean): void {\n const resume = options.resume;\n const snap = store.snapshot();\n if (resume === undefined || snap.roomName === null) return;\n const at = now();\n if (!force && at - lastSavedAt < 10_000) return;\n const request = options.currentRequest();\n if (request === null) return;\n lastSavedAt = at;\n resume.save(snap.roomName, { request, at });\n }\n\n const unsubscribeStore = store.subscribe(() => {\n if (disposed) return;\n const snap = store.snapshot();\n const phase = snap.phase;\n const changed = phase !== lastPhase;\n lastPhase = phase;\n if (changed) {\n if (phase === \"in_call\") {\n rejoinAttempts = 0;\n stopRejoin();\n saveMarker(true);\n } else if (WAITING_RESUMES(phase)) {\n stopRejoin();\n driverRegion = null;\n saveMarker(true);\n } else if (phase === \"disconnected\") {\n driverRegion = null;\n saveMarker(true);\n // Reachable right now? Probe at once; the probe starts the rejoin.\n stopProbe();\n scheduleProbe(0);\n } else {\n stopRejoin();\n if (phase !== \"joining\") driverRegion = null;\n if (CLEARS_RESUME(phase) && snap.roomName !== null) options.resume?.clear(snap.roomName);\n }\n } else if (phase === \"in_call\" || phase === \"disconnected\") {\n saveMarker(false);\n }\n syncBanner();\n });\n\n const unsubscribeNetwork = network.subscribe((online) => {\n if (disposed) return;\n if (!online) {\n if (inCall() || dropped()) setRegion(\"offline\");\n stopRejoin();\n stopProbe();\n if (needsWatch()) scheduleProbe(probeEveryMs);\n return;\n }\n // Back online: is it really (our servers answer)? The probe decides the region and the rejoin.\n stopProbe();\n if (needsWatch()) scheduleProbe(0);\n });\n\n return {\n driverState(state) {\n if (disposed) return;\n if (state === \"connected\") {\n driverRegion = null;\n if (inCall() || store.snapshot().phase === \"joining\") setRegion(network.online() ? \"stable\" : \"offline\");\n if (!needsWatch()) stopProbe();\n return;\n }\n if (state === \"reconnecting\" || state === \"signal_reconnecting\") {\n driverRegion = state;\n if (inCall()) {\n // Honest at once: no network is `offline` before any probe; otherwise LiveKit's word,\n // until the probe says whether our servers answer.\n setRegion(network.online() ? state : \"offline\");\n stopProbe();\n scheduleProbe(0);\n }\n }\n },\n exitConnection() {\n return network.online() ? \"server_unreachable\" : \"offline\";\n },\n resumable(roomName) {\n const resume = options.resume;\n if (resume === undefined) return null;\n const marker = resume.load(roomName);\n if (marker === null) return null;\n if (now() - marker.at > RESUME_WINDOW_MS) {\n resume.clear(roomName);\n return null;\n }\n return marker;\n },\n dispose() {\n disposed = true;\n stopProbe();\n stopRejoin();\n if (bannerTimer !== null) clearTimeout(bannerTimer);\n bannerTimer = null;\n unsubscribeStore();\n unsubscribeNetwork();\n },\n };\n}\n\n/* ------------------------------------------------------------------ others' drops (ghosts) */\n\n/**\n * LiveKit departure reasons that mean \"they meant to go, or were sent\": no ghost. Anything else —\n * no reason, a signal close, a timeout, a media failure — is a drop, and the tile stays as\n * `reconnecting` for `reconnect_grace_seconds` so others see the truth instead of a vanished tile.\n */\nconst DELIBERATE_DEPARTURES: ReadonlySet<string> = new Set([\n \"CLIENT_INITIATED\",\n \"PARTICIPANT_REMOVED\",\n \"ROOM_DELETED\",\n \"ROOM_CLOSED\",\n \"DUPLICATE_IDENTITY\",\n \"USER_REJECTED\",\n \"USER_UNAVAILABLE\",\n \"SIP_TRUNK_FAILURE\",\n]);\n\nexport function isUnannouncedDeparture(reason: string | null): boolean {\n return reason === null || !DELIBERATE_DEPARTURES.has(reason);\n}\n\nexport interface Ghost {\n readonly participant: DriverParticipant;\n readonly until: number;\n}\n\n/**\n * The roster with the live ghosts merged in. A ghost whose identity is back in the live roster is\n * dropped (the person returned — one tile, never a copy); an expired one is dropped.\n */\nexport function mergeGhosts(\n live: readonly DriverParticipant[],\n ghosts: ReadonlyMap<string, Ghost>,\n at: number,\n): { readonly roster: readonly (DriverParticipant & { readonly reconnecting?: boolean })[]; readonly kept: Map<string, Ghost> } {\n const present = new Set(live.map((p) => p.identity));\n const kept = new Map<string, Ghost>();\n const extra: (DriverParticipant & { readonly reconnecting: boolean })[] = [];\n for (const [identity, ghost] of ghosts) {\n if (present.has(identity) || ghost.until <= at) continue;\n kept.set(identity, ghost);\n extra.push({\n ...ghost.participant,\n micEnabled: false,\n cameraEnabled: false,\n screenSharing: false,\n reconnecting: true,\n });\n }\n return { roster: [...live, ...extra], kept };\n}\n","/**\n * S8 — LEAVE INTENTS (CORE-DESIGN §2.7 `/v1/meet/leave`, §4.2 `host_leave_prompt` · `end_confirm`,\n * §11 `leave-or-end-choice`, `host-leave-assign`).\n *\n * What the host's Leave OFFERS is the meeting's behavior profile, resolved by the server and\n * carried on the token response (`leave_policy`). What each choice DOES is the server's: the\n * `/leave` door decides from the pass and the run row whether this person holds the run's host\n * powers. The skin owns only the words.\n */\n\nimport type { ParticipantRole } from \"./types\";\n\nexport type HostLeavePrompt = \"leave_or_end\" | \"must_assign_or_end\" | \"leave_only\";\n\nexport interface LeavePolicy {\n readonly hostLeavePrompt: HostLeavePrompt;\n /** Ask \"End for everyone?\" once more before ending (Teams). */\n readonly endConfirm: boolean;\n}\n\n/** The Meet profile's values — used only when the server sent none (an ad-hoc call). */\nexport const DEFAULT_LEAVE_POLICY: LeavePolicy = { hostLeavePrompt: \"leave_or_end\", endConfirm: false };\n\nconst PROMPTS: readonly HostLeavePrompt[] = [\"leave_or_end\", \"must_assign_or_end\", \"leave_only\"];\n\nexport function leavePolicyFrom(raw: unknown): LeavePolicy {\n if (typeof raw !== \"object\" || raw === null) return DEFAULT_LEAVE_POLICY;\n const value = raw as Record<string, unknown>;\n const prompt = value[\"host_leave_prompt\"];\n const confirm = value[\"end_confirm\"];\n return {\n hostLeavePrompt: PROMPTS.includes(prompt as HostLeavePrompt)\n ? (prompt as HostLeavePrompt)\n : DEFAULT_LEAVE_POLICY.hostLeavePrompt,\n endConfirm: typeof confirm === \"boolean\" ? confirm : DEFAULT_LEAVE_POLICY.endConfirm,\n };\n}\n\n/** One thing the Leave control can do. */\nexport type LeaveChoice = \"leave\" | \"assign_and_leave\" | \"end_for_all\";\n\n/**\n * The choices Leave offers, in order. Not the host → just leave (no menu). The host:\n * `leave_or_end` (Meet) → leave · end for everyone; `must_assign_or_end` (Zoom) → assign a new\n * host (when someone else is here) · end for everyone, with a plain leave only when nobody is\n * left to hand it to; `leave_only` (Teams) → leave · end meeting.\n */\nexport function leaveChoices(args: {\n readonly policy: LeavePolicy;\n readonly role: ParticipantRole | null;\n readonly othersPresent: boolean;\n}): readonly LeaveChoice[] {\n if (args.role !== \"host\") return [\"leave\"];\n switch (args.policy.hostLeavePrompt) {\n case \"must_assign_or_end\":\n return args.othersPresent ? [\"assign_and_leave\", \"end_for_all\"] : [\"leave\", \"end_for_all\"];\n case \"leave_only\":\n case \"leave_or_end\":\n return [\"leave\", \"end_for_all\"];\n }\n}\n","/**\n * THE TOKEN FLOW — the ONLY authorization path into a room (R1).\n *\n * 🚨 **THE CLIENT NEVER HOLDS A LIVEKIT SECRET.** Not in an env var, not in a\n * build-time constant, not \"just for local dev\". A LiveKit API secret in a\n * browser bundle mints a token for ANY room in the project, including one the\n * holder was never invited to — it is a full compromise of every meeting in the\n * organization. This package therefore has no code path that could accept one:\n * it asks aidream, and aidream mints.\n *\n * Four hard parts live here so no host reimplements them (C22):\n *\n * 1. **Single flight.** A React strict-mode double effect, a re-render, and a\n * reconnect all ask at once; they share ONE request. Two tokens for one\n * identity is not harmless — LiveKit disconnects the older session, which\n * looks exactly like \"my call randomly drops when I open the tab\".\n * 2. **Expiry is honored BEFORE it bites.** A token is refreshed at 80% of its\n * life, not when a reconnect fails at 100%. A meeting that outlives its\n * token and cannot reconnect is the classic 2-hour-meeting bug.\n * 3. **Guests are first-class, not an exception.** A guest has no session and\n * the request carries a display name instead; the SERVER decides what a\n * guest may do. There is no client-side \"is this person allowed\" branch.\n * 4. **A missing session is a lifecycle moment.** Between sign-in and token\n * arrival, the fetch retries ONCE after the host's session resolves and then\n * reports `session-unavailable` as a warning — never a red error, never a\n * retry storm.\n */\n\nimport { connectionPolicyFrom, type ConnectionPolicy } from \"./connection-supervisor\";\nimport { leavePolicyFrom, type LeavePolicy } from \"./leave\";\nimport type { MeetApi } from \"./api\";\nimport { invalidResponse, MeetError } from \"./errors\";\nimport type { JoinRefusalReason } from \"./reasons\";\nimport type {\n AdmissionState,\n ObserversVisibleTo,\n MeetIdentity,\n ParticipantIdentity,\n ParticipantRole,\n ParticipantStanding,\n RoomName,\n} from \"./types\";\n\nexport interface RoomToken {\n readonly token: string;\n readonly serverUrl: string;\n readonly roomName: RoomName;\n /** S7: the PERSON key (`user:<id>` · `guest:<device>`) — what every roster tile and row uses. */\n readonly identity: ParticipantIdentity;\n /** S7: this tab's LiveKit identity, `<person>~<tab>` (the person key from an older server). */\n readonly connectionIdentity?: string | undefined;\n /** S7: `companion` for a \"join here too\" connection (no audio publish, speaker off). */\n readonly connectionKind?: \"main\" | \"companion\" | undefined;\n /**\n * S7: set when the person is ALREADY in the room on another tab or device and has not chosen\n * yet — the meeting's `second_device` rule (`ask_switch_or_join` · `ask_transfer_or_add`). No\n * LiveKit token was issued (`token` is empty); the pre-join offers the choice.\n */\n readonly secondDeviceChoice?: string | null | undefined;\n /** S7: the meeting's `second_device` rule, always — told to a second tab of this browser. */\n readonly secondDeviceRule?: string | null | undefined;\n readonly displayName: string;\n /** ISO. The package refreshes at 80% of the remaining life. */\n readonly expiresAt: string;\n /** What the SERVER granted. The client renders from this; it never decides. */\n readonly canPublish: boolean;\n readonly canSubscribe: boolean;\n readonly canRecord: boolean;\n readonly isHost: boolean;\n /**\n * 🚨 THE SERVER'S WORD ON WHO THIS JOINER IS (MRI-A11) — `host · cohost · presenter ·\n * participant · viewer` (CORE-DESIGN §2.1). The same string is minted into the LiveKit\n * token's participant metadata and its `matrx.participant.role` attribute.\n */\n readonly role: ParticipantRole;\n /** Where this joiner stands with the meeting — beside the role, never inside it. */\n readonly standing: ParticipantStanding | null;\n /**\n * 🚨 THE MEETING PASS (CORE-DESIGN §2.5) — the credential for every aidream meet route,\n * sent as `x-meet-pass`. It is NOT the LiveKit token (`token`), which is for the media\n * connection only. Null for an ad-hoc call room (no meeting) or an older server.\n */\n readonly meetingPass: string | null;\n readonly meetingPassExpiresAt: string | null;\n /** The run (`meet_sessions.id`) this join belongs to; null while waiting before any run. */\n readonly sessionId: string | null;\n /** True when the server put this joiner in the lobby (D6). */\n readonly lobby: boolean;\n /**\n * S4: what the mint wrote on the person row — `admitted` · `knocking` · `waiting_host`.\n * A waiting person then reads `GET /v1/meet/state` every `pollSeconds`.\n */\n readonly admissionState?: AdmissionState | undefined;\n readonly pollSeconds?: number | undefined;\n /**\n * Meet wave 5: the breakout room this pass is for, or null for the main room. The\n * pass is still keyed by the MEETING's room name — it is this person's pass for the\n * meeting, and the server accepts a breakout pass wherever the meeting's is asked.\n */\n readonly breakoutRoomId?: string | null | undefined;\n /** HR-360 wave 2 (MD-16): who sees the \"Observing\" line in People (knob). */\n readonly observersVisibleTo?: ObserversVisibleTo | undefined;\n /**\n * S6b: the meeting's connection knobs (`reconnect_banner_delay_ms`, `auto_rejoin`,\n * `auto_rejoin_max_attempts`, `reconnect_grace_seconds`), resolved by the server's policy\n * ladder. Absent from an older server → the knob defaults.\n */\n readonly connectionPolicy?: ConnectionPolicy | undefined;\n /** S8: what the host's Leave offers for this meeting (`host_leave_prompt`, `end_confirm`). */\n readonly leavePolicy?: LeavePolicy | undefined;\n /** host-reclaim (`host_reclaim = prompt`): the returning owner may take the host powers back. */\n readonly canReclaim?: boolean | undefined;\n}\n\nexport type { AccessTokenSource } from \"./api\";\n\nexport interface TokenClientOptions {\n /** The ONE org-aware aidream client (MRI-A16). Owns base url and headers. */\n api: MeetApi;\n /** Injected clock, so expiry logic is provable without waiting two hours. */\n now?: (() => number) | undefined;\n /**\n * Where a GUEST's pass survives a page close (MRF-1). Absent means passes\n * live only in memory, which is correct for a member (they have a session to\n * mint a new one from) and is why this is optional rather than assumed.\n */\n storage?: RoomTokenStorage | undefined;\n}\n\n/**\n * 🚨 **WHY A GUEST'S PASS MUST OUTLIVE THE TAB (MRF-1).**\n *\n * A guest has no session, so their only credential for a meeting is the room\n * token this server minted for them. After the meeting ends, `/token` refuses\n * to mint another one — correctly: the room is gone and nobody may rejoin it.\n * So a guest who closes the tab and comes back to read the summary has, in a\n * purely in-memory client, *no way to prove they were ever there* — and the one\n * honest thing left to show them is a refusal.\n *\n * Persisting the pass is what makes the durable link durable for them. It is\n * per-device by nature (it is stored beside the device id that minted it) and\n * it expires on its own: `meet.room_token_ttl_seconds` is the organization's\n * setting for how long, and an expired pass produces the renew-the-link\n * sentence, never \"you were never here\".\n */\nexport interface RoomTokenStorage {\n read(roomName: RoomName): string | null;\n write(roomName: RoomName, serialized: string): void;\n remove(roomName: RoomName): void;\n}\n\n/** `localStorage` when it exists; a no-op that SAYS so otherwise. */\nexport function createWebRoomTokenStorage(\n onDiagnostic?: (message: string, remedy: string) => void,\n): RoomTokenStorage {\n const store = (globalThis as { localStorage?: Storage }).localStorage;\n const key = (roomName: RoomName): string => `mx.meet.pass.${roomName}`;\n if (store === undefined) {\n // NEVER SILENT. A guest in a browser with no storage will be refused the\n // record after they close the tab, and that is worth a line rather than a\n // mystery.\n onDiagnostic?.(\n \"This browser exposes no localStorage, so a guest's meeting pass cannot be kept.\",\n \"A guest who closes the tab will have to open the meeting link again before the \" +\n \"record opens. Signed-in members are unaffected.\",\n );\n return { read: () => null, write: () => undefined, remove: () => undefined };\n }\n return {\n read(roomName) {\n try {\n return store.getItem(key(roomName));\n } catch {\n return null;\n }\n },\n write(roomName, serialized) {\n try {\n store.setItem(key(roomName), serialized);\n } catch {\n /* a full or blocked store is a lost convenience, never a lost join */\n }\n },\n remove(roomName) {\n try {\n store.removeItem(key(roomName));\n } catch {\n /* see write */\n }\n },\n };\n}\n\n/**\n * The meeting pass a GUEST's browser kept for a room (MRF-1), or null — read from the same\n * storage `createWebRoomTokenStorage` writes. Used by \"keep your notes\" after sign-up.\n */\nexport function rememberedMeetingPass(\n roomName: RoomName,\n storage: RoomTokenStorage = createWebRoomTokenStorage(),\n): string | null {\n const raw = storage.read(roomName);\n if (raw === null) return null;\n try {\n const parsed = JSON.parse(raw) as Partial<RoomToken>;\n if (parsed.standing !== \"guest\") return null;\n return typeof parsed.meetingPass === \"string\" && parsed.meetingPass.length > 0\n ? parsed.meetingPass\n : null;\n } catch {\n return null;\n }\n}\n\nexport interface TokenRequest {\n readonly roomName: RoomName;\n readonly identity: MeetIdentity;\n /** Present for a meeting join; absent for an ad-hoc call room. */\n readonly meetingId?: string | undefined;\n /** The public meeting slug, when joining by link without knowing the id. */\n readonly slug?: string | undefined;\n /** Meet wave 5: a breakout room of this meeting; absent = the main room. */\n readonly breakoutRoomId?: string | null | undefined;\n /** HR-360 wave 2 (MD-16): ask to join as a silent observer. The server decides. */\n readonly observe?: boolean | undefined;\n /** S7: this tab's claimed id (`core/tab-claim.ts`) — the LiveKit identity is `<person>~<tab>`. */\n readonly tabId?: string | undefined;\n /** S7: the person's answer when they are already in this meeting on another tab or device. */\n readonly secondDevice?: SecondDeviceAnswer | undefined;\n}\n\n/** S7: what a person already in the meeting elsewhere chose (CORE-DESIGN §2.1). */\nexport type SecondDeviceAnswer = \"switch_here\" | \"join_too\";\n\nexport interface TokenClient {\n /** A currently-valid token, minting or refreshing only when it must. */\n get(request: TokenRequest): Promise<RoomToken>;\n /** Drop the cached token for a room (after a `denied`, or on leave). */\n invalidate(roomName: RoomName): void;\n /** True when the cached token is inside its refresh window. */\n needsRefresh(roomName: RoomName): boolean;\n /**\n * The pass this DEVICE holds for a room, **including an expired one** (MRF-1).\n *\n * 🚨 The expired case is the whole reason this returns the token instead of\n * `null`: \"your pass expired, open the link again\" and \"you were never in\n * this meeting\" are different sentences, and only the caller that can see the\n * expiry can say the right one. Never used to join — `get` mints for that.\n */\n remembered(roomName: RoomName): RoomToken | null;\n}\n\n/** The aidream routes this package calls. ONE place, so a rename is one edit. */\n/**\n * EVERY aidream ROUTE THIS PACKAGE CALLS, in one object.\n *\n * This is the contract in both directions: aidream carries a test\n * (`services/meet/tests/test_meet_service.py`) that reads THIS object out of\n * this source file and fails if the server stops serving one of these paths.\n * The package is the spec.\n *\n * `meetingEnd` is pinned here BEFORE the server serves it (MRI-A6 owns landing\n * it). That is deliberate and it is not a lie on screen: the control that calls\n * it renders, and a 404 from a server that has not landed A6 is reported as the\n * honest sentence \"Ending a meeting is not available on this server yet\" —\n * never a dead button, never a spinner, and never a silent no-op.\n */\nexport const MEET_ROUTES = {\n token: \"/api/v1/meet/token\",\n recordingStart: \"/api/v1/meet/recording/start\",\n recordingStop: \"/api/v1/meet/recording/stop\",\n noteTakerStart: \"/api/v1/meet/agent/start\",\n noteTakerStop: \"/api/v1/meet/agent/stop\",\n /** Anyone in the meeting (guests with the room pass included) asks for captions for themselves. */\n noteTakerCaptions: \"/api/v1/meet/agent/captions\",\n /** In-meeting Q&A. Streams NDJSON; body is `{meeting_id, question}` only. */\n intelligenceAsk: \"/api/v1/meet/intelligence/ask\",\n /** A host ends the meeting for everyone. Served by MRI-A6. */\n meetingEnd: \"/api/v1/meet/end\",\n /**\n * THE POST-MEETING RECORD FOR A READER WITH NO DATABASE (MRF-1). A `GET`,\n * authorized by the room token this device already holds, carried in the\n * `x-meet-pass` header — never a query parameter, because a bearer\n * credential in a URL lands in access logs, referrers and history.\n */\n record: \"/api/v1/meet/record\",\n /** A host chooses what guests may read of ONE meeting (MRF-1). */\n recordAccess: \"/api/v1/meet/record/access\",\n /**\n * KEEP YOUR NOTES (2026-10-01): a guest who just created an account presents\n * their kept pass in `x-meet-pass` with their new session, and the\n * meeting they attended becomes theirs to read. Idempotent; never a grant.\n */\n claim: \"/api/v1/meet/claim\",\n /**\n * Meet wave 1 (0.8): bring every invitee up to date after a write — sends the\n * invitation / update / cancellation emails with their `.ics` and re-queues\n * reminders. Idempotent. `{meeting_id}` is substituted by `meetingPath`.\n */\n meetingAnnounce: \"/api/v1/meet/meetings/{meeting_id}/announce\",\n /** Put a one-off meeting on the caller's own Google Calendar (gated by the server). */\n meetingGoogleCalendar: \"/api/v1/meet/meetings/{meeting_id}/google-calendar\",\n /**\n * Meet wave 3: the recap email after the meeting. `GET` = the draft (subject,\n * body from the wrap-up, who it can go to); `POST` = send what the host\n * reviewed (`dry_run` renders and queues nothing). Never sent automatically.\n */\n meetingFollowUp: \"/api/v1/meet/meetings/{meeting_id}/follow-up\",\n /**\n * Meet wave 2: EVERY in-meeting host power (mute, stop video, mute all, ask to\n * unmute, spotlight, lower hands, co-host, hand over, rename, remove, report).\n * Decided by the server from the meeting itself; a guest's pass rides\n * `x-meet-pass`.\n */\n moderate: \"/api/v1/meet/moderate\",\n /** Meet wave 2: the SAVED chat — `POST` sends to everyone, `GET ?meeting_id=` reads history. */\n chat: \"/api/v1/meet/chat\",\n /**\n * Meet wave 5: polls, Q&A, the shared whiteboard and breakout rooms. `POST` acts\n * (`{meeting_id, action, …}`), `GET ?meeting_id=` reads what this viewer may see.\n */\n collab: \"/api/v1/meet/collab\",\n /** Meet wave 5: translate finished caption lines into this viewer's language. */\n captionsTranslate: \"/api/v1/meet/captions/translate\",\n /**\n * S4 — THE STATE READ (CORE-DESIGN §2.6): `GET ?meeting_id=` with `x-meet-pass`. This\n * person's admission (lazy expiry applied), whether anyone who can admit is here, the run's\n * phase. Read every `waiting_poll_seconds` while waiting and on every `mx.state.changed`.\n */\n state: \"/api/v1/meet/state\",\n /** S4: `withdraw` (stop waiting) or `leave` (close this tab); the pass may ride in the body. */\n leave: \"/api/v1/meet/leave\",\n /**\n * S8: a host's browser just called an admission DOOR (`meet_admit` · `meet_deny` ·\n * `meet_undeny` · `meet_admit_all`). Payload-free: the server republishes the room's\n * replicated state (the People badge's waiting count) from the database truth.\n */\n admissionChanged: \"/api/v1/meet/admission/changed\",\n /** S4: a pass holder (guests included) saw the recording notice, for this run. */\n consent: \"/api/v1/meet/consent\",\n} as const;\n\n/** A `MEET_ROUTES` template with its `{meeting_id}` filled in, URL-encoded. */\nexport function meetingPath(template: string, meetingId: string): string {\n return template.replace(\"{meeting_id}\", encodeURIComponent(meetingId));\n}\n\n/** The header the meeting pass rides on (CORE-DESIGN §2.5). Pinned on both sides. */\nexport const MEETING_PASS_HEADER = \"x-meet-pass\";\n\n/** The `x-meet-pass` header for a held token, or no header at all. */\nexport function meetingPassHeaders(token: RoomToken | null | undefined): Record<string, string> {\n const pass = token?.meetingPass ?? null;\n return pass !== null && pass.length > 0 ? { [MEETING_PASS_HEADER]: pass } : {};\n}\n\n/** Refresh at 80% of the token's life — never at the moment it dies. */\nconst REFRESH_RATIO = 0.8;\n\ninterface CacheEntry {\n token: RoomToken;\n mintedAt: number;\n inFlight: Promise<RoomToken> | null;\n}\n\nfunction readString(source: Record<string, unknown>, key: string): string | null {\n const value = source[key];\n return typeof value === \"string\" && value.length > 0 ? value : null;\n}\n\nfunction readBoolean(source: Record<string, unknown>, key: string, fallback: boolean): boolean {\n const value = source[key];\n return typeof value === \"boolean\" ? value : fallback;\n}\n\nconst TOKEN_STANDINGS: readonly ParticipantStanding[] = [\"member\", \"invited\", \"outsider\", \"guest\"];\n\nfunction readStanding(source: Record<string, unknown>, key: string): ParticipantStanding | null {\n const value = source[key];\n return TOKEN_STANDINGS.includes(value as ParticipantStanding) ? (value as ParticipantStanding) : null;\n}\n\nconst TOKEN_ROLES: readonly ParticipantRole[] = [\"host\", \"cohost\", \"presenter\", \"participant\", \"viewer\"];\n\n/** A word this package does not know grants nothing — it is not a host. */\nfunction readRole(source: Record<string, unknown>, key: string): ParticipantRole | null {\n const value = source[key];\n return TOKEN_ROLES.includes(value as ParticipantRole) ? (value as ParticipantRole) : null;\n}\n\nexport function parseRoomToken(body: unknown, roomName: RoomName): RoomToken {\n if (typeof body !== \"object\" || body === null) {\n throw invalidResponse(\"meet.token\", \"the response body was not an object\");\n }\n const envelope = body as Record<string, unknown>;\n // aidream's ordinary envelope is `{ success, data, msg }`; a bare object is\n // accepted too so a self-hosted route does not have to imitate it.\n const payload =\n typeof envelope[\"data\"] === \"object\" && envelope[\"data\"] !== null\n ? (envelope[\"data\"] as Record<string, unknown>)\n : envelope;\n\n const isHost = readBoolean(payload, \"is_host\", false);\n const token = readString(payload, \"token\");\n const serverUrl = readString(payload, \"server_url\") ?? readString(payload, \"serverUrl\");\n const identity = readString(payload, \"identity\");\n const expiresAt = readString(payload, \"expires_at\") ?? readString(payload, \"expiresAt\");\n\n const secondDeviceChoice = readString(payload, \"second_device_choice\");\n if ((token === null && secondDeviceChoice === null) || serverUrl === null || identity === null) {\n throw invalidResponse(\n \"meet.token\",\n \"the response is missing token, server_url, or identity — joining with a partial \" +\n \"token would fail inside LiveKit with an opaque error instead of here\",\n );\n }\n if (expiresAt === null) {\n throw invalidResponse(\n \"meet.token\",\n \"the response carries no expires_at. Without it the package cannot refresh before \" +\n \"the token dies, and a long meeting silently loses its ability to reconnect\",\n );\n }\n const personKey = readString(payload, \"person_key\") ?? identity;\n return {\n token: token ?? \"\",\n serverUrl,\n roomName,\n identity: personKey as ParticipantIdentity,\n connectionIdentity: readString(payload, \"connection_identity\") ?? personKey,\n connectionKind: readString(payload, \"connection_kind\") === \"companion\" ? \"companion\" : \"main\",\n secondDeviceChoice,\n secondDeviceRule: readString(payload, \"second_device_rule\"),\n displayName: readString(payload, \"display_name\") ?? readString(payload, \"displayName\") ?? identity,\n expiresAt,\n canPublish: readBoolean(payload, \"can_publish\", true),\n canSubscribe: readBoolean(payload, \"can_subscribe\", true),\n canRecord: readBoolean(payload, \"can_record\", false),\n isHost,\n // An older server sends `is_host` and no `role`; a guest join sends neither.\n // Deriving the role from what DID arrive keeps a host a host against a\n // server that has not landed MRI-A11 — and never invents a host from\n // silence, because both fallbacks below grant nothing.\n // A word this package does not know grants nothing: `participant`, never a host.\n role: readRole(payload, \"role\") ?? (isHost ? \"host\" : \"participant\"),\n standing: readStanding(payload, \"standing\"),\n meetingPass: readString(payload, \"meeting_pass\"),\n meetingPassExpiresAt: readString(payload, \"meeting_pass_expires_at\"),\n sessionId: readString(payload, \"session_id\"),\n lobby: readBoolean(payload, \"lobby\", false),\n admissionState: (readString(payload, \"admission_state\") as AdmissionState | null) ?? undefined,\n pollSeconds: typeof payload[\"poll_seconds\"] === \"number\" ? (payload[\"poll_seconds\"] as number) : undefined,\n breakoutRoomId: readString(payload, \"breakout_room_id\") ?? null,\n observersVisibleTo: readString(payload, \"observers_visible_to\") === \"hosts\" ? \"hosts\" : \"everyone\",\n connectionPolicy: connectionPolicyFrom(payload[\"connection_policy\"]),\n leavePolicy: leavePolicyFrom(payload[\"leave_policy\"]),\n canReclaim: payload[\"can_reclaim\"] === true,\n };\n}\n\nexport function createTokenClient(options: TokenClientOptions): TokenClient {\n const cache = new Map<RoomName, CacheEntry>();\n const now = options.now ?? (() => Date.now());\n const storage = options.storage;\n\n /** A GUEST's pass is kept; a member's is not — they can always mint again. */\n function remember(token: RoomToken): void {\n if (storage === undefined) return;\n if (token.standing !== \"guest\") return;\n storage.write(token.roomName, JSON.stringify(token));\n }\n\n function recall(roomName: RoomName): RoomToken | null {\n if (storage === undefined) return null;\n const raw = storage.read(roomName);\n if (raw === null) return null;\n try {\n const parsed = JSON.parse(raw) as RoomToken;\n // A stored blob that is not a pass for THIS room proves nothing, and a\n // half-written one must not be handed out as a credential.\n if (typeof parsed.token !== \"string\" || parsed.token.length === 0) return null;\n if (parsed.roomName !== roomName) return null;\n return parsed;\n } catch {\n storage.remove(roomName);\n return null;\n }\n }\n\n function expired(entry: CacheEntry): boolean {\n const expiry = Date.parse(entry.token.expiresAt);\n if (Number.isNaN(expiry)) return true;\n const life = expiry - entry.mintedAt;\n if (life <= 0) return true;\n return now() >= entry.mintedAt + life * REFRESH_RATIO;\n }\n\n async function request(input: TokenRequest, retryOnMissingSession: boolean): Promise<RoomToken> {\n const guest = input.identity.userId === null;\n let response: Response;\n try {\n // THE ORGANIZATION IS A HEADER, NOT ONLY A BODY FIELD — and it is put\n // there by `core/api.ts`, the one client, so no caller can forget it\n // (MRI-A16). A body-only `organization_id` looked right and never\n // arrived: the org-admission middleware refuses first, 400, before the\n // meet router is ever reached.\n response = await options.api.request({\n path: MEET_ROUTES.token,\n method: \"POST\",\n // A GUEST HAS NO SESSION AND IS STILL A FIRST-CLASS JOINER (D6), so\n // only an authenticated join requires one.\n requireAuth: !guest,\n operation: \"meet.token\",\n body: {\n room_name: input.roomName,\n ...(input.meetingId !== undefined ? { meeting_id: input.meetingId } : {}),\n ...(input.slug !== undefined ? { slug: input.slug } : {}),\n ...(input.breakoutRoomId != null ? { breakout_room_id: input.breakoutRoomId } : {}),\n ...(input.observe === true ? { observe: true } : {}),\n organization_id: input.identity.organizationId,\n display_name: input.identity.displayName,\n device_id: input.identity.deviceId,\n guest,\n ...(input.tabId !== undefined ? { tab_id: input.tabId } : {}),\n ...(input.secondDevice !== undefined ? { second_device: input.secondDevice } : {}),\n },\n });\n } catch (error) {\n if (\n retryOnMissingSession &&\n (error as { code?: string }).code === \"session-unavailable\"\n ) {\n // ONE retry, after the host's session source has had a turn. This is\n // the cure for \"909 captured errors in 0.6 seconds\": a read that lands\n // between sign-in and token arrival is a lifecycle moment.\n await Promise.resolve();\n return request(input, false);\n }\n throw error;\n }\n\n if (response.status === 401 || response.status === 403) {\n // THE SERVER'S SENTENCE IS THE ONE A PERSON READS. aidream answers every\n // refusal with `{ message, remedy }` (\"The host did not admit you…\", \"A host\n // removed you…\"); the fallback only covers a body that says nothing.\n const said = await serverSentence(response);\n throw new MeetError(\n \"forbidden\",\n said?.message ?? \"You can't join this meeting.\",\n said?.remedy ??\n \"Ask the person who shared the link to let you in, then open the link again.\",\n undefined,\n said?.reason ?? null,\n said?.retryAfterSeconds ?? null,\n );\n }\n if (response.status === 404) {\n throw new MeetError(\n \"not-found\",\n \"meet.token: no such meeting\",\n \"The link is wrong or the meeting was deleted. Show the 'meeting not found' screen; \" +\n \"a durable link that 404s is never a retry.\",\n undefined,\n \"not_found\" satisfies JoinRefusalReason,\n );\n }\n if (response.status === 410) {\n // Meet wave 1: a CANCELLED meeting. The link resolves; the room never opens.\n // The server's own sentence (with the host's reason) is the message.\n const said = await serverSentence(response);\n throw new MeetError(\n \"cancelled\",\n said?.message ?? \"This meeting was cancelled.\",\n \"Nobody can join a cancelled meeting. Show the cancelled screen with this sentence; \" +\n \"ask the host to schedule a new one.\",\n undefined,\n said?.reason ?? (\"cancelled\" satisfies JoinRefusalReason),\n );\n }\n if (response.status === 423) {\n const said = await serverSentence(response);\n throw new MeetError(\n \"room-locked\",\n said?.message ?? \"The meeting is locked.\",\n said?.remedy ??\n \"The host locked the room. Ask them to unlock it, then join again.\",\n undefined,\n said?.reason ?? (\"locked\" satisfies JoinRefusalReason),\n );\n }\n if (response.status === 503) {\n throw new MeetError(\n \"misconfigured\",\n \"meet.token: the meeting service is not configured\",\n \"aidream is missing LIVEKIT_URL / LIVEKIT_API_KEY / LIVEKIT_API_SECRET. That is a \" +\n \"server deployment fix; the client cannot and must not supply them.\",\n );\n }\n if (!response.ok) {\n throw new MeetError(\n \"token\",\n `meet.token: the server returned ${response.status}`,\n \"Transient. The package retries the join once; if it persists check aidream's meet \" +\n \"router logs for the room name in question.\",\n );\n }\n\n return parseRoomToken(await response.json(), input.roomName);\n }\n\n return {\n get(input) {\n const entry = cache.get(input.roomName);\n // A pass for the main room is not a pass for a breakout room, and back.\n const sameRoom =\n entry !== undefined &&\n (entry.token.breakoutRoomId ?? null) === (input.breakoutRoomId ?? null) &&\n // An observer's pass is not a participant's, and back (MD-16).\n (entry.token.role === \"viewer\") === (input.observe === true);\n if (entry !== undefined && sameRoom) {\n if (entry.inFlight !== null) return entry.inFlight;\n if (!expired(entry)) return Promise.resolve(entry.token);\n }\n // SINGLE FLIGHT. Every concurrent caller shares this promise, so a strict\n // mode double-mount cannot mint two tokens for one identity.\n const pending = request(input, true)\n .then((token) => {\n if (token.secondDeviceChoice != null) {\n // A QUESTION, NOT A CREDENTIAL (S7): never cached, never remembered.\n cache.delete(input.roomName);\n return token;\n }\n cache.set(input.roomName, { token, mintedAt: now(), inFlight: null });\n // MRF-1: kept HERE, at the one place a pass is ever minted, so no\n // future caller can acquire one without it being remembered.\n remember(token);\n return token;\n })\n .catch((error: unknown) => {\n // A failure is NEVER cached — one wobble would otherwise leave the\n // room unjoinable until a reload.\n cache.delete(input.roomName);\n throw error;\n });\n cache.set(input.roomName, {\n token: entry?.token ?? placeholder(input.roomName),\n mintedAt: entry?.mintedAt ?? now(),\n inFlight: pending,\n });\n return pending;\n },\n invalidate(roomName) {\n cache.delete(roomName);\n // 🚨 THE STORED PASS SURVIVES `invalidate` ON PURPOSE. This is called on\n // leave and after a refused mint, and a guest leaving a meeting is\n // exactly the person who comes back to read its summary. What `get`\n // refuses to REUSE and what the record read may PRESENT are two different\n // questions; clearing the pass here would answer the second one wrong.\n },\n needsRefresh(roomName) {\n const entry = cache.get(roomName);\n return entry === undefined || expired(entry);\n },\n remembered(roomName) {\n // The in-flight `placeholder()` occupies the cache slot with an EMPTY\n // token string and is never handed out — including here, where handing it\n // out would present \"\" as a credential and earn a 403 that means nothing.\n const live = cache.get(roomName)?.token;\n if (live !== undefined && live.token.length > 0) return live;\n return recall(roomName);\n },\n };\n}\n\n/** Never handed out: it only occupies the slot while a mint is in flight. */\nfunction placeholder(roomName: RoomName): RoomToken {\n return {\n token: \"\",\n serverUrl: \"\",\n roomName,\n identity: \"\" as ParticipantIdentity,\n connectionIdentity: \"\",\n connectionKind: \"main\",\n secondDeviceChoice: null,\n displayName: \"\",\n expiresAt: new Date(0).toISOString(),\n canPublish: false,\n canSubscribe: false,\n canRecord: false,\n isHost: false,\n role: \"participant\",\n standing: null,\n meetingPass: null,\n meetingPassExpiresAt: null,\n sessionId: null,\n lobby: false,\n breakoutRoomId: null,\n };\n}\n\n/**\n * The `{ message, remedy }` aidream puts on a refusal (`MeetError` →\n * `{ error, message, user_message, remedy }`, or FastAPI's `{ detail: {…} }`),\n * or null when the body carries no sentence.\n */\nasync function serverSentence(\n response: Response,\n): Promise<{\n message: string;\n remedy: string | undefined;\n reason: string | null;\n retryAfterSeconds: number | null;\n} | null> {\n try {\n const body = (await response.json()) as Record<string, unknown> | null;\n const source =\n body !== null && typeof body.detail === \"object\" && body.detail !== null\n ? (body.detail as Record<string, unknown>)\n : body;\n if (source === null) return null;\n const message =\n typeof source.user_message === \"string\" && source.user_message.trim() !== \"\"\n ? source.user_message\n : typeof source.message === \"string\" && source.message.trim() !== \"\"\n ? source.message\n : null;\n if (message === null) return null;\n const remedy =\n typeof source.remedy === \"string\" && source.remedy.trim() !== \"\" ? source.remedy : undefined;\n const reason = typeof source.reason === \"string\" && source.reason !== \"\" ? source.reason : null;\n const retry = source.retry_after_seconds;\n const retryAfterSeconds = typeof retry === \"number\" && Number.isFinite(retry) && retry > 0 ? Math.ceil(retry) : null;\n return { message, remedy, reason, retryAfterSeconds };\n } catch {\n return null;\n }\n}\n","/**\n * THE NOTE-TAKER, AS A SURFACE MUST RENDER IT.\n *\n * 🚨 **THE PACKAGE NEVER GUESSES WHAT THE AGENT IS DOING.** Two facts decide\n * everything here and both come from outside this client: the meeting row's\n * `metadata.note_taker` (written by the server with a CAS, MRI-A2/D9) and\n * whether the agent participant is actually in the LiveKit room. A spinner —\n * \"starting…\" held open by a client-side timer — is the exact lie this file\n * exists to prevent: it survives a worker that never came, a dispatch nobody\n * claimed, and a job that died on a release.\n *\n * 🚨 **A REFUSAL IS RENDERED VERBATIM.** `/agent/start` answers a 503 whose body\n * carries `reason` and `remedy` written by the readiness gate (D7) — \"no worker\n * is registered under matrx-note-taker\", \"every worker is at capacity\",\n * \"the fleet is draining\". Those sentences are the truth about the system and\n * they reach the screen unedited. This package adds no sentence of its own on\n * top of one the server already wrote.\n *\n * The agent's identity is DETERMINISTIC — `agent:note_taker:<meeting_id>` (D9) —\n * which is what makes \"is it in the room\" answerable without a round trip, and\n * what makes a second note-taker impossible to mistake for the first.\n */\n\nimport type { JsonObject, MeetParticipant, MeetingRecord, NoteTakerState } from \"./types\";\n\n/** The one spelling of the agent's participant identity (D9). */\nexport const NOTE_TAKER_IDENTITY_PREFIX = \"agent:note_taker:\";\n\nexport function noteTakerIdentity(meetingId: string): string {\n return `${NOTE_TAKER_IDENTITY_PREFIX}${meetingId}`;\n}\n\n/**\n * The participant attribute the note-taker sets on itself so EVERY client —\n * including one that cannot read the token metadata — knows a notice is owed\n * (D10).\n */\nexport const CONSENT_NOTICE_ATTRIBUTE = \"matrx.agent.consent_notice\";\n\n/** Where the state lives on the meeting row. One spelling, one file. */\nexport const NOTE_TAKER_METADATA_KEY = \"note_taker\";\n\nconst STATES: readonly NoteTakerState[] = [\n \"absent\",\n \"joining\",\n \"transcribing\",\n \"handoff_pending\",\n \"ended\",\n];\n\n/** The server's refusal, as it wrote it. Never paraphrased. */\nexport interface NoteTakerRefusal {\n readonly reason: string;\n readonly remedy: string;\n readonly status: number;\n}\n\nexport interface NoteTakerStatus {\n readonly state: NoteTakerState;\n /** The server's own words for why it is in this state, when it gave any. */\n readonly detail: string | null;\n /** True when the agent participant is in the room RIGHT NOW. */\n readonly present: boolean;\n /** The last dispatch refusal, rendered verbatim. */\n readonly refusal: NoteTakerRefusal | null;\n}\n\nfunction readState(value: unknown): NoteTakerState | null {\n // The server's `dispatching` (summoned, not yet in the room) reads as `joining` on screen.\n if (value === \"dispatching\") return \"joining\";\n return typeof value === \"string\" && STATES.includes(value as NoteTakerState)\n ? (value as NoteTakerState)\n : null;\n}\n\n/** Is this participant the note-taker (or any agent that owes a notice, D10)? */\nexport function isNoteTaker(participant: MeetParticipant): boolean {\n return (\n participant.identity.startsWith(NOTE_TAKER_IDENTITY_PREFIX) ||\n participant.attributes[CONSENT_NOTICE_ATTRIBUTE] !== undefined ||\n participant.isAgent\n );\n}\n\n/** The agent participant in the room, if any. */\nexport function noteTakerParticipant(\n participants: readonly MeetParticipant[],\n): MeetParticipant | null {\n return participants.find(isNoteTaker) ?? null;\n}\n\n/** The exact deterministic actor whose room presence can corroborate the row. */\nfunction exactNoteTakerParticipant(\n meeting: MeetingRecord | null,\n participants: readonly MeetParticipant[],\n): MeetParticipant | null {\n if (meeting === null) return null;\n const identity = noteTakerIdentity(meeting.id);\n return participants.find((participant) => participant.identity === identity) ?? null;\n}\n\n/**\n * THE STATE, from the two facts that decide it.\n *\n * The durable row is the authority for work. Exact room presence corroborates\n * only a durable `transcribing` state; an arbitrary agent, a consent marker,\n * or a stale note-taker participant never proves speech capture.\n */\nexport function noteTakerStatus(args: {\n meeting: MeetingRecord | null;\n participants: readonly MeetParticipant[];\n refusal?: NoteTakerRefusal | null;\n}): NoteTakerStatus {\n const present = exactNoteTakerParticipant(args.meeting, args.participants) !== null;\n const raw = args.meeting?.metadata?.[NOTE_TAKER_METADATA_KEY];\n const row: JsonObject =\n typeof raw === \"object\" && raw !== null && !Array.isArray(raw) ? raw : {};\n const recorded = readState(row[\"state\"]);\n const detail = typeof row[\"reason\"] === \"string\" && row[\"reason\"].length > 0\n ? row[\"reason\"]\n : null;\n const refusal = args.refusal ?? null;\n\n if (recorded === null) {\n return { state: \"absent\", detail, present, refusal };\n }\n if (recorded === \"transcribing\" && !present) {\n return { state: \"handoff_pending\", detail, present, refusal };\n }\n return { state: recorded, detail, present, refusal };\n}\n\n/**\n * THE SENTENCE FOR EACH STATE. One place, so the room, the panel and any future\n * surface say the same true thing — and so a state can never render as a bare\n * enum name.\n */\nexport function noteTakerLabel(status: NoteTakerStatus): string {\n switch (status.state) {\n case \"joining\":\n return \"The note-taker is joining this meeting\";\n case \"transcribing\":\n return \"The note-taker is connected and listening for speech\";\n case \"handoff_pending\":\n return \"The note-taker's transcription is interrupted; reconnection is pending\";\n case \"ended\":\n return \"The note-taker has stopped\";\n case \"absent\":\n default:\n return status.present\n ? \"A meeting assistant is connected. Transcription is not confirmed\"\n : \"No note-taker is in this meeting\";\n }\n}\n","/**\n * CAPTIONS ARE HINTS; SEGMENTS ARE TRUTH (D16).\n *\n * Two independent streams describe the same speech and they arrive out of order\n * with different identities:\n *\n * 1. **Captions** ride the LiveKit `lk.transcription` text stream, keyed by\n * `lk.segment_id`, interim-then-final, milliseconds behind the voice. They\n * exist so a person can READ the room live. Their `sender_identity` is\n * supplied by the publishing client and LiveKit does not validate it, so\n * nothing downstream — notes, Q&A, the wrap-up — may ever read them.\n * 2. **Records** are `communication.meet_transcript_segments` rows, written\n * server-side by the note-taker with a deterministic id (D6) and delivered\n * here by Postgres Changes. They are the meeting's transcript.\n *\n * The reconciliation below is what keeps the screen from saying everything\n * twice. A record REPLACES the caption it supersedes — matched by the caption\n * id the worker carries into the row, and failing that by speaker + text — and\n * a caption never overwrites a record, however late it lands.\n *\n * Everything here is a pure function over a list so the rules are provable\n * without a store, a socket, or a browser.\n */\n\nimport type { ParticipantIdentity, TranscriptSegment } from \"./types\";\n\n/** Comparable text: case, punctuation and spacing are not differences. */\nexport function normalizeSpoken(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, \" \")\n .trim();\n}\n\nfunction supersedes(record: TranscriptSegment, caption: TranscriptSegment): boolean {\n if (caption.source !== \"caption\") return false;\n if (caption.identity !== record.identity) return false;\n if (record.captionId !== null && caption.captionId === record.captionId) return true;\n const left = normalizeSpoken(record.text);\n const right = normalizeSpoken(caption.text);\n if (left.length === 0 || right.length === 0) return false;\n // An interim caption is a PREFIX of what was finally said; a final caption and\n // its record usually differ only in punctuation. Both are the same utterance.\n return left === right || left.startsWith(right) || right.startsWith(left);\n}\n\n/**\n * A caption arrived. Interim replaces interim in place; a final never loses to a\n * late interim; a record is never touched.\n */\nexport function mergeCaption(\n list: readonly TranscriptSegment[],\n caption: TranscriptSegment,\n): readonly TranscriptSegment[] {\n const index = list.findIndex(\n (entry) => entry.captionId !== null && entry.captionId === caption.captionId,\n );\n if (index === -1) {\n // The record for this utterance may already be here (the row can beat the\n // caption on a slow media path). Do not draw the same sentence twice.\n if (list.some((entry) => entry.source === \"record\" && supersedes(entry, caption))) {\n return list;\n }\n return [...list, caption];\n }\n const existing = list[index];\n if (existing === undefined) return list;\n // TRUTH WINS. A caption cannot revert a durable row, and a late interim cannot\n // revert a final — that is a finished sentence visibly becoming a half-word.\n if (existing.source === \"record\") return list;\n if (existing.final && !caption.final) return list;\n return list.map((entry, position) => (position === index ? caption : entry));\n}\n\n/**\n * A durable row arrived. It replaces the caption it supersedes — in place, so\n * the line does not jump down the panel — or lands as a new line if no caption\n * ever showed it (a surface that joined late, or captions that never came).\n */\nexport function mergeRecord(\n list: readonly TranscriptSegment[],\n record: TranscriptSegment,\n): readonly TranscriptSegment[] {\n const byId = list.findIndex((entry) => entry.source === \"record\" && entry.id === record.id);\n if (byId !== -1) {\n const existing = list[byId];\n if (existing !== undefined && existing.text === record.text && existing.final === record.final) {\n return list;\n }\n return list.map((entry, position) => (position === byId ? record : entry));\n }\n const captionIndex = list.findIndex((entry) => supersedes(record, entry));\n if (captionIndex === -1) return [...list, record];\n return list.map((entry, position) => (position === captionIndex ? record : entry));\n}\n\n/** The transcript a person reads: chronological, and never an empty line. */\nexport function readableTranscript(\n list: readonly TranscriptSegment[],\n): readonly TranscriptSegment[] {\n return [...list]\n .filter((entry) => entry.text.trim().length > 0)\n .sort((left, right) => left.startedAt.localeCompare(right.startedAt));\n}\n\n/** Build a caption line from the media plane. */\nexport function captionSegment(args: {\n captionId: string;\n meetingId: TranscriptSegment[\"meetingId\"];\n identity: ParticipantIdentity;\n speaker: string;\n text: string;\n final: boolean;\n startedAt: string;\n}): TranscriptSegment {\n return {\n id: `caption:${args.captionId}`,\n meetingId: args.meetingId,\n identity: args.identity,\n speaker: args.speaker,\n text: args.text,\n startedAt: args.startedAt,\n final: args.final,\n source: \"caption\",\n captionId: args.captionId,\n };\n}\n","/**\n * THE MEET CHANNEL NAMESPACES.\n *\n * Zero hand-rolled `.channel(` in this package (the realtime doctrine). Every\n * topic is declared through `@ai-matrx/realtime`'s registry, which refuses a\n * colliding re-declaration.\n *\n * THREE channels, each because it has a different AUDIENCE:\n *\n * - `meet-user` (per user): where a call REACHES you — the ring, the cancel,\n * the \"answered on another device\". You are subscribed to this everywhere the\n * provider is mounted, whether or not you are in a meeting.\n * - `meet-room` (per room): the pre-media room — who is knocking, who was\n * admitted, whether recording started. It carries the moments that happen\n * BEFORE and AFTER media, which LiveKit's own data plane cannot: a guest in\n * the lobby is not in the LiveKit room yet, by construction (D6).\n * - `meet-meeting` (per meeting): the durable record's feed — transcript\n * segments, notes, the recording row going `available` — so a surface that is\n * NOT in the call (a meeting page, an admin console) stays live.\n *\n * 🚨 The in-MEDIA control plane (hand raise, reactions, in-room chat) rides\n * LiveKit data messages, NOT these channels — see `DATA_TOPICS`. Splitting them\n * this way is deliberate: anything that must reach a guest with no Supabase\n * session travels on the media plane, where their room token is the authority.\n */\n\nimport { defineChannelNamespace, type ChannelNamespace } from \"@ai-matrx/realtime\";\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\nimport type { MeetingId, RoomName, UserId } from \"./types\";\n\ninterface Namespaces {\n user: ChannelNamespace;\n room: ChannelNamespace;\n meeting: ChannelNamespace;\n}\n\nfunction namespaces(): Namespaces {\n // `defineChannelNamespace` throws on a conflicting re-declaration, so these\n // are created lazily through a `globalThis` slot: with dual ESM/CJS output\n // the module can be evaluated twice in one process, and a second identical\n // declaration must be a no-op rather than a crash.\n return globalSlot<Namespaces>(\"ai-matrx.meet.channel-namespaces\", () => ({\n user: defineChannelNamespace({\n namespace: \"meet-user\",\n parts: [\"userId\"],\n description:\n \"One user's calling presence: incoming rings, cancellations, and the \" +\n \"answered-elsewhere signal that stops every other device ringing.\",\n }),\n room: defineChannelNamespace({\n namespace: \"meet-room\",\n parts: [\"roomName\"],\n description:\n \"One media room's out-of-band plane: lobby knocks and admissions, lock \" +\n \"state, and recording state — the moments that happen before a guest is \" +\n \"in the LiveKit room at all.\",\n }),\n meeting: defineChannelNamespace({\n namespace: \"meet-meeting\",\n parts: [\"meetingId\"],\n description:\n \"One meeting record's live feed: transcript segments, AI notes, and the \" +\n \"recording row settling — for surfaces that are not in the call.\",\n }),\n }));\n}\n\nexport function userTopic(userId: UserId): string {\n return namespaces().user.topic({ userId });\n}\n\nexport function roomTopic(roomName: RoomName): string {\n return namespaces().room.topic({ roomName });\n}\n\nexport function meetingTopic(meetingId: MeetingId): string {\n return namespaces().meeting.topic({ meetingId });\n}\n\n/** Broadcast event names. One place, so a sender and a receiver cannot drift. */\nexport const MEET_EVENTS = {\n /** A call is ringing you. Carries the whole invite; no follow-up read. */\n ring: \"mx.call.ring\",\n /** The caller hung up before you answered. */\n cancel: \"mx.call.cancel\",\n /** You answered (or declined) on another device. Stop ringing here. */\n settled: \"mx.call.settled\",\n /**\n * 🚨 S4 (CORE-DESIGN §2.6): the ONE room-channel event — payload-free \"something changed,\n * read again\". Nothing a person must see rides it: a waiting person learns admitted /\n * denied / expired / ended by READING their row (`GET /v1/meet/state`) on a timer and on\n * every hint. The four payload broadcasts (`mx.lobby.knock`, `mx.lobby.admission`,\n * `mx.room.lock`, `mx.room.recording`) are gone with their senders and receivers. The\n * server publishes the same event on the same topic (aidream `admission.room_topic`).\n */\n stateChanged: \"mx.state.changed\",\n} as const;\n\n/**\n * 🚨 `mx.meeting.transcript` and `mx.meeting.note` WERE declared here and\n * emitted and subscribed by nothing — a broadcast fallback that existed only as\n * a name (MRI-A10 found it; feedback `b25db4d4`). They are gone. The thing they\n * described is real now and lives somewhere better: the platform publishes the\n * durable rows into the LIVEKIT ROOM under `matrx.meet.*` (`core/room-events.ts`,\n * MRI-A12), where a session-less guest's room token is the authority. Sending\n * them over a Supabase broadcast channel would have required the guest to hold a\n * Supabase connection, which is the exact thing they do not have.\n */\n\nexport type MeetEvent = (typeof MEET_EVENTS)[keyof typeof MEET_EVENTS];\n","/**\n * THE JOIN GATE'S REASON CODES — ONE VOCABULARY (CORE-DESIGN §2.5).\n *\n * aidream's `services/meet/errors.py` (`JOIN_REFUSAL_REASONS`) emits exactly these codes and a\n * server test fails if the two lists differ; the consuming app decides every refusal screen by\n * one of them — never by the HTTP status and never by matching message text. Skins own the\n * words; the phase renders `refused:<reason>`, except `ended`, which renders the `ended` phase.\n */\nexport const JOIN_REFUSAL_REASONS = [\n \"not_found\",\n \"cancelled\",\n \"ended\",\n \"expired\",\n \"too_early\",\n \"sign_in_required\",\n \"wrong_account\",\n \"not_invited\",\n \"locked\",\n \"removed\",\n \"lobby_rejects\",\n \"meeting_full\",\n \"platform_capacity\",\n \"verification_required\",\n \"registration_required\",\n] as const;\n\nexport type JoinRefusalReason = (typeof JOIN_REFUSAL_REASONS)[number];\n\n/** Is `value` one of the codes the join gate speaks? */\nexport function isJoinRefusalReason(value: unknown): value is JoinRefusalReason {\n return typeof value === \"string\" && (JOIN_REFUSAL_REASONS as readonly string[]).includes(value);\n}\n","/**\n * THE PHASE MACHINE — the one place a meeting tab's top-level phase changes (CORE-DESIGN §3.1,\n * step S6a).\n *\n * The values ARE the observation contract's `data-meet-phase` column (HARNESS-CONTRACT §2), so\n * the core renders its own state with no translation layer. Every change goes through\n * `nextPhase(from, event)`: an explicit table of which event may move which phase where. An\n * event the table does not allow from the current phase is REFUSED (the store keeps the phase\n * and reports it) — never applied.\n *\n * 🚨 THE INVARIANT (guard: `phase.test.ts`, `in-call-phase-guard.test.ts`): nothing enters\n * `prejoin` except `resolving` (the link resolved) or the person's own click. In-call outcomes —\n * share, devices, recording, captions, chat, collab, moderation — live in REGIONS and have no\n * event here at all, so the MD-01 class (\"a cancelled share picker threw a live meeting back to\n * Ready to join?\") cannot recur: from `in_call` the only ways out are the exit-reason\n * resolution (§3.4), the person's own Leave, and the meeting ending.\n *\n * 1:1 calls run on this same machine (§3.6): ringing is a pre-room phase set, and an answered\n * call goes `joining` → `in_call` exactly like a meeting.\n */\n\nimport { isJoinRefusalReason, type JoinRefusalReason } from \"./reasons\";\nimport type { AdmissionState } from \"./types\";\n\n/** Every gate reason except `ended`, which renders the `ended` phase (contract §2). */\nexport type RefusedReason = Exclude<JoinRefusalReason, \"ended\">;\n\n/** Where the person is (HARNESS-CONTRACT §2, verbatim). */\nexport type MeetPhase =\n | \"resolving\"\n | \"unsupported\"\n | `refused:${RefusedReason}`\n | \"guest_name\"\n | \"prejoin\"\n | \"joining\"\n | \"waiting:host_not_started\"\n | \"waiting:knocking\"\n | \"denied\"\n | \"knock_expired\"\n | \"in_call\"\n | \"disconnected\"\n | \"left\"\n | \"removed\"\n | \"superseded\"\n | \"inactive\"\n | \"ended\"\n | \"record\"\n | \"ringing_out\"\n | \"ringing_in\"\n | \"call_declined\"\n | \"call_missed\"\n | \"call_busy\"\n | \"answered_elsewhere\";\n\n/** The phases a §3.4 exit-reason resolution can land on. */\nexport type ExitPhase = \"removed\" | \"superseded\" | \"inactive\" | \"ended\" | \"disconnected\" | \"waiting:knocking\";\n\n/** How a 1:1 call ended before it connected (§3.6). */\nexport type CallOutcomePhase = \"call_declined\" | \"call_missed\" | \"call_busy\" | \"answered_elsewhere\";\n\n/**\n * Every event that can move the phase. `click: true` marks the ones only the person's own\n * press may send — the guard test reads it.\n */\nexport type PhaseEvent =\n /** The link resolved on page load / mount: `resolving` → `prejoin` (first entry). */\n | { readonly type: \"resolved\" }\n /** Capability gate failed: this browser cannot join. */\n | { readonly type: \"unsupported\" }\n /**\n * The link's door said, BEFORE any Join, that this run cannot be joined (`ended`, `cancelled`):\n * `resolving` → that screen, never the pre-join with a Join button that will be refused.\n */\n | { readonly type: \"link_state\"; readonly reason: string | null }\n /** The person pressed Join / Rejoin / Ask again. */\n | { readonly type: \"join\"; readonly click: true }\n /**\n * S6b: the connection supervisor rejoins by itself from `disconnected` once the network and our\n * servers answer (`auto_rejoin`). Not a click — and it can only ever lead to `joining`.\n */\n | { readonly type: \"auto_rejoin\" }\n /**\n * S6b: the page reloaded while this tab was in (or dropped from) the call — Chrome's offline\n * page reloading, a refresh — so `resolving` goes straight back to `joining`, never pre-join.\n */\n | { readonly type: \"resumed\" }\n /** The person chose to go back to the device check from `left` / `disconnected`. */\n | { readonly type: \"back_to_prejoin\"; readonly click: true }\n /** The person pressed Try again on a refusal: look the link up again. */\n | { readonly type: \"retry\"; readonly click: true }\n /** The server said wait (or decided a wait): the admission row's own word. */\n | { readonly type: \"admission\"; readonly admission: AdmissionState }\n /** Admitted from the waiting room: the core mints the real pass and joins. */\n | { readonly type: \"admitted\" }\n /** LiveKit said connected. */\n | { readonly type: \"connected\" }\n /**\n * S7: the server answered the join with a QUESTION — this person is already in the meeting on\n * another tab or device — so `joining` goes back to `prejoin`, which offers the choice.\n */\n | { readonly type: \"second_device\" }\n /** The join itself failed: a gate reason (→ `refused:*` / `ended`) or none (→ `disconnected`). */\n | { readonly type: \"join_failed\"; readonly reason: string | null }\n /** §3.4: the exit-reason resolution decided where an in-call person went. */\n | { readonly type: \"exit\"; readonly to: ExitPhase }\n /** My Leave (in the room, joining, or withdrawing from the waiting room). */\n | { readonly type: \"leave\" }\n /** The meeting (or this run of it) is over, by any carrier. */\n | { readonly type: \"ended\" }\n /** §3.6: I placed a call / a call rings me while nothing else is on screen. */\n | { readonly type: \"ring_out\" }\n | { readonly type: \"ring_in\" }\n /** §3.6: an answered call (by me, or my callee) — join its room. */\n | { readonly type: \"call_answered\" }\n /** §3.6: the call ended before it connected. */\n | { readonly type: \"call_outcome\"; readonly outcome: CallOutcomePhase }\n /**\n * Back to the start: a different meeting mounted or the call surface was dismissed (refused\n * from a live phase — leave first). `teardown: true` is the engine's own dispose: it has\n * already closed the connection, so nothing live remains to protect and the reset applies from\n * any phase.\n */\n | { readonly type: \"reset\"; readonly teardown?: true };\n\nexport type PhaseEventType = PhaseEvent[\"type\"];\n\n/** Phases in which this tab holds (or is getting) a live media connection. */\nexport const LIVE_PHASES: ReadonlySet<MeetPhase> = new Set<MeetPhase>([\"joining\", \"in_call\"]);\n\n/** The waiting-room phase set (the core reads the server's state while in one of these). */\nexport const WAITING_PHASES: ReadonlySet<MeetPhase> = new Set<MeetPhase>([\n \"waiting:host_not_started\",\n \"waiting:knocking\",\n \"denied\",\n \"knock_expired\",\n]);\n\n/** A phase the person can press Join / Rejoin from. */\nconst JOINABLE_FROM: ReadonlySet<MeetPhase> = new Set<MeetPhase>([\n \"prejoin\",\n \"left\",\n \"disconnected\",\n \"superseded\",\n \"inactive\",\n \"denied\",\n \"knock_expired\",\n // A refused knock whose rule lets the person ask again (a cooldown that ran out): the machine\n // allows the press; the server's gate decides whether it knocks or is refused again.\n \"refused:lobby_rejects\",\n]);\n\n/**\n * Refusals that lift by themselves or by a host's act (the lock opens, the window opens, a seat\n * frees): Try again re-asks the gate with the same request. Every other refusal stays a refusal.\n */\nexport const RETRYABLE_REFUSALS: ReadonlySet<MeetPhase> = new Set<MeetPhase>([\n \"refused:locked\",\n \"refused:too_early\",\n \"refused:meeting_full\",\n \"refused:platform_capacity\",\n]);\n\n/** A call that never connected has no meeting to end; an ended meeting stays ended. */\nconst NOT_ENDABLE: ReadonlySet<MeetPhase> = new Set<MeetPhase>([\n \"ended\",\n \"record\",\n \"unsupported\",\n \"ringing_out\",\n \"ringing_in\",\n \"call_declined\",\n \"call_missed\",\n \"call_busy\",\n \"answered_elsewhere\",\n]);\n\nconst RINGING: ReadonlySet<MeetPhase> = new Set<MeetPhase>([\"ringing_out\", \"ringing_in\"]);\n\n/** Nothing live, nothing pending: a call may take the screen from here. */\nexport const IDLE_PHASES: ReadonlySet<MeetPhase> = new Set<MeetPhase>([\n \"resolving\",\n \"left\",\n \"call_declined\",\n \"call_missed\",\n \"call_busy\",\n \"answered_elsewhere\",\n]);\n\nexport function isLivePhase(phase: MeetPhase): boolean {\n return LIVE_PHASES.has(phase);\n}\n\nexport function isWaitingPhase(phase: MeetPhase): boolean {\n return WAITING_PHASES.has(phase);\n}\n\nexport function isRefusedPhase(phase: MeetPhase): phase is `refused:${RefusedReason}` {\n return phase.startsWith(\"refused:\");\n}\n\n/** The reason a refused / terminal phase carries (`data-meet-reason`). */\nexport function reasonOf(phase: MeetPhase): string | null {\n return isRefusedPhase(phase) ? phase.slice(\"refused:\".length) : null;\n}\n\n/** A gate reason → the phase it renders. `ended` is the ended phase, never `refused:ended`. */\nexport function refusalPhase(reason: string | null): MeetPhase {\n if (reason === null || !isJoinRefusalReason(reason)) return \"disconnected\";\n return reason === \"ended\" ? \"ended\" : (`refused:${reason}` as const);\n}\n\n/** The server's admission word → the waiting phase it renders (§3.1, S4). */\nexport function admissionPhase(admission: AdmissionState): MeetPhase | null {\n switch (admission) {\n case \"waiting_host\":\n return \"waiting:host_not_started\";\n case \"knocking\":\n return \"waiting:knocking\";\n case \"denied\":\n return \"denied\";\n case \"expired\":\n return \"knock_expired\";\n case \"withdrawn\":\n return \"left\";\n case \"ended\":\n return \"ended\";\n case \"removed\":\n return \"refused:removed\";\n default:\n // `admitted` is never a waiting phase: it is a join (`admitted` event).\n return null;\n }\n}\n\n/**\n * THE TABLE. Returns the next phase, or null when `event` may not move `from` (refused).\n * Pure — the store applies it, and the guard tests enumerate it.\n */\nexport function nextPhase(from: MeetPhase, event: PhaseEvent): MeetPhase | null {\n switch (event.type) {\n case \"resolved\":\n return from === \"resolving\" ? \"prejoin\" : null;\n case \"unsupported\":\n return from === \"resolving\" ? \"unsupported\" : null;\n case \"link_state\":\n return from === \"resolving\" && event.reason !== null && isJoinRefusalReason(event.reason)\n ? refusalPhase(event.reason)\n : null;\n case \"join\":\n // A refusal's Try again re-asks the gate with the same request (CORE-DESIGN §11\n // `meeting-locked-join`, `before-start-early-entry`): the server decides again.\n return JOINABLE_FROM.has(from) || RETRYABLE_REFUSALS.has(from) ? \"joining\" : null;\n case \"auto_rejoin\":\n return from === \"disconnected\" ? \"joining\" : null;\n case \"resumed\":\n return from === \"resolving\" ? \"joining\" : null;\n case \"back_to_prejoin\":\n return from === \"left\" || from === \"disconnected\" ? \"prejoin\" : null;\n case \"retry\":\n return isRefusedPhase(from) || from === \"unsupported\" ? \"resolving\" : null;\n case \"admission\": {\n // The server's word on a wait applies while joining or waiting — never to a live call\n // (being sent back to waiting from the room is an EXIT, resolved by §3.4).\n if (from !== \"joining\" && !WAITING_PHASES.has(from)) return null;\n return admissionPhase(event.admission);\n }\n case \"admitted\":\n return WAITING_PHASES.has(from) ? \"joining\" : null;\n case \"connected\":\n return from === \"joining\" ? \"in_call\" : null;\n case \"second_device\":\n return from === \"joining\" ? \"prejoin\" : null;\n case \"join_failed\":\n // ONLY a join can fail the join. A failure reported while in the room is not this event.\n return from === \"joining\" ? refusalPhase(event.reason) : null;\n case \"exit\":\n return from === \"in_call\" || from === \"joining\" ? event.to : null;\n case \"leave\":\n return from === \"in_call\" || from === \"joining\" || WAITING_PHASES.has(from) ? \"left\" : null;\n case \"ended\":\n return NOT_ENDABLE.has(from) ? null : \"ended\";\n case \"ring_out\":\n return IDLE_PHASES.has(from) || from === \"prejoin\" ? \"ringing_out\" : null;\n case \"ring_in\":\n return IDLE_PHASES.has(from) ? \"ringing_in\" : null;\n case \"call_answered\":\n return RINGING.has(from) ? \"joining\" : null;\n case \"call_outcome\":\n return RINGING.has(from) ? event.outcome : null;\n case \"reset\":\n // Never out from under a live connection: leave first.\n return LIVE_PHASES.has(from) && event.teardown !== true ? null : \"resolving\";\n }\n}\n\n/* ------------------------------------------------------- §3.4 exit-reason resolution (ER) */\n\n/** What the server's state read (`GET /v1/meet/state?connection=`) says about this exit. */\nexport interface ExitServerWord {\n /** `meet_connections.exit_reason` of THIS connection, if the server recorded one. */\n readonly exitReason: string | null;\n /** The run's phase: `ended` means the meeting (this run) is over. */\n readonly sessionPhase: string | null;\n readonly meetingPhase?: string | null;\n}\n\nexport interface ExitInput {\n /** The server's word; `null` = the state read failed (offline / unreachable); undefined = no ST for this room (a call). */\n readonly server: ExitServerWord | null | undefined;\n /** LiveKit's `DisconnectReason` name (`PARTICIPANT_REMOVED`, `DUPLICATE_IDENTITY`, …), or null. */\n readonly hint: string | null;\n /** The page was frozen (Page Lifecycle) when the connection dropped. */\n readonly frozen?: boolean;\n}\n\nexport interface ExitResolution {\n readonly to: ExitPhase;\n /** `data-meet-reason` for the exit (null when the phase says it all). */\n readonly reason: string | null;\n /** `data-meet-connection` to show beside `disconnected`. */\n readonly connection: \"stable\" | \"offline\" | \"server_unreachable\";\n /** A notice code the skin renders with it (`sent-to-waiting`), when one applies. */\n readonly notice: string | null;\n}\n\n/** §3.4, row by row. Pure: the engine gathers the inputs, this decides. */\nexport function resolveExit(input: ExitInput): ExitResolution {\n const { server, hint } = input;\n if (server === null) {\n return { to: \"disconnected\", reason: null, connection: \"server_unreachable\", notice: null };\n }\n const word = server?.exitReason ?? null;\n if (server !== undefined && (server.sessionPhase === \"ended\" || server.meetingPhase === \"ended\" || word === \"ended\" || word === \"time_limit\")) {\n return { to: \"ended\", reason: word === \"time_limit\" ? \"time_limit\" : \"ended\", connection: \"stable\", notice: null };\n }\n if (word === \"removed\") return { to: \"removed\", reason: \"removed\", connection: \"stable\", notice: null };\n if (word === \"displaced\") return { to: \"superseded\", reason: \"displaced\", connection: \"stable\", notice: null };\n if (word === \"sent_to_waiting\") {\n return { to: \"waiting:knocking\", reason: \"sent_to_waiting\", connection: \"stable\", notice: \"sent-to-waiting\" };\n }\n if (hint === \"DUPLICATE_IDENTITY\") return { to: \"superseded\", reason: \"displaced\", connection: \"stable\", notice: null };\n if (hint === \"PARTICIPANT_REMOVED\" && word === null && server === undefined) {\n // A call room has no state read: LiveKit's own word is all there is.\n return { to: \"removed\", reason: \"removed\", connection: \"stable\", notice: null };\n }\n if (hint === \"ROOM_DELETED\" && server === undefined) {\n return { to: \"ended\", reason: \"ended\", connection: \"stable\", notice: null };\n }\n if (input.frozen === true) return { to: \"inactive\", reason: \"inactive\", connection: \"stable\", notice: null };\n return { to: \"disconnected\", reason: null, connection: \"stable\", notice: null };\n}\n","/**\n * THE MEET DOMAIN MODEL.\n *\n * D3: an audio call, a video call, and a meeting are ONE thing — a room. An\n * audio call is a room with video off; a meeting is a room plus scheduling,\n * lobby and host metadata. That ruling is expressed here as ONE `MeetRoom`\n * identity carried by both `/calls` and `/meetings`, not as three parallel\n * hierarchies that drift.\n *\n * Identity-bearing values are BRANDED (C22). A `MeetingId` and a `RoomName` are\n * both strings and are NOT interchangeable — passing the meeting's row id where\n * LiveKit wants the room name produces a room nobody else joins, which looks\n * exactly like \"the other person's camera is broken\".\n */\n\ndeclare const brand: unique symbol;\ntype Brand<T, B extends string> = T & { readonly [brand]: B };\n\nexport type MeetingId = Brand<string, \"MeetingId\">;\nexport type CallId = Brand<string, \"CallId\">;\n/** The LiveKit room name. THE MEDIA ROOM KEY — durable for a meeting link. */\nexport type RoomName = Brand<string, \"RoomName\">;\nexport type UserId = Brand<string, \"UserId\">;\nexport type OrganizationId = Brand<string, \"OrganizationId\">;\n/** LiveKit participant identity. `user:<uuid>` or `guest:<opaque>`. */\nexport type ParticipantIdentity = Brand<string, \"ParticipantIdentity\">;\n\nexport const asMeetingId = (value: string): MeetingId => value as MeetingId;\nexport const asCallId = (value: string): CallId => value as CallId;\nexport const asRoomName = (value: string): RoomName => value as RoomName;\nexport const asUserId = (value: string): UserId => value as UserId;\nexport const asOrganizationId = (value: string): OrganizationId =>\n value as OrganizationId;\nexport const asParticipantIdentity = (value: string): ParticipantIdentity =>\n value as ParticipantIdentity;\n\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\nexport type JsonObject = { [key: string]: JsonValue };\n\n/** Audio-only or with video. A call escalates; it never becomes a new call. */\nexport type CallMode = \"audio\" | \"video\";\n\n/**\n * WHO IS IN THE ROOM, as far as authorization is concerned.\n *\n * A guest has no `userId` — by construction, not by convention. Everything that\n * requires authentication (host powers, recording, starting a call) takes a\n * `userId` and therefore cannot compile against a guest identity (D6).\n */\nexport interface MeetIdentity {\n readonly userId: UserId | null;\n readonly organizationId: OrganizationId;\n readonly displayName: string;\n readonly avatarUrl: string | null;\n /** Stable per-device value so a reconnect keeps ONE LiveKit identity. */\n readonly deviceId: string;\n}\n\nexport function isGuest(identity: MeetIdentity): boolean {\n return identity.userId === null;\n}\n\n/**\n * WHO SOMEBODY IS IN A ROOM — the SERVER's word, never this client's (MRI-A11).\n *\n * THE ROLE VOCABULARY (CORE-DESIGN §2.1, S3): `host · cohost · presenter · participant ·\n * viewer`. `host` is `meet_meetings.host_user_id`; `cohost` is a real `iam` grant on the\n * meeting. Being a guest is a STANDING (`ParticipantStanding`), never a role: a guest is a\n * `participant` whose standing is `guest`. Pinned against aidream's `config.py` by\n * `test_meet_service.py`.\n */\nexport type ParticipantRole = \"host\" | \"cohost\" | \"presenter\" | \"participant\" | \"viewer\";\n\n/**\n * Where a person stands with the meeting, beside the role: `member` of its organization,\n * `invited` (shared with them), `outsider` (signed in, followed the link), `guest` (no\n * session). The server mints it (attribute `matrx.participant.standing`).\n */\nexport type ParticipantStanding = \"member\" | \"invited\" | \"outsider\" | \"guest\";\n\n/** Rank, so \"can this actor do that to that participant\" is one comparison. */\nexport const ROLE_RANK: Readonly<Record<ParticipantRole, number>> = {\n host: 4,\n cohost: 3,\n presenter: 2,\n participant: 1,\n viewer: 0,\n};\n\n/** Does this standing follow the link-holder (guest) rules? `outsider` and `guest`. */\nexport function isLinkFollower(standing: ParticipantStanding | null | undefined): boolean {\n return standing === \"guest\" || standing === \"outsider\";\n}\n\n/**\n * 🚨 THE ONE PREDICATE FOR \"DOES THIS PERSON HOLD HOST POWERS\".\n *\n * Every host control in this package answers this question and no other. It is\n * a pure function of a SERVER-minted role, so there is no path by which a\n * client's own state can grant it anything.\n */\nexport function isHostRole(role: ParticipantRole): boolean {\n return role === \"host\" || role === \"cohost\";\n}\n\n/**\n * THE SILENT OBSERVER (HR-360 wave 2, MD-16) — the watch-only role `viewer` the core design\n * already froze: publishes nothing, has no tile on anyone's stage, and is listed in People\n * as \"Observing\". The server decides it at the join gate; nothing here grants it.\n */\nexport function isObserverRole(role: ParticipantRole | null | undefined): boolean {\n return role === \"viewer\";\n}\n\n/** Who sees the \"Observing\" line in People — knob `meet.observers_visible_to`. */\nexport type ObserversVisibleTo = \"everyone\" | \"hosts\";\n\n/**\n * THE LOBBY LADDER (D6). A guest link-follower starts `knocking`; the host\n * admits or denies. An authenticated member of the meeting's org is `admitted`\n * on arrival unless the meeting is locked.\n */\nexport type AdmissionState =\n | \"waiting_host\"\n | \"knocking\"\n | \"admitted\"\n | \"denied\"\n | \"expired\"\n | \"withdrawn\"\n | \"removed\"\n | \"ended\";\n\n/** States a person is still WAITING in (CORE-DESIGN §2.2). */\nexport const WAITING_ADMISSION_STATES: readonly AdmissionState[] = [\"waiting_host\", \"knocking\"];\n\nexport interface LobbyEntry {\n readonly identity: ParticipantIdentity;\n readonly displayName: string;\n readonly avatarUrl: string | null;\n readonly userId: UserId | null;\n readonly state: AdmissionState;\n readonly knockedAt: string;\n /** When an unanswered knock lapses (`knock_timeout_minutes`); null = never. */\n readonly knockExpiresAt?: string | null | undefined;\n readonly decidedAt: string | null;\n readonly decidedBy: UserId | null;\n /**\n * The server's flag on this knock (`mod-removed-bypass`): `previously_removed` when its coarse\n * signals match someone a host removed from this meeting. Advice for the host, never a block.\n */\n readonly flag?: string | null | undefined;\n}\n\nexport type ConnectionQuality = \"excellent\" | \"good\" | \"poor\" | \"lost\" | \"unknown\";\n\n/** One person in the room, as the UI needs them. Media state is a FACT here. */\nexport interface MeetParticipant {\n readonly identity: ParticipantIdentity;\n readonly userId: UserId | null;\n readonly displayName: string;\n readonly avatarUrl: string | null;\n readonly role: ParticipantRole;\n /** Where they stand with the meeting (server-minted); null when the server sent none. */\n readonly standing?: ParticipantStanding | null;\n /**\n * Whether the SERVER granted this participant the power to start a recording\n * (MRI-A11). It is not `role === \"host\"`: a meeting whose recording policy is\n * `always-on` is the server's to start and a host may not touch it, and a\n * `disabled` policy grants it to nobody. The client renders this; it never\n * recomputes the policy.\n */\n readonly canRecord: boolean;\n readonly isLocal: boolean;\n readonly isAgent: boolean;\n readonly micEnabled: boolean;\n readonly cameraEnabled: boolean;\n readonly screenSharing: boolean;\n readonly speaking: boolean;\n readonly handRaised: boolean;\n /**\n * When the hand went up (epoch ms), or null. The HAND QUEUE is ordered by it —\n * first raised, first called on (Meet wave 2). Optional so a roster built by a\n * host before 0.8 still type-checks.\n */\n readonly handRaisedAt?: number | null | undefined;\n readonly quality: ConnectionQuality;\n /**\n * S6b (`net-others-see-drop`): this person dropped without leaving and is held as a\n * `reconnecting` tile for `reconnect_grace_seconds`. Absent/false = a live participant.\n */\n readonly reconnecting?: boolean | undefined;\n /**\n * P1 `net-video-auto-off`: this person's camera is paused by their network (to keep their\n * audio), not by their choice — from their own heartbeat. Absent/false = not paused.\n */\n readonly videoPaused?: boolean | undefined;\n readonly joinedAt: string;\n /**\n * LiveKit participant attributes, verbatim. The note-taker announces itself\n * here (`matrx.agent.consent_notice`), which is how a consent notice can be\n * rendered for a participant whose token metadata this client cannot read.\n */\n readonly attributes: Readonly<Record<string, string>>;\n}\n\nexport type MeetingKind = \"instant\" | \"scheduled\" | \"recurring\";\n\n/**\n * WHAT THE LINK'S CURRENT RUN IS, read through the no-pass link door (`meet_link_state`) so the\n * entry screen can say it before any Join (CORE-DESIGN §2.3). One vocabulary: `reason` is a\n * join-gate reason code (`ended` · `cancelled`) or null; no private data rides it.\n */\nexport interface LinkRunState {\n readonly state: \"open\" | \"not_started\" | \"ended\" | \"cancelled\";\n readonly reason: \"ended\" | \"cancelled\" | null;\n /** `ended`: the next run's start (null = the meeting can never run again). `not_started`: when it starts. */\n readonly nextStart: string | null;\n}\n\nexport interface MeetingRecord {\n readonly id: MeetingId;\n readonly organizationId: OrganizationId;\n /** Durable: the same room name before, during, and after (R3). */\n readonly roomName: RoomName;\n /** The public slug in a meeting link. Durable for recurring meetings. */\n readonly slug: string;\n readonly title: string;\n readonly kind: MeetingKind;\n readonly hostUserId: UserId;\n readonly scheduledFor: string | null;\n readonly scheduledDurationMinutes: number | null;\n readonly startedAt: string | null;\n readonly endedAt: string | null;\n /** Present only on a row resolved through the link door; absent = unknown (never guessed). */\n readonly linkRun?: LinkRunState | undefined;\n readonly locked: boolean;\n readonly lobbyEnabled: boolean;\n readonly recordingPolicy: RecordingPolicy;\n readonly aiEnabled: boolean;\n readonly metadata: JsonObject;\n /*\n * ── MEET WAVE 1 — management (0.8). Always present on a row read from the\n * database by this package; optional in the type only so a hand-built\n * fixture from before 0.8 still compiles.\n */\n /** IANA zone the meeting's wall-clock time is authored in (`America/Los_Angeles`). */\n readonly timeZone?: string;\n /** Description / agenda text. */\n readonly agenda?: string | null;\n /** RFC 5545 RRULE subset — one row and one durable link per series. */\n readonly recurrenceRule?: string | null;\n /** Signed-in members and invitees may enter before a host arrives. */\n readonly joinBeforeHost?: boolean;\n /** Cancel is a STATE: the link still resolves and says so; nobody can join. */\n readonly cancelledAt?: string | null;\n readonly cancellationReason?: string | null;\n /** Archived (soft deleted). Restorable by the host or a co-host. */\n readonly deletedAt?: string | null;\n /** iCalendar SEQUENCE — bumped by the database on every reschedule and on cancel. */\n readonly calendarSequence?: number;\n /** Row version, for the compare-and-swap in `updateMeeting`. */\n readonly version?: number;\n}\n\n/* ─────────────────────────────── MEET WAVE 1 — management vocabulary (0.8) */\n\n/** An invitee's answer. `needs_action` = not answered yet (iCalendar NEEDS-ACTION). */\nexport type RsvpState = \"needs_action\" | \"accepted\" | \"declined\" | \"tentative\";\n/** The answers a person can give. */\nexport type RsvpAnswer = Exclude<RsvpState, \"needs_action\">;\n/** A co-host holds host moderation through a real `iam` admin grant on the meeting. */\nexport type InviteeRole = \"invitee\" | \"cohost\";\n\n/** One person a meeting was sent to (`communication.meet_invitees`). */\nexport interface MeetingInvitee {\n readonly id: string;\n readonly meetingId: MeetingId;\n /** Set when the person has an account — they skip the waiting room and answer in-app. */\n readonly userId: UserId | null;\n readonly email: string | null;\n readonly displayName: string | null;\n readonly role: InviteeRole;\n readonly rsvpState: RsvpState;\n readonly respondedAt: string | null;\n readonly rsvpNote: string | null;\n /** The calendar_sequence of the last invitation they were sent; null = never told. */\n readonly lastSentSequence: number | null;\n readonly lastSentAt: string | null;\n}\n\n/** Who to invite: an account (`userId`) or an address (`email`), or both. */\nexport interface InviteeInput {\n readonly userId?: UserId | undefined;\n readonly email?: string | undefined;\n readonly displayName?: string | undefined;\n /** Omit to keep an existing invitee's role; `cohost` needs the host or a co-host. */\n readonly role?: InviteeRole | undefined;\n}\n\n/** Where one occurrence of a meeting stands. */\nexport type OccurrenceState = \"scheduled\" | \"moved\" | \"cancelled\";\n\n/** One occurrence — for a one-off meeting, the meeting itself. */\nexport interface MeetingOccurrence {\n readonly meetingId: MeetingId;\n /** The start the rule generates (iCalendar RECURRENCE-ID). The key for exceptions. */\n readonly originalStart: string;\n /** When it actually happens (differs from `originalStart` only when `moved`). */\n readonly occurrenceStart: string;\n readonly durationMinutes: number | null;\n readonly state: OccurrenceState;\n readonly sequence: number;\n}\n\n/** One row of the Upcoming list — an occurrence plus what the list shows about its meeting. */\nexport interface UpcomingOccurrence extends MeetingOccurrence {\n readonly organizationId: OrganizationId;\n readonly title: string;\n readonly slug: string;\n readonly kind: MeetingKind;\n readonly hostUserId: UserId;\n readonly timeZone: string;\n readonly meetingCancelled: boolean;\n /** The signed-in reader's own answer, when they are an invitee. */\n readonly myRsvp: RsvpState | null;\n /** `host`, `cohost` or `invitee` — null when the reader simply can see it (org member). */\n readonly myRole: \"host\" | InviteeRole | null;\n}\n\n/** An exception row (`communication.meet_occurrence_overrides`). */\nexport interface OccurrenceOverride {\n readonly id: string;\n readonly meetingId: MeetingId;\n readonly originalStart: string;\n readonly state: \"cancelled\" | \"moved\";\n readonly newStart: string | null;\n readonly newDurationMinutes: number | null;\n readonly reason: string | null;\n readonly calendarSequence: number;\n /** Set when the exception was withdrawn (`restore`) and the occurrence is back on schedule. */\n readonly deletedAt: string | null;\n}\n\n/** Settings a meeting carries; unset ones come from the person's / organization's knobs. */\nexport interface MeetingSettings {\n readonly lobbyEnabled?: boolean | undefined;\n readonly recordingPolicy?: RecordingPolicy | undefined;\n readonly aiEnabled?: boolean | undefined;\n readonly joinBeforeHost?: boolean | undefined;\n}\n\n/** A partial edit. `null` clears a clearable field (agenda, recurrence rule, time). */\nexport interface MeetingChanges extends MeetingSettings {\n readonly title?: string | undefined;\n readonly agenda?: string | null | undefined;\n readonly timeZone?: string | undefined;\n readonly scheduledFor?: string | null | undefined;\n readonly scheduledDurationMinutes?: number | null | undefined;\n readonly recurrenceRule?: string | null | undefined;\n /**\n * When the SERIES moves (start, rule or zone), carry its per-date changes\n * (cancelled or moved occurrences) to the new schedule instead of archiving\n * them — Google Calendar's and Outlook's \"keep changes to individual events\".\n * Absent or false archives them (restorable). Ask the person; never decide.\n */\n readonly keepExceptions?: boolean | undefined;\n}\n\n/** What the emailed RSVP link shows and answers (no account needed). */\nexport type RsvpInvitation =\n | {\n readonly ok: true;\n readonly meeting: {\n readonly title: string;\n readonly slug: string;\n readonly scheduledFor: string | null;\n readonly durationMinutes: number | null;\n readonly timeZone: string;\n readonly agenda: string | null;\n readonly recurrenceRule: string | null;\n readonly cancelledAt: string | null;\n readonly cancellationReason: string | null;\n readonly endedAt: string | null;\n };\n readonly invitee: {\n readonly displayName: string | null;\n readonly email: string | null;\n readonly rsvpState: RsvpState;\n readonly respondedAt: string | null;\n };\n }\n | { readonly ok: false; readonly message: string };\n\n/** Org-configurable, never hardcoded taste (Law 6 / \"opinions become knobs\"). */\nexport type RecordingPolicy = \"disabled\" | \"host-controlled\" | \"always-on\";\n\nexport type RecordingState =\n | \"idle\"\n | \"starting\"\n | \"recording\"\n | \"stopping\"\n | \"processing\"\n | \"available\"\n /** Bytes were purged under retention; notes and transcript remain durable. */\n | \"expired\"\n | \"failed\";\n\n/**\n * WHICH transport last said what the recording is doing (MRI-A16).\n *\n * `local` — this browser's own optimistic `starting`/`stopping`, which is all\n * the indicator ever had through 0.4.7, and the reason it never went solid.\n * `room` — the room's replicated state (metadata), the ONLY feed a guest has.\n * `record` — the durable `meet_recordings` row. It outranks both (D16).\n */\nexport type RecordingSource = \"local\" | \"room\" | \"record\";\n\n/** The server's durable retention receipt; never inferred from a browser clock. */\nexport interface RecordingRetention {\n readonly operationId: string;\n readonly policyId: string;\n readonly policyUpdatedAt: string;\n readonly phase: \"warning\" | \"expired\";\n readonly dueAt: string;\n readonly expiredAt: string | null;\n}\n\nexport interface RecordingRecord {\n readonly id: string;\n readonly meetingId: MeetingId;\n readonly state: RecordingState;\n readonly startedAt: string | null;\n readonly endedAt: string | null;\n readonly startedBy: UserId | null;\n /** The PLATFORM file id. Never a provider URL (R3). */\n readonly fileId: string | null;\n readonly failureReason: string | null;\n /** Present when central retention warned or purged this recording. */\n readonly retention: RecordingRetention | null;\n}\n\n/**\n * WHERE A TRANSCRIPT LINE CAME FROM — and therefore how much it is worth.\n *\n * D16: `lk.transcription` is published by a room participant and its\n * `sender_identity` is client-supplied and unvalidated by LiveKit, so a caption\n * is a HINT — good enough to read live, never good enough to reason from. A\n * `record` line is a durable `meet_transcript_segments` row written server-side\n * by the note-taker: that is the transcript. A record always beats a caption;\n * a caption never overwrites a record.\n */\nexport type TranscriptSource = \"caption\" | \"record\";\n\nexport interface TranscriptSegment {\n readonly id: string;\n readonly meetingId: MeetingId;\n readonly identity: ParticipantIdentity;\n readonly speaker: string;\n readonly text: string;\n readonly startedAt: string;\n /** Interim text is replaced in place by its final; it is never appended. */\n readonly final: boolean;\n readonly source: TranscriptSource;\n /**\n * The `lk.segment_id` this line was captioned under, when known. A durable\n * row carries it too (the worker writes it into the row's metadata), which is\n * what lets a record REPLACE the caption it supersedes rather than doubling it.\n */\n readonly captionId: string | null;\n}\n\n/**\n * SOMEBODY WHO WAS IN THE MEETING — the durable `meet_participants` row, which\n * outlives the LiveKit room (MRI-D2). `MeetParticipant` above is a live tile;\n * THIS is the attendance record a person reads a week later.\n */\nexport interface MeetingAttendee {\n readonly id: string;\n readonly meetingId: MeetingId;\n readonly identity: ParticipantIdentity;\n readonly displayName: string;\n readonly avatarUrl: string | null;\n readonly userId: UserId | null;\n readonly role: ParticipantRole;\n readonly admissionState: AdmissionState;\n /** When this person knocked, and when an unanswered knock lapses (null = never). */\n readonly knockedAt?: string | null | undefined;\n readonly knockExpiresAt?: string | null | undefined;\n /** The server's flag on a knock (`metadata.knock_flag`), e.g. `previously_removed`. */\n readonly knockFlag?: string | null | undefined;\n readonly isAgent: boolean;\n /** FIRST arrival in the room — a rejoin never moves it. */\n readonly joinedAt: string | null;\n /** Null while in the room, else the last time they left. */\n readonly leftAt: string | null;\n /**\n * Every stay in the room, oldest first (`metadata.sessions`, written by the\n * server's one attendance rule). Empty for a row recorded before stays were\n * kept; the report then falls back to `joinedAt`–`leftAt`.\n */\n readonly sessions?: readonly AttendanceStay[] | undefined;\n}\n\n/** One stay in the room: arrived, and left (null = still there or never recorded). */\nexport interface AttendanceStay {\n readonly joinedAt: string;\n readonly leftAt: string | null;\n}\n\nexport type MeetingNoteKind = \"note\" | \"decision\" | \"action-item\";\n\n/**\n * ONE ROW OF WHAT THE MEETING DECIDED. `meet_notes.kind` is a live CHECK\n * constraint of exactly three values (D5) — there is no `summary` kind and no\n * `transcript` kind — so the wrap-up summary is a `note` carrying\n * `metadata.role = \"summary\"`, and a live-window note carries `metadata.window`.\n * Both are projected here so the UI never re-reads raw metadata.\n */\nexport interface MeetingNote {\n readonly id: string;\n readonly meetingId: MeetingId;\n readonly text: string;\n readonly createdAt: string;\n readonly kind: MeetingNoteKind;\n /** Set ONLY when the assignee resolved to a real user (D18). */\n readonly assigneeUserId: UserId | null;\n /** Who the item is for, as a person reads it. A guest keeps their name here. */\n readonly assigneeDisplayName: string | null;\n /** `\"summary\"` for the wrap-up; `null` for everything else. */\n readonly role: \"summary\" | null;\n /** The live-notes window index this came out of, when it came from one. */\n readonly window: number | null;\n /**\n * 🚨 THE WRAP-UP IS THE AUTHORITATIVE POST-MEETING SET (D20).\n *\n * A live-notes window and the wrap-up both write decisions and action items,\n * and on production 2026-09-09 meeting `9cb-39f8-782` carried the SAME\n * decision twice — once from the note-taker at 04:43:57 and once from the\n * wrap-up at 04:47:15 — and the record view listed both. A reader cannot tell\n * a repeated decision from two decisions.\n *\n * So when the wrap-up lands, the server MARKS the window-written decisions\n * and action items it supersedes (`metadata.superseded_by` = the wrap-up's\n * run id) and never deletes them: the row is still the timeline of what was\n * known at 04:43. This field carries that marker, and the record view and the\n * assistant panel render only the rows where it is `null`.\n */\n readonly supersededBy: string | null;\n /**\n * WHICH TRANSPORT DELIVERED THIS COPY (MRI-A12). The same durable row reaches\n * a signed-in member twice — once as a `meet_notes` row over Postgres Changes\n * (`\"record\"`) and once as a `matrx.meet.note` packet on the media plane\n * (`\"room\"`), which is the ONLY copy a session-less guest can receive, because\n * `anon` holds no grant on the table (MRI-A10).\n *\n * They carry the same id and the same words, so the store renders one note.\n * Where they could ever disagree, `\"record\"` wins and `\"room\"` never overwrites\n * it — the database is the record and the packet is the courtesy copy. That is\n * the same precedence captions and segments already follow (D16).\n */\n readonly source: \"record\" | \"room\";\n}\n\n/**\n * THE WRAP-UP, ANNOUNCED (MRI-A12). The summary, decisions and action items\n * arrive as ordinary notes; this says the pass that wrote them FINISHED, which\n * is the difference between \"no summary yet\" and \"no summary is coming\".\n *\n * 🚨 It reaches a screen only while the room still exists. `POST /v1/meet/end`\n * deletes the LiveKit room and the wrap-up runs afterwards, so on the ordinary\n * ending path nobody is in the room to be told — the post-meeting record view is\n * where the summary is read. A surface must therefore treat `null` as \"not\n * announced\", never as \"not written\".\n */\nexport interface WrapUpAnnouncement {\n readonly meetingId: MeetingId;\n /** The `meet_notes` ids the pass wrote, in the order it wrote them. */\n readonly noteIds: readonly string[];\n /** How many durable transcript segments it summarized. */\n readonly segmentCount: number;\n readonly at: string;\n}\n\n/**\n * THE NOTE-TAKER'S STATE, as the meeting row records it\n * (`meet_meetings.metadata.note_taker.state`, MRI-A2). Five values and no\n * spinner: a surface renders the state it is in, or the server's refusal.\n *\n * - `absent` — nobody summoned it. The control offers to.\n * - `joining` — dispatched; it has not entered the room yet.\n * - `transcribing` — the server recorded active transcription and the exact\n * deterministic actor is present in the room.\n * - `handoff_pending` — a row that said transcribing no longer has that exact\n * actor in the room. The next state and any missed audio are not inferred.\n * - `ended` — the recorded note-taker stopped. The cause is not inferred.\n */\nexport type NoteTakerState =\n | \"absent\"\n | \"joining\"\n | \"transcribing\"\n | \"handoff_pending\"\n | \"ended\";\n\n/** The lifecycle of a 1:1 or ad-hoc group call (R2). */\nexport type CallState =\n | \"idle\"\n | \"dialing\"\n | \"ringing\"\n | \"connecting\"\n | \"active\"\n | \"declined\"\n | \"missed\"\n | \"busy\"\n | \"cancelled\"\n | \"ended\"\n | \"failed\";\n\nexport interface CallInvite {\n readonly id: CallId;\n readonly organizationId: OrganizationId;\n readonly roomName: RoomName;\n readonly mode: CallMode;\n readonly callerUserId: UserId;\n readonly callerName: string;\n readonly callerAvatarUrl: string | null;\n readonly calleeUserIds: readonly UserId[];\n /** The durable conversation the invite was delivered into, if any. */\n readonly conversationId: string | null;\n readonly createdAt: string;\n readonly expiresAt: string;\n readonly state: CallState;\n readonly declineMessage: string | null;\n}\n\n/** A person the UI can call. Deliberately minimal: hosts already have this. */\nexport interface CallablePerson {\n readonly userId: UserId;\n readonly displayName: string;\n readonly avatarUrl: string | null;\n}\n\nexport type StageLayout = \"grid\" | \"speaker\" | \"sidebar\";\n","/**\n * THE CALL CENTER — ringing, answering, and the six ways a call ends badly.\n *\n * A call reaches you on whatever surface you are on (R2), which is why this is\n * mounted by the provider and not by a call page. It is also why delivery is\n * DUAL by design, not by belt-and-braces nervousness:\n *\n * - **The durable leg** is the invite ROW plus its Postgres Changes event. It\n * survives a reload, a late-joining tab, and a dropped socket, and it is what\n * makes a missed call a fact rather than a lost packet.\n * - **The instant leg** is a broadcast on the callee's user channel, which\n * arrives without a database round trip.\n *\n * Both are deduplicated on the invite id, so a call rings ONCE however it\n * arrives. Neither is a fallback for the other: removing the durable leg loses\n * calls, and removing the instant leg makes ringing feel slow.\n *\n * 🚨 THE FOUR RULES THAT MAKE RINGING SURVIVABLE\n *\n * 1. **A ring always ends.** Every outgoing call arms a timeout that writes\n * `missed`. A ring with no terminal state is a phone that never stops.\n * 2. **Answering on one device stops every other.** The `settled` signal on\n * your OWN user channel silences the other tabs. Without it, answering on a\n * laptop leaves a phone ringing in the room.\n * 3. **Busy is answered, not ignored.** Being in an active call auto-declines\n * with `busy`, so the caller learns something instead of ringing out.\n * 4. **The state machine is a MACHINE.** Transitions are validated: a\n * `declined` call cannot become `active` because a late accept arrived.\n */\n\nimport type { ChannelHandle, RealtimeManager } from \"@ai-matrx/realtime\";\nimport { MEET_EVENTS, userTopic } from \"./channels\";\nimport { MeetError } from \"./errors\";\nimport type { MeetRepository } from \"./repository\";\nimport type { MeetStore } from \"./store\";\nimport { isLivePhase, type CallOutcomePhase } from \"./phase\";\nimport {\n asCallId,\n asRoomName,\n type CallId,\n type CallInvite,\n type CallMode,\n type CallState,\n type MeetIdentity,\n type RoomName,\n type UserId,\n} from \"./types\";\n\n/** Which states may follow which. An invalid transition throws, never silently applies. */\nconst TRANSITIONS: Readonly<Record<CallState, readonly CallState[]>> = {\n idle: [\"dialing\", \"ringing\"],\n dialing: [\"ringing\", \"connecting\", \"cancelled\", \"declined\", \"busy\", \"missed\", \"failed\"],\n ringing: [\"connecting\", \"declined\", \"busy\", \"missed\", \"cancelled\", \"failed\"],\n connecting: [\"active\", \"failed\", \"ended\"],\n active: [\"ended\", \"failed\"],\n declined: [],\n missed: [],\n busy: [],\n cancelled: [],\n ended: [],\n failed: [],\n};\n\nexport function canTransition(from: CallState, to: CallState): boolean {\n return TRANSITIONS[from].includes(to);\n}\n\nexport interface CallCenterOptions {\n manager: RealtimeManager;\n repository: MeetRepository;\n store: MeetStore;\n identity: MeetIdentity;\n /** Ring for this long before the call is `missed`. Org knob, not a constant. */\n ringTimeoutMs?: number | undefined;\n onIncoming?: ((invite: CallInvite) => void) | undefined;\n onSettled?: ((invite: CallInvite) => void) | undefined;\n /**\n * §3.6: MY outgoing call was answered — join its room on the same engine and machine\n * (`ringing_out` → `joining` → `in_call`). The provider wires this to `engine.join`.\n */\n onAnswered?: ((invite: CallInvite) => void) | undefined;\n onDiagnostic?: ((event: { level: \"info\" | \"warn\" | \"error\"; message: string; remedy?: string }) => void) | undefined;\n timers?: {\n setTimeout: (run: () => void, ms: number) => unknown;\n clearTimeout: (handle: unknown) => void;\n now: () => number;\n };\n}\n\nexport interface CallCenter {\n /** Subscribe to this user's calling channel. Call once, from the provider. */\n start(): Promise<void>;\n /** Ring someone. Returns the invite the UI shows as \"calling…\". */\n call(args: {\n people: readonly UserId[];\n mode: CallMode;\n conversationId?: string | undefined;\n }): Promise<CallInvite>;\n accept(id: CallId, mode: CallMode): Promise<CallInvite>;\n decline(id: CallId, message?: string): Promise<CallInvite>;\n cancel(id: CallId): Promise<CallInvite>;\n /** Every invite currently ringing this device. */\n incoming(): readonly CallInvite[];\n outgoing(): CallInvite | null;\n /** Feed a ring that arrived through another path (a messaging action chip). */\n ingest(invite: CallInvite): void;\n dispose(): void;\n}\n\nconst DEFAULT_RING_TIMEOUT_MS = 45_000;\n\nexport function createCallCenter(options: CallCenterOptions): CallCenter {\n const { manager, repository, identity } = options;\n const timers = options.timers ?? {\n setTimeout: (run, ms) => setTimeout(run, ms),\n clearTimeout: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),\n now: () => Date.now(),\n };\n const ringTimeoutMs = options.ringTimeoutMs ?? DEFAULT_RING_TIMEOUT_MS;\n\n const incoming = new Map<CallId, CallInvite>();\n const timeouts = new Map<CallId, unknown>();\n let outgoing: CallInvite | null = null;\n let channel: ChannelHandle | null = null;\n\n /**\n * Send-only handles on OTHER people's user channels.\n *\n * 🚨 CACHED AND CLOSED, deliberately. Since `@ai-matrx/realtime` 0.2.0 a\n * channel is ref-counted per DECLARED topic, so `manager.open(...).send(...)`\n * without a matching `close()` increments a refcount that is never released —\n * a leaked subscription per ring, forever. One handle per peer, all closed on\n * dispose.\n */\n const outbound = new Map<string, ChannelHandle>();\n\n function to(userId: UserId): ChannelHandle {\n const topic = userTopic(userId);\n const existing = outbound.get(topic);\n if (existing !== undefined) return existing;\n const opened = manager.open({ topic });\n outbound.set(topic, opened);\n return opened;\n }\n\n function report(level: \"info\" | \"warn\" | \"error\", message: string, remedy?: string): void {\n options.onDiagnostic?.(remedy === undefined ? { level, message } : { level, message, remedy });\n }\n\n function requireUser(operation: string): UserId {\n if (identity.userId === null) {\n throw new MeetError(\n \"forbidden\",\n `${operation}: a guest cannot place or receive calls`,\n \"Guests join meetings by link (D6); calling requires an account. Render the join-by-\" +\n \"link path instead of a call button for guest identities.\",\n );\n }\n return identity.userId;\n }\n\n function clearRing(id: CallId): void {\n const handle = timeouts.get(id);\n if (handle !== undefined) {\n timers.clearTimeout(handle);\n timeouts.delete(id);\n }\n }\n\n function settleLocal(invite: CallInvite): void {\n clearRing(invite.id);\n incoming.delete(invite.id);\n if (outgoing !== null && outgoing.id === invite.id) outgoing = invite;\n // §3.6 on the machine: a ring that ended here without this device answering it.\n const held = options.store.snapshot().call;\n if (held !== null && held.invite.id === invite.id && held.direction === \"incoming\") {\n const outcome: CallOutcomePhase | \"reset\" =\n invite.state === \"connecting\" || invite.state === \"active\"\n ? \"answered_elsewhere\"\n : invite.state === \"declined\"\n ? \"reset\"\n : \"call_missed\";\n const moved = outcome === \"reset\"\n ? options.store.dispatch({ type: \"reset\" })\n : options.store.dispatch({ type: \"call_outcome\", outcome });\n if (moved) options.store.patch({ call: outcome === \"reset\" ? null : { ...held, invite } });\n }\n options.onSettled?.(invite);\n }\n\n /** §3.6: the callee's answer reached the caller. */\n function settleOutgoing(invite: CallInvite): void {\n if (outgoing === null || outgoing.id !== invite.id) return;\n clearRing(invite.id);\n outgoing = invite;\n const store = options.store;\n const held = store.snapshot().call;\n if (held !== null && held.invite.id === invite.id) store.patch({ call: { ...held, invite } });\n switch (invite.state) {\n case \"connecting\":\n case \"active\":\n if (store.dispatch({ type: \"call_answered\" })) options.onAnswered?.(invite);\n break;\n case \"declined\":\n store.dispatch({ type: \"call_outcome\", outcome: \"call_declined\" });\n break;\n case \"busy\":\n store.dispatch({ type: \"call_outcome\", outcome: \"call_busy\" });\n break;\n case \"missed\":\n case \"failed\":\n store.dispatch({ type: \"call_outcome\", outcome: \"call_missed\" });\n break;\n default:\n break;\n }\n options.onSettled?.(invite);\n }\n\n function ingest(invite: CallInvite): void {\n // ONE ring per invite, whichever leg arrived first.\n if (incoming.has(invite.id)) return;\n if (invite.state !== \"ringing\" && invite.state !== \"dialing\") return;\n if (identity.userId === null) return;\n if (invite.callerUserId === identity.userId) return;\n\n // RULE 3: busy is ANSWERED. The caller learns why rather than ringing out.\n const phase = options.store.snapshot().phase;\n if (isLivePhase(phase) || phase === \"ringing_out\") {\n void repository\n .settleCallInvite(invite.id, \"busy\", null)\n .then((settled) => {\n // The CALLER is who needs to know. Sending this on my own topic\n // would tell my other devices something they already worked out and\n // leave the caller ringing out into silence.\n to(settled.callerUserId).send(MEET_EVENTS.settled, settled);\n })\n .catch((error: unknown) => {\n report(\"warn\", `meet.calls: could not report busy — ${String(error)}`,\n \"The caller will ring out and see a missed call. Harmless, but the busy signal \" +\n \"is what makes a second call feel intentional.\");\n });\n return;\n }\n\n incoming.set(invite.id, { ...invite, state: \"ringing\" });\n // §3.6: when nothing else is on screen the ring IS the phase; otherwise (a pre-join, a\n // waiting room) the card rings over it and the phase stays where the person is.\n if (options.store.snapshot().call === null && options.store.dispatch({ type: \"ring_in\" })) {\n options.store.patch({ call: { invite: { ...invite, state: \"ringing\" }, direction: \"incoming\" } });\n }\n options.onIncoming?.(invite);\n // RULE 1: every ring ends. Even if the caller vanishes mid-ring.\n timeouts.set(\n invite.id,\n timers.setTimeout(() => {\n const held = incoming.get(invite.id);\n if (held === undefined) return;\n settleLocal({ ...held, state: \"missed\" });\n }, ringTimeoutMs),\n );\n }\n\n return {\n async start() {\n const me = requireUser(\"meet.calls.start\");\n channel = manager.open({\n topic: userTopic(me),\n broadcast: [\n {\n event: MEET_EVENTS.ring,\n onMessage: (message) => {\n ingest(message.data as CallInvite);\n },\n },\n {\n event: MEET_EVENTS.cancel,\n onMessage: (message) => {\n const invite = message.data as CallInvite;\n const held = incoming.get(invite.id);\n if (held === undefined) return;\n settleLocal({ ...held, state: \"cancelled\" });\n },\n },\n {\n event: MEET_EVENTS.settled,\n onMessage: (message) => {\n // RULE 2: answered elsewhere. Stop ringing HERE.\n const invite = message.data as CallInvite;\n const held = incoming.get(invite.id);\n if (held !== undefined) settleLocal(invite);\n // THE CALLER'S LEG: the callee answered, declined or was busy. Through 0.10 this\n // handler only knew incoming rings, so a caller never learned the call was answered.\n else if (outgoing?.id === invite.id) settleOutgoing(invite);\n },\n },\n ],\n postgresChanges: [\n {\n event: \"INSERT\",\n schema: repository.schema,\n table: repository.tables.invites,\n filter: `callee_ids=cs.{${me}}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: (delivery) => {\n if (delivery.row === null) return;\n ingest(repository.projectInvite(delivery.row));\n },\n },\n {\n event: \"UPDATE\",\n schema: repository.schema,\n table: repository.tables.invites,\n filter: `callee_ids=cs.{${me}}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: (delivery) => {\n if (delivery.row === null) return;\n const invite = repository.projectInvite(delivery.row);\n if (invite.state !== \"ringing\" && invite.state !== \"dialing\") {\n const held = incoming.get(invite.id);\n if (held !== undefined) settleLocal(invite);\n }\n },\n },\n ],\n // THE BACKFILL DOOR. Realtime has no replay: a socket that dropped for\n // 20 seconds must re-read, or a call placed during the gap rings on the\n // caller's screen and nowhere else.\n onBackfill: async () => {\n const pending = await repository.pendingInvites(me);\n for (const invite of pending) ingest(invite);\n },\n });\n const pending = await repository.pendingInvites(me);\n for (const invite of pending) ingest(invite);\n },\n\n async call({ people, mode, conversationId }) {\n const me = requireUser(\"meet.calls.call\");\n if (people.length === 0) {\n throw new MeetError(\n \"misconfigured\",\n \"meet.calls.call: no callees\",\n \"Pass at least one user id. An empty call would create a room nobody is invited to.\",\n );\n }\n // This is a compatible proposal only. The server authors the room name;\n // outgoing state, signaling and joins must use the returned persisted invite.\n const roomName: RoomName = asRoomName(\n `call-${me}-${timers.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`,\n );\n const invite = await repository.createCallInvite({\n roomName,\n mode,\n callerUserId: me,\n callerName: identity.displayName,\n callerAvatarUrl: identity.avatarUrl,\n calleeUserIds: people,\n organizationId: identity.organizationId,\n conversationId: conversationId ?? null,\n expiresAt: new Date(timers.now() + ringTimeoutMs).toISOString(),\n });\n outgoing = invite;\n // §3.6: placing a call is a phase on the same machine.\n if (options.store.dispatch({ type: \"ring_out\" })) {\n options.store.patch({ call: { invite, direction: \"outgoing\" } });\n }\n\n // The instant leg. The durable leg already happened — the row is written.\n for (const callee of people) {\n to(callee).send(MEET_EVENTS.ring, invite);\n }\n timeouts.set(\n invite.id,\n timers.setTimeout(() => {\n if (outgoing?.id !== invite.id) return;\n void repository\n .settleCallInvite(invite.id, \"missed\", null)\n .then((settled) => {\n settleOutgoing(settled);\n })\n .catch(() => undefined);\n }, ringTimeoutMs),\n );\n return invite;\n },\n\n async accept(id, mode) {\n const held = incoming.get(id);\n if (held !== undefined && !canTransition(held.state, \"connecting\")) {\n throw new MeetError(\n \"conflict\",\n `meet.calls.accept: this call is already ${held.state}`,\n \"A late accept must not resurrect a settled call. Close the ringing UI; the caller \" +\n \"has already moved on.\",\n );\n }\n // §3.6: the server settles FIRST. A settle that fails (the caller gave up, the network\n // dropped) throws with the phase still `ringing_in` — the ring screen is honest — instead of\n // stranding the tab on `joining` with nothing joining. Only a settled answer moves it.\n const settled = await repository.settleCallInvite(id, \"connecting\", null);\n // `ringing_in` → `joining` BEFORE this device hears its own settle, so it never reads its\n // own answer as \"answered elsewhere\". The join itself is the provider's.\n if (options.store.snapshot().phase === \"ringing_in\") options.store.dispatch({ type: \"call_answered\" });\n options.store.patch({ call: { invite: settled, direction: \"incoming\" } });\n settleLocal(settled);\n // Tell my OTHER devices to stop ringing, and the caller to stop waiting.\n channel?.send(MEET_EVENTS.settled, settled);\n to(settled.callerUserId).send(MEET_EVENTS.settled, { ...settled, mode });\n return settled;\n },\n\n async decline(id, message) {\n const settled = await repository.settleCallInvite(id, \"declined\", message ?? null);\n if (options.store.snapshot().call?.invite.id === id && options.store.dispatch({ type: \"reset\" })) {\n options.store.patch({ call: null });\n }\n settleLocal(settled);\n channel?.send(MEET_EVENTS.settled, settled);\n to(settled.callerUserId).send(MEET_EVENTS.settled, settled);\n return settled;\n },\n\n async cancel(id) {\n const settled = await repository.settleCallInvite(id, \"cancelled\", null);\n clearRing(id);\n if (outgoing?.id === id) outgoing = settled;\n if (options.store.snapshot().call?.invite.id === id && options.store.dispatch({ type: \"reset\" })) {\n options.store.patch({ call: null });\n }\n for (const callee of settled.calleeUserIds) {\n to(callee).send(MEET_EVENTS.cancel, settled);\n }\n return settled;\n },\n\n incoming: () => [...incoming.values()],\n outgoing: () => outgoing,\n ingest,\n\n dispose() {\n for (const id of [...timeouts.keys()]) clearRing(asCallId(id));\n incoming.clear();\n for (const handle of outbound.values()) handle.close();\n outbound.clear();\n channel?.close();\n channel = null;\n },\n };\n}\n","/**\n * THE DATA CONTRACT — the ONE place a meet table or RPC name exists.\n *\n * 🚨 **STATE OF THE SCHEMA (read this before believing a green test).** The\n * `communication.meet_*` tables below are the canonical schema DESIGNED by this\n * package per R8; they are **not deployed to Matrx Main yet**. They were placed\n * by inspecting the live database on 2026-08-31: `communication` is the schema\n * that already holds `dm_*` (messaging) and `sms_*` (telephony), so meet takes\n * the `meet_*` prefix in the same schema rather than inventing a new one. Until\n * the migration lands, every read here fails LOUDLY with `not-found` and a\n * remedy naming this file — never silently, and never with a fabricated row.\n * The migration and the aidream server half are one deliverable; see FEATURE.md\n * § Deliberately NOT in this package.\n *\n * Four rules hold this file together — the same four that hold messaging's:\n *\n * 1. **Org is explicit on every write (R5).** Not defaulted by a trigger we\n * hope fires — passed, and refused in-package when absent.\n * 2. **Authorization is the database's job.** There is no permission branch in\n * this file. An RPC that says no is a `forbidden` MeetError, never a\n * client-side re-decision. Guest capability is scoped by the room TOKEN, not\n * by a row this client could write.\n * 3. **Writes go through SECURITY DEFINER RPCs; reads are RLS-backed table\n * reads** (C17's hybrid ruling). Every write here is an RPC, which is also\n * what keeps admission and settle-a-call ATOMIC — two hosts clicking \"admit\"\n * and a callee accepting on two devices are the ordinary case, not the edge.\n * 4. **Pagination is keyset, terminated by a unique column.** `(created_at,\n * id)` — never a timestamp alone, which duplicates or skips rows whenever\n * two share a millisecond.\n *\n * The Supabase client is accepted STRUCTURALLY, through the shape\n * `@ai-matrx/messaging` already owns. One definition of \"a Supabase client\" for\n * the whole communications family; this package adds no second copy.\n *\n * 🚨 AND THE PUBLIC BOUNDARY IS THE SHALLOW ONE (0.2.1). `RepositoryOptions`\n * and `<MeetProvider client>` take `MessagingSupabaseClient` — method names and\n * arity, results as `unknown` — never the rich `SupabaseLike`. Checking a\n * host's real `SupabaseClient<Database>` (built from generated types) against\n * the rich shape makes tsc chase PostgREST's schema machinery until it bails\n * with `TS2589: Type instantiation is excessively deep and possibly infinite`,\n * so the only way to pass the client a host ALREADY HAS was a cast — which is\n * what the first real consumer had to write (register MRI-D1). Messaging 0.9.0\n * learned this and wrote it down; this package did not apply the lesson until\n * now. The rich shape stays the INTERNAL contract, and we narrow to it ONCE,\n * here, in-package.\n */\n\nimport { scheduleSettingsPatch } from \"./app-panel\";\nimport type { MessagingSupabaseClient, SupabaseLike } from \"@ai-matrx/messaging\";\nimport { invalidResponse, MeetError, normalizeMeetError } from \"./errors\";\nimport {\n asCallId,\n asMeetingId,\n asOrganizationId,\n asParticipantIdentity,\n asRoomName,\n asUserId,\n type CallId,\n type CallInvite,\n type CallMode,\n type CallState,\n type JsonObject,\n type MeetingId,\n type MeetingKind,\n type MeetingAttendee,\n type MeetingNote,\n type LinkRunState,\n type MeetingRecord,\n type ParticipantRole,\n type AdmissionState,\n type OrganizationId,\n type RecordingPolicy,\n type RecordingRecord,\n type RecordingRetention,\n type RecordingState,\n type RoomName,\n type TranscriptSegment,\n type UserId,\n type InviteeInput,\n type InviteeRole,\n type MeetingChanges,\n type AttendanceStay,\n type MeetingInvitee,\n type MeetingOccurrence,\n type MeetingSettings,\n type OccurrenceOverride,\n type OccurrenceState,\n type RsvpAnswer,\n type RsvpInvitation,\n type RsvpState,\n type UpcomingOccurrence,\n} from \"./types\";\n\n/** The schema. Meet is never in `public`. */\nexport const MEET_SCHEMA = \"communication\";\n\nexport const TABLES = {\n meetings: \"meet_meetings\",\n participants: \"meet_participants\",\n invites: \"meet_call_invites\",\n recordings: \"meet_recordings\",\n transcripts: \"meet_transcript_segments\",\n notes: \"meet_notes\",\n /** Wave 1 (0.8): who a meeting was sent to, and what they answered. */\n invitees: \"meet_invitees\",\n /** Wave 1 (0.8): one occurrence of a series cancelled or moved. */\n occurrenceOverrides: \"meet_occurrence_overrides\",\n /** S5: one row per RUN of a meeting; carries that run's note-taker state. */\n sessions: \"meet_sessions\",\n} as const;\n\nexport const RPCS = {\n /** Atomic: create the invite row and stamp its expiry in one statement. */\n createCallInvite: \"meet_create_call_invite\",\n /** Atomic settle. Refuses a transition out of a terminal state. */\n settleCallInvite: \"meet_settle_call_invite\",\n /** Every invite still ringing this user — the reconnect backfill door. */\n pendingCallInvites: \"meet_pending_call_invites\",\n /** Instant meeting, or the durable one behind a recurring link. */\n getOrCreateMeeting: \"meet_get_or_create_meeting\",\n /** Resolve a durable public link, before/during/after its scheduled time. */\n meetingBySlug: \"meet_meeting_by_slug\",\n /** The current run of a durable link (open / not started / ended + next start), no pass needed. */\n linkState: \"meet_link_state\",\n /*\n * ── S4 ADMISSION (CORE-DESIGN §2.4). Each returns the decided row; the caller then\n * sends the payload-free `mx.state.changed` hint. Authorized by `who_may_admit`.\n */\n admit: \"meet_admit\",\n deny: \"meet_deny\",\n undeny: \"meet_undeny\",\n admitAll: \"meet_admit_all\",\n setLock: \"meet_set_lock\",\n recordConsent: \"meet_record_consent\",\n /** One meeting rule as the gate obeys it (the knob ladder, this meeting's rung included). */\n meetingRule: \"meet_policy\",\n /** A host sets one rule for THIS meeting (the `meet_meeting` rung); refusals come back as `{ok:false}`. */\n setMeetingRule: \"meet_policy_set\",\n /*\n * ── MEET WAVE 1 — management (0.8). Every write is a SECURITY DEFINER door\n * authorized by the meeting's host or a real iam grant (editor to change,\n * admin = co-host to archive or name a co-host) — never an organization\n * match. After any of these writes, call `announceMeeting` so the server\n * sends invitations / updates / cancellations and re-queues reminders.\n */\n /** Schedule with every setting at create; unset settings come from the knobs. */\n scheduleMeeting: \"meet_schedule_meeting\",\n /** Edit / reschedule — a JSON patch with a version compare-and-swap. */\n updateMeeting: \"meet_update_meeting\",\n /** Cancel is a state (`cancelled_at`); the link keeps resolving. */\n cancelMeeting: \"meet_cancel_meeting\",\n /** Archive (soft delete) — the invitations and exceptions cascade. */\n archiveMeeting: \"meet_archive_meeting\",\n restoreMeeting: \"meet_restore_meeting\",\n /** Cancel, move or restore ONE occurrence of a series by its original start. */\n setOccurrence: \"meet_set_occurrence\",\n addInvitees: \"meet_add_invitees\",\n setInviteeRole: \"meet_set_invitee_role\",\n removeInvitee: \"meet_remove_invitee\",\n /** A signed-in invitee answers for themselves. */\n respond: \"meet_respond\",\n /** The emailed RSVP link (no account): read and answer. */\n invitationByToken: \"meet_invitation_by_token\",\n respondByToken: \"meet_respond_by_token\",\n /** One meeting's occurrences between two instants (INVOKER — the reader's RLS). */\n meetingOccurrences: \"meet_meeting_occurrences\",\n /** Every occurrence the reader can see between two instants — the Upcoming list. */\n occurrencesBetween: \"meet_occurrences_between\",\n /*\n * ── MEET WAVE 3 — after the meeting. Both INVOKER (the reader's RLS) and both\n * scoped to the reader's OWN meetings (host, invitee, attended) — RLS is the\n * ceiling, never the view.\n */\n /** The recordings library. */\n myRecordings: \"meet_my_recordings\",\n /** Titles, agendas, current notes and final transcript lines; snippets mark hits with ⟦ ⟧. */\n search: \"meet_search\",\n} as const;\n\n/**\n * THE RPCs THAT RETURN NOTHING, verified against the live database\n * (`pg_get_function_result`, 2026-09-08). They go through `guardVoid`; every\n * other RPC returns a row and goes through `guard`. Wiring a void RPC to\n * `guard` makes a SUCCESSFUL write look like a failure — see `guardVoid`.\n */\nexport const VOID_RPCS: readonly string[] = [RPCS.recordConsent];\n\nconst MEETING_COLUMNS =\n \"id,organization_id,room_name,slug,title,kind,host_user_id,scheduled_for,\" +\n \"scheduled_duration_minutes,started_at,ended_at,locked,lobby_enabled,\" +\n \"recording_policy,ai_enabled,metadata,\" +\n // Wave 1 (0.8).\n \"time_zone,agenda,recurrence_rule,join_before_host,cancelled_at,cancellation_reason,\" +\n \"deleted_at,calendar_sequence,version\";\n\nconst INVITEE_COLUMNS =\n \"id,meeting_id,invitee_user_id,email,display_name,role,rsvp_state,responded_at,\" +\n \"rsvp_note,last_sent_sequence,last_sent_at\";\n\n/**\n * The invite row's columns. Not used by a `select` today — every invite read\n * and write goes through an RPC (C17's hybrid: writes are SECURITY DEFINER,\n * and a settle must be atomic) — but it is part of the contract S2's migration\n * has to satisfy, and it is what `projectInvite` reads out of an RPC's return\n * row. Keep it in step with that function.\n */\nconst INVITE_COLUMNS =\n \"id,organization_id,room_name,mode,caller_user_id,caller_name,caller_avatar_url,\" +\n \"callee_ids,conversation_id,created_at,expires_at,state,decline_message\";\n\nconst RECORDING_COLUMNS =\n \"id,meeting_id,state,started_at,ended_at,started_by,file_id,failure_reason\";\n\n/**\n * THE TRANSCRIPT'S COLUMNS. `is_final` — not `final` — is the live column name,\n * and only final rows are ever written (D6: an interim is a caption and a\n * caption is never persisted).\n */\nconst SEGMENT_COLUMNS = \"id,meeting_id,identity,speaker,text,started_at,is_final,metadata\";\n\n/** What the meeting decided. `kind` is a three-value CHECK constraint (D5). */\nconst NOTE_COLUMNS = \"id,meeting_id,text,kind,assignee_user_id,created_at,metadata\";\n\n/**\n * WHO WAS THERE. The row survives the room, which is the whole point: an\n * attendance list that only exists while LiveKit holds the room is not a record\n * of anything (MRI-D2).\n */\nconst ATTENDEE_COLUMNS =\n \"id,meeting_id,identity,participant_user_id,display_name,avatar_url,role,\" +\n \"admission_state,knocked_at,knock_expires_at,is_agent,joined_at,left_at,sessions:metadata->sessions,\" +\n \"knock_flag:metadata->>knock_flag\";\n\ntype Row = Record<string, unknown>;\n\n/** `metadata.sessions` → stays; anything malformed is skipped, never guessed. */\nfunction projectStays(value: unknown): readonly AttendanceStay[] {\n if (!Array.isArray(value)) return [];\n const stays: AttendanceStay[] = [];\n for (const item of value) {\n if (item === null || typeof item !== \"object\") continue;\n const joined = (item as Row)[\"joined_at\"];\n const left = (item as Row)[\"left_at\"];\n if (typeof joined !== \"string\" || joined.length === 0) continue;\n stays.push({ joinedAt: joined, leftAt: typeof left === \"string\" && left.length > 0 ? left : null });\n }\n return stays;\n}\n\nfunction text(row: Row, key: string): string | null {\n const value = row[key];\n return typeof value === \"string\" && value.length > 0 ? value : null;\n}\n\nfunction requireText(row: Row, key: string, operation: string): string {\n const value = text(row, key);\n if (value === null) {\n throw invalidResponse(operation, `the row is missing \"${key}\"`);\n }\n return value;\n}\n\nfunction bool(row: Row, key: string, fallback: boolean): boolean {\n const value = row[key];\n return typeof value === \"boolean\" ? value : fallback;\n}\n\nfunction stringArray(row: Row, key: string): readonly string[] {\n const value = row[key];\n if (!Array.isArray(value)) return [];\n return value.filter((entry): entry is string => typeof entry === \"string\");\n}\n\nconst CALL_STATES: readonly CallState[] = [\n \"idle\",\n \"dialing\",\n \"ringing\",\n \"connecting\",\n \"active\",\n \"declined\",\n \"missed\",\n \"busy\",\n \"cancelled\",\n \"ended\",\n \"failed\",\n];\n\nfunction callState(value: string | null): CallState {\n return CALL_STATES.includes(value as CallState) ? (value as CallState) : \"failed\";\n}\n\nexport interface CreateCallInviteInput {\n readonly roomName: RoomName;\n readonly mode: CallMode;\n readonly callerUserId: UserId;\n readonly callerName: string;\n readonly callerAvatarUrl: string | null;\n readonly calleeUserIds: readonly UserId[];\n readonly organizationId: OrganizationId;\n readonly conversationId: string | null;\n readonly expiresAt: string;\n}\n\nexport interface CreateMeetingInput {\n readonly organizationId: OrganizationId;\n readonly hostUserId: UserId;\n readonly title: string;\n readonly kind: MeetingKind;\n readonly scheduledFor?: string | null;\n readonly scheduledDurationMinutes?: number | null;\n readonly lobbyEnabled?: boolean;\n readonly recordingPolicy?: RecordingPolicy;\n readonly aiEnabled?: boolean;\n /** Reuse a durable slug (a recurring meeting keeps ONE link — R3). */\n readonly slug?: string | undefined;\n}\n\nexport interface MeetRepository {\n readonly schema: string;\n readonly tables: typeof TABLES;\n projectInvite(row: Row): CallInvite;\n projectMeeting(row: Row): MeetingRecord;\n createCallInvite(input: CreateCallInviteInput): Promise<CallInvite>;\n settleCallInvite(id: CallId, state: CallState, declineMessage: string | null): Promise<CallInvite>;\n pendingInvites(userId: UserId): Promise<readonly CallInvite[]>;\n getOrCreateMeeting(input: CreateMeetingInput): Promise<MeetingRecord>;\n meetingBySlug(slug: string): Promise<MeetingRecord>;\n meeting(id: MeetingId): Promise<MeetingRecord>;\n /**\n * Keyset paged, `(scheduled_for, id)`. Every meeting the person can access, in\n * ALL their organizations — `organizationId` is an OPTIONAL page filter\n * (absent/null = all organizations), never the active organization\n * (active-org-is-never-a-list-filter.md). Access is decided by RLS.\n */\n listMeetings(args: {\n organizationId?: OrganizationId | null;\n limit?: number;\n before?: { scheduledFor: string; id: MeetingId } | null;\n }): Promise<{ readonly items: readonly MeetingRecord[]; readonly hasMore: boolean }>;\n /** S4: one decision on one waiting person; returns the row as it now stands. */\n decideAdmission(args: {\n meetingId: MeetingId;\n identity: string;\n decision: \"admit\" | \"deny\" | \"undeny\";\n byUserId: UserId;\n }): Promise<MeetingAttendee>;\n /** S4: admit everyone still waiting (not expired); returns the admitted rows. */\n admitAll(meetingId: MeetingId, byUserId: UserId): Promise<readonly MeetingAttendee[]>;\n setLock(meetingId: MeetingId, locked: boolean, byUserId: UserId): Promise<MeetingRecord>;\n /** `wr-policy-who-waits`: read one rule of this meeting (`lobby_bypass`, `access_type`…). */\n meetingRule(meetingId: MeetingId, key: string): Promise<unknown>;\n /** A host sets one rule for this meeting; a refusal throws with the server's sentence. */\n setMeetingRule(meetingId: MeetingId, key: string, value: string | number | boolean): Promise<unknown>;\n projectSegment(row: Row): TranscriptSegment;\n projectNote(row: Row): MeetingNote;\n /** One `meet_recordings` row, however it arrived (row or room packet). */\n projectRecording(row: Row): RecordingRecord;\n /** One durable `meet_participants` row — attendance, and the lobby queue. */\n projectAttendee(row: Row): MeetingAttendee;\n projectAttendee(row: Row): MeetingAttendee;\n /**\n * WHO ATTENDED, oldest arrival first. Everyone the meeting ever admitted —\n * including the note-taker, which is a labelled participant and never a hidden\n * listener (R4) — so the record can say who was in the room AND that it was\n * transcribed.\n */\n attendees(meetingId: MeetingId): Promise<readonly MeetingAttendee[]>;\n /**\n * THE DURABLE TRANSCRIPT (D16). Final rows only — the table holds nothing\n * else — oldest first, capped so a four-hour meeting cannot hand a surface\n * fifty thousand rows in one read.\n */\n transcript(meetingId: MeetingId, limit?: number): Promise<readonly TranscriptSegment[]>;\n /** Live notes, decisions, action items and the wrap-up summary, oldest first. */\n notes(meetingId: MeetingId): Promise<readonly MeetingNote[]>;\n /** R5: consent is RECORDED, not assumed. One row per participant per recording. */\n recordConsent(args: {\n meetingId: MeetingId;\n identity: string;\n acknowledgedAt: string;\n }): Promise<void>;\n recordings(meetingId: MeetingId): Promise<readonly RecordingRecord[]>;\n /** Meet wave 3: every recording of the reader's own meetings, newest first. */\n myRecordings(args?: { includeArchived?: boolean; limit?: number }): Promise<readonly LibraryRecording[]>;\n /** Meet wave 3: search the reader's own meetings. */\n searchMeetings(query: string, limit?: number): Promise<readonly MeetingSearchHit[]>;\n /** Meet wave 3: name a recording (`null` = back to the meeting's title and date). */\n renameRecording(recordingId: string, title: string | null): Promise<void>;\n /** Meet wave 3: archive (soft delete) or restore a recording. Never a purge. */\n setRecordingArchived(recordingId: string, archived: boolean): Promise<void>;\n\n /* ── MEET WAVE 1 — management (0.8) ── */\n scheduleMeeting(input: ScheduleMeetingInput): Promise<MeetingRecord>;\n /** `expectedVersion` from the row you edited; a stale one is a `conflict`. */\n updateMeeting(\n meetingId: MeetingId,\n changes: MeetingChanges,\n options?: { expectedVersion?: number | null; byUserId?: UserId | null },\n ): Promise<MeetingRecord>;\n cancelMeeting(meetingId: MeetingId, reason: string | null, byUserId?: UserId | null): Promise<MeetingRecord>;\n archiveMeeting(meetingId: MeetingId, byUserId?: UserId | null): Promise<MeetingRecord>;\n restoreMeeting(meetingId: MeetingId, byUserId?: UserId | null): Promise<MeetingRecord>;\n setOccurrence(args: {\n meetingId: MeetingId;\n originalStart: string;\n action: \"cancel\" | \"move\" | \"restore\";\n newStart?: string | null;\n newDurationMinutes?: number | null;\n reason?: string | null;\n byUserId?: UserId | null;\n }): Promise<OccurrenceOverride>;\n meetingOccurrences(\n meetingId: MeetingId,\n range: { from: string; to: string; limit?: number },\n ): Promise<readonly MeetingOccurrence[]>;\n /** The Upcoming list: every occurrence the reader may see. `organizationId` FILTERS; RLS decides. */\n occurrencesBetween(range: {\n from: string;\n to: string;\n organizationId?: OrganizationId | null;\n limit?: number;\n }): Promise<readonly UpcomingOccurrence[]>;\n invitees(meetingId: MeetingId): Promise<readonly MeetingInvitee[]>;\n addInvitees(meetingId: MeetingId, people: readonly InviteeInput[], byUserId?: UserId | null): Promise<readonly MeetingInvitee[]>;\n setInviteeRole(inviteeId: string, role: InviteeRole, byUserId?: UserId | null): Promise<MeetingInvitee>;\n removeInvitee(inviteeId: string, byUserId?: UserId | null): Promise<MeetingInvitee>;\n respond(meetingId: MeetingId, answer: RsvpAnswer, note?: string | null): Promise<MeetingInvitee>;\n invitationByToken(secret: string): Promise<RsvpInvitation>;\n respondByToken(secret: string, answer: RsvpAnswer, note?: string | null): Promise<RsvpInvitation>;\n projectInvitee(row: Row): MeetingInvitee;\n}\n\nexport interface ScheduleMeetingInput {\n readonly organizationId: OrganizationId;\n readonly hostUserId: UserId;\n readonly title: string;\n readonly scheduledFor: string | null;\n /** IANA zone, e.g. `America/Los_Angeles`. The wall-clock time a series keeps across DST. */\n readonly timeZone: string;\n /** Omit to use the person's `meet.default_duration_minutes`. */\n readonly durationMinutes?: number | null | undefined;\n readonly agenda?: string | null | undefined;\n /** RRULE subset: FREQ=DAILY|WEEKLY|MONTHLY, INTERVAL, BYDAY, BYMONTHDAY, COUNT, UNTIL. */\n readonly recurrenceRule?: string | null | undefined;\n readonly settings?: MeetingSettings | undefined;\n /** HR-360 wave 2 (MD-15): the registered app panel the call opens beside it. */\n readonly appPanel?: { readonly key: string; readonly recordId: string } | undefined;\n /** HR-360 wave 2 (MD-16): user ids the host names as silent observers. */\n readonly observers?: readonly string[] | undefined;\n}\n\nexport interface RepositoryOptions {\n /**\n * Any Supabase client. Deliberately the SHALLOW public shape — pass the\n * `SupabaseClient<Database>` you already have, with no cast. See the header.\n */\n client: MessagingSupabaseClient;\n /** Resolve a session; used to retry ONCE across the sign-in gap. */\n session?: (() => Promise<unknown>) | undefined;\n}\n\n/**\n * 🚨 THE PROJECTIONS ARE MODULE-LEVEL AND EXPORTED, DELIBERATELY (MRI-A12).\n *\n * A durable Meet row reaches a client on one of two transports: as a\n * `meet_notes` / `meet_transcript_segments` row over Postgres Changes, which\n * needs a Supabase session and an org grant, or as a `matrx.meet.*` packet on\n * the LiveKit data plane, which needs only the room token — and is therefore the\n * ONLY one a session-less guest can receive (MRI-A10 settled that `anon` never\n * gets a grant on an org-scoped table).\n *\n * Both carry the same row. If each transport projected it with its own function,\n * a guest and a member would render the same meeting differently the first time\n * the two drifted. So there is one function per row shape, it takes no client,\n * and `core/room-events.ts` calls exactly these.\n */\nexport function projectSegment(row: Row): TranscriptSegment {\n const operation = \"meet.projectSegment\";\n const id = requireText(row, \"id\", operation);\n return {\n id,\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n identity: asParticipantIdentity(text(row, \"identity\") ?? \"unknown\"),\n // A nameless line is a line nobody can act on. The identity is a worse\n // name than a real one and a far better one than an empty string.\n speaker: text(row, \"speaker\") ?? text(row, \"identity\") ?? \"Someone\",\n text: text(row, \"text\") ?? \"\",\n startedAt: text(row, \"started_at\") ?? new Date(0).toISOString(),\n // The table holds finals only; a row that somehow says otherwise is still\n // a durable record and is rendered as one.\n final: bool(row, \"is_final\", true),\n source: \"record\",\n // The worker publishes the caption under THIS row's id (`lk.segment_id` =\n // the durable segment id), which is what lets a record replace the exact\n // caption it supersedes instead of doubling the line (D16).\n captionId: id,\n };\n}\n\nexport function projectNote(row: Row): MeetingNote {\n const operation = \"meet.projectNote\";\n const kind = text(row, \"kind\");\n const rawMetadata = row[\"metadata\"];\n const metadata: Record<string, unknown> =\n typeof rawMetadata === \"object\" && rawMetadata !== null && !Array.isArray(rawMetadata)\n ? (rawMetadata as Record<string, unknown>)\n : {};\n const assignee = metadata[\"assignee\"];\n const assigneeRow: Record<string, unknown> =\n typeof assignee === \"object\" && assignee !== null && !Array.isArray(assignee)\n ? (assignee as Record<string, unknown>)\n : {};\n const windowValue = metadata[\"window\"];\n const windowRow: Record<string, unknown> =\n typeof windowValue === \"object\" && windowValue !== null && !Array.isArray(windowValue)\n ? (windowValue as Record<string, unknown>)\n : {};\n const assigneeUserId = text(row, \"assignee_user_id\");\n const displayName = assigneeRow[\"display_name\"];\n return {\n id: requireText(row, \"id\", operation),\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n text: (text(row, \"text\") ?? \"\").trim(),\n createdAt: text(row, \"created_at\") ?? new Date().toISOString(),\n // An unknown kind is a NOTE, never dropped: a row the database accepted\n // is a row a person wrote down, whatever a future migration calls it.\n kind:\n kind === \"decision\" || kind === \"action-item\" ? (kind as MeetingNote[\"kind\"]) : \"note\",\n assigneeUserId: assigneeUserId === null ? null : asUserId(assigneeUserId),\n // D18: a guest never gets an `assignee_user_id` (the FK targets\n // `auth.users`), so their name lives in the metadata and is read here —\n // an action item whose owner has no name is an action item nobody owns.\n assigneeDisplayName:\n typeof displayName === \"string\" && displayName.length > 0 ? displayName : null,\n role: metadata[\"role\"] === \"summary\" ? \"summary\" : null,\n window: typeof windowRow[\"index\"] === \"number\" ? windowRow[\"index\"] : null,\n // D20. `superseded_by` is in `NOTE_PUBLIC_METADATA_KEYS` server-side, so a\n // session-less guest's room-borne copy carries it too — otherwise the guest\n // would be the one lane still reading a decision twice.\n supersededBy:\n typeof metadata[\"superseded_by\"] === \"string\" && metadata[\"superseded_by\"].length > 0\n ? (metadata[\"superseded_by\"] as string)\n : null,\n // THE DATABASE ROW. `core/room-events.ts` marks its copy of the same row\n // `\"room\"`, and the store's precedence rule reads this field (MRI-A12).\n source: \"record\",\n };\n}\n\n\n\n/* ─────────────────────────────── MEET WAVE 1 — management projections (0.8) */\n\nconst RSVP_STATES: readonly RsvpState[] = [\"needs_action\", \"accepted\", \"declined\", \"tentative\"];\nconst OCCURRENCE_STATES: readonly OccurrenceState[] = [\"scheduled\", \"moved\", \"cancelled\"];\n\nfunction rsvpState(value: string | null): RsvpState {\n return RSVP_STATES.includes(value as RsvpState) ? (value as RsvpState) : \"needs_action\";\n}\n\nfunction num(row: Row, key: string): number | null {\n const value = row[key];\n return typeof value === \"number\" ? value : null;\n}\n\n/** A database timestamp as ISO text. PostgREST sends strings; a test double may send Dates. */\nfunction when(row: Row, key: string): string | null {\n const value = row[key];\n if (value instanceof Date) return value.toISOString();\n return text(row, key);\n}\n\nexport function projectInvitee(row: Row): MeetingInvitee {\n const operation = \"meet.projectInvitee\";\n const userId = text(row, \"invitee_user_id\");\n return {\n id: requireText(row, \"id\", operation),\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n userId: userId === null ? null : asUserId(userId),\n email: text(row, \"email\"),\n displayName: text(row, \"display_name\"),\n role: text(row, \"role\") === \"cohost\" ? \"cohost\" : \"invitee\",\n rsvpState: rsvpState(text(row, \"rsvp_state\")),\n respondedAt: when(row, \"responded_at\"),\n rsvpNote: text(row, \"rsvp_note\"),\n lastSentSequence: num(row, \"last_sent_sequence\"),\n lastSentAt: when(row, \"last_sent_at\"),\n };\n}\n\nexport function projectOccurrence(row: Row): MeetingOccurrence {\n const operation = \"meet.projectOccurrence\";\n const state = text(row, \"state\");\n return {\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n originalStart: when(row, \"original_start\") ?? requireText(row, \"original_start\", operation),\n occurrenceStart: when(row, \"occurrence_start\") ?? requireText(row, \"occurrence_start\", operation),\n durationMinutes: num(row, \"duration_minutes\"),\n // An unknown state is shown as scheduled, never dropped: the row is real.\n state: OCCURRENCE_STATES.includes(state as OccurrenceState) ? (state as OccurrenceState) : \"scheduled\",\n sequence: num(row, \"sequence\") ?? 0,\n };\n}\n\nexport function projectUpcoming(row: Row): UpcomingOccurrence {\n const operation = \"meet.projectUpcoming\";\n const kind = text(row, \"kind\");\n const myRole = text(row, \"my_role\");\n const myRsvp = text(row, \"my_rsvp\");\n return {\n ...projectOccurrence(row),\n organizationId: asOrganizationId(requireText(row, \"organization_id\", operation)),\n title: text(row, \"title\") ?? \"Meeting\",\n slug: requireText(row, \"slug\", operation),\n kind: kind === \"scheduled\" || kind === \"recurring\" ? (kind as MeetingKind) : \"instant\",\n hostUserId: asUserId(requireText(row, \"host_user_id\", operation)),\n timeZone: text(row, \"time_zone\") ?? \"UTC\",\n meetingCancelled: bool(row, \"meeting_cancelled\", false),\n myRsvp: myRsvp === null ? null : rsvpState(myRsvp),\n myRole: myRole === \"host\" || myRole === \"cohost\" || myRole === \"invitee\" ? myRole : null,\n };\n}\n\nexport function projectOverride(row: Row): OccurrenceOverride {\n const operation = \"meet.projectOverride\";\n return {\n id: requireText(row, \"id\", operation),\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n originalStart: when(row, \"original_start\") ?? requireText(row, \"original_start\", operation),\n state: text(row, \"state\") === \"moved\" ? \"moved\" : \"cancelled\",\n newStart: when(row, \"new_start\"),\n newDurationMinutes: num(row, \"new_duration_minutes\"),\n reason: text(row, \"reason\"),\n calendarSequence: num(row, \"calendar_sequence\") ?? 0,\n deletedAt: when(row, \"deleted_at\"),\n };\n}\n\n/** The jsonb the RSVP doors return, typed. A body that is neither shape is refused loudly. */\nexport function projectRsvpInvitation(body: unknown): RsvpInvitation {\n const operation = \"meet.projectRsvpInvitation\";\n if (typeof body !== \"object\" || body === null) {\n throw invalidResponse(operation, \"the RSVP door returned no object\");\n }\n const row = body as Row;\n if (row[\"ok\"] !== true) {\n return {\n ok: false,\n message: text(row, \"message\") ?? \"This invitation link is no longer valid — ask the host to send it again.\",\n };\n }\n const meeting = (row[\"meeting\"] ?? {}) as Row;\n const invitee = (row[\"invitee\"] ?? {}) as Row;\n return {\n ok: true,\n meeting: {\n title: text(meeting, \"title\") ?? \"Meeting\",\n slug: requireText(meeting, \"slug\", operation),\n scheduledFor: when(meeting, \"scheduled_for\"),\n durationMinutes: num(meeting, \"duration_minutes\"),\n timeZone: text(meeting, \"time_zone\") ?? \"UTC\",\n agenda: text(meeting, \"agenda\"),\n recurrenceRule: text(meeting, \"recurrence_rule\"),\n cancelledAt: when(meeting, \"cancelled_at\"),\n cancellationReason: text(meeting, \"cancellation_reason\"),\n endedAt: when(meeting, \"ended_at\"),\n },\n invitee: {\n displayName: text(invitee, \"display_name\"),\n email: text(invitee, \"email\"),\n rsvpState: rsvpState(text(invitee, \"rsvp_state\")),\n respondedAt: when(invitee, \"responded_at\"),\n },\n };\n}\n\n/** Settings → the door's snake_case object, carrying ONLY what was set. */\nexport function settingsPatch(settings: MeetingSettings): Record<string, unknown> {\n const patch: Record<string, unknown> = {};\n if (settings.lobbyEnabled !== undefined) patch[\"lobby_enabled\"] = settings.lobbyEnabled;\n if (settings.recordingPolicy !== undefined) patch[\"recording_policy\"] = settings.recordingPolicy;\n if (settings.aiEnabled !== undefined) patch[\"ai_enabled\"] = settings.aiEnabled;\n if (settings.joinBeforeHost !== undefined) patch[\"join_before_host\"] = settings.joinBeforeHost;\n return patch;\n}\n\n/**\n * A partial edit → the `meet_update_meeting` patch. `undefined` = leave alone,\n * `null` = clear — the difference a JSON patch exists to carry.\n */\nexport function changesPatch(changes: MeetingChanges): Record<string, unknown> {\n const patch = settingsPatch(changes);\n if (changes.title !== undefined) patch[\"title\"] = changes.title;\n if (changes.agenda !== undefined) patch[\"agenda\"] = changes.agenda;\n if (changes.timeZone !== undefined) patch[\"time_zone\"] = changes.timeZone;\n if (changes.scheduledFor !== undefined) patch[\"scheduled_for\"] = changes.scheduledFor;\n if (changes.scheduledDurationMinutes !== undefined) {\n patch[\"scheduled_duration_minutes\"] = changes.scheduledDurationMinutes;\n }\n if (changes.recurrenceRule !== undefined) patch[\"recurrence_rule\"] = changes.recurrenceRule;\n if (changes.keepExceptions !== undefined) patch[\"keep_exceptions\"] = changes.keepExceptions;\n return patch;\n}\n\nexport function createMeetRepository(options: RepositoryOptions): MeetRepository {\n // THE ONE NARROWING, in-package. Everything below works against the rich\n // internal contract; no consumer ever has to satisfy it.\n const db = (options.client as SupabaseLike).schema(MEET_SCHEMA);\n\n /**\n * 🚨 A `RETURNS void` RPC RESOLVES TO `null`, AND THAT IS SUCCESS.\n *\n * `guard` below treats a null `data` as an invalid response, which is right\n * for every RPC that returns a row and CATASTROPHIC for one that returns\n * nothing: `communication.meet_admit_participant` and `meet_record_consent`\n * are both `RETURNS void`, so from the day this package shipped, admitting\n * somebody from the lobby and recording consent BOTH performed their write\n * and then threw \"the database returned no row\" on the way back — the write\n * landed, the client believed it failed, the broadcast that tells the guest\n * never went out, and the guest sat in the waiting room forever. Found in a\n * browser (MRI-A11); the RPC return types are pinned in `VOID_RPCS` below so\n * a future void RPC cannot be wired to the wrong guard by accident.\n */\n async function guardVoid(\n operation: string,\n run: () => PromiseLike<{ data: unknown; error: { message: string; code?: string } | null }>,\n ): Promise<void> {\n let response = await run();\n if (response.error !== null) {\n const normalized = normalizeMeetError(response.error, operation);\n if (normalized.code === \"session-unavailable\" && options.session !== undefined) {\n await options.session();\n response = await run();\n }\n if (response.error !== null) throw normalizeMeetError(response.error, operation);\n }\n // No row is expected and none is read. The absence of an error IS the\n // result — anything else would be inventing a postcondition the database\n // never promised.\n }\n\n /**\n * The link's current run. It adds to the lookup and never replaces it: a failed read leaves the\n * run UNKNOWN (undefined) and the join gate still decides on Join — an unknown is never guessed.\n */\n async function readLinkRun(slug: string): Promise<LinkRunState | undefined> {\n try {\n const response = await db.rpc<Row>(RPCS.linkState, { p_slug: slug });\n return projectLinkRun(response.error === null ? response.data : null);\n } catch {\n return undefined;\n }\n }\n\n async function guard<T>(operation: string, run: () => PromiseLike<{ data: T | null; error: { message: string; code?: string } | null }>): Promise<T> {\n let response = await run();\n if (response.error !== null) {\n const normalized = normalizeMeetError(response.error, operation);\n // ONE retry, and only for the sign-in gap. A permission denial is NEVER\n // retried — retrying one is how a request storm is built.\n if (normalized.code === \"session-unavailable\" && options.session !== undefined) {\n await options.session();\n response = await run();\n }\n if (response.error !== null) throw normalizeMeetError(response.error, operation);\n }\n if (response.data === null) {\n throw invalidResponse(operation, \"the database returned no row\");\n }\n return response.data;\n }\n\n function projectInvite(row: Row): CallInvite {\n const operation = \"meet.projectInvite\";\n const mode = text(row, \"mode\");\n return {\n id: asCallId(requireText(row, \"id\", operation)),\n organizationId: asOrganizationId(requireText(row, \"organization_id\", operation)),\n roomName: asRoomName(requireText(row, \"room_name\", operation)),\n mode: mode === \"video\" ? \"video\" : \"audio\",\n callerUserId: asUserId(requireText(row, \"caller_user_id\", operation)),\n callerName: text(row, \"caller_name\") ?? \"Someone\",\n callerAvatarUrl: text(row, \"caller_avatar_url\"),\n calleeUserIds: stringArray(row, \"callee_ids\").map(asUserId),\n conversationId: text(row, \"conversation_id\"),\n createdAt: requireText(row, \"created_at\", operation),\n expiresAt: text(row, \"expires_at\") ?? requireText(row, \"created_at\", operation),\n state: callState(text(row, \"state\")),\n declineMessage: text(row, \"decline_message\"),\n };\n }\n\n function projectMeeting(row: Row): MeetingRecord {\n const operation = \"meet.projectMeeting\";\n const kind = text(row, \"kind\");\n const policy = text(row, \"recording_policy\");\n const duration = row[\"scheduled_duration_minutes\"];\n const metadata = row[\"metadata\"];\n return {\n id: asMeetingId(requireText(row, \"id\", operation)),\n organizationId: asOrganizationId(requireText(row, \"organization_id\", operation)),\n roomName: asRoomName(requireText(row, \"room_name\", operation)),\n slug: requireText(row, \"slug\", operation),\n title: text(row, \"title\") ?? \"Meeting\",\n kind: kind === \"scheduled\" || kind === \"recurring\" ? (kind as MeetingKind) : \"instant\",\n hostUserId: asUserId(requireText(row, \"host_user_id\", operation)),\n scheduledFor: text(row, \"scheduled_for\"),\n scheduledDurationMinutes: typeof duration === \"number\" ? duration : null,\n startedAt: text(row, \"started_at\"),\n endedAt: text(row, \"ended_at\"),\n locked: bool(row, \"locked\", false),\n lobbyEnabled: bool(row, \"lobby_enabled\", true),\n recordingPolicy:\n policy === \"disabled\" || policy === \"always-on\"\n ? (policy as RecordingPolicy)\n : \"host-controlled\",\n aiEnabled: bool(row, \"ai_enabled\", true),\n metadata:\n typeof metadata === \"object\" && metadata !== null && !Array.isArray(metadata)\n ? (metadata as JsonObject)\n : {},\n timeZone: text(row, \"time_zone\") ?? \"UTC\",\n agenda: text(row, \"agenda\"),\n recurrenceRule: text(row, \"recurrence_rule\"),\n joinBeforeHost: bool(row, \"join_before_host\", true),\n cancelledAt: text(row, \"cancelled_at\"),\n cancellationReason: text(row, \"cancellation_reason\"),\n deletedAt: text(row, \"deleted_at\"),\n calendarSequence: typeof row[\"calendar_sequence\"] === \"number\" ? row[\"calendar_sequence\"] : 0,\n version: typeof row[\"version\"] === \"number\" ? row[\"version\"] : 1,\n };\n }\n\n function projectRecording(row: Row): RecordingRecord {\n const operation = \"meet.projectRecording\";\n const state = text(row, \"state\");\n const known: readonly RecordingState[] = [\n \"idle\",\n \"starting\",\n \"recording\",\n \"stopping\",\n \"processing\",\n \"available\",\n \"expired\",\n \"failed\",\n ];\n return {\n id: requireText(row, \"id\", operation),\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n state: known.includes(state as RecordingState) ? (state as RecordingState) : \"failed\",\n startedAt: text(row, \"started_at\"),\n endedAt: text(row, \"ended_at\"),\n startedBy: text(row, \"started_by\") === null ? null : asUserId(text(row, \"started_by\") as string),\n // A PLATFORM file id, never a provider URL (R3). A recording that only\n // exists as a LiveKit URL is orphaned the day the room is garbage\n // collected, which is the whole reason this column is not `url`.\n fileId: text(row, \"file_id\"),\n failureReason: text(row, \"failure_reason\"),\n retention: projectRecordingRetention(row[\"metadata\"]),\n };\n }\n\n function projectRecordingRetention(value: unknown): RecordingRetention | null {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return null;\n const retention = (value as Record<string, unknown>)[\"retention\"];\n if (typeof retention !== \"object\" || retention === null || Array.isArray(retention)) return null;\n const row = retention as Record<string, unknown>;\n const phase = row[\"phase\"];\n const operationId = row[\"operation_id\"];\n const policyId = row[\"policy_id\"];\n const policyUpdatedAt = row[\"policy_updated_at\"];\n const dueAt = row[\"due_at\"];\n const expiredAt = row[\"expired_at\"];\n if (\n (phase !== \"warning\" && phase !== \"expired\") ||\n typeof operationId !== \"string\" ||\n typeof policyId !== \"string\" ||\n typeof policyUpdatedAt !== \"string\" ||\n typeof dueAt !== \"string\" ||\n (expiredAt !== null && typeof expiredAt !== \"string\")\n ) return null;\n return { operationId, policyId, policyUpdatedAt, phase, dueAt, expiredAt };\n }\n\n function projectAttendee(row: Row): MeetingAttendee {\n const operation = \"meet.projectAttendee\";\n const role = text(row, \"role\");\n const admission = text(row, \"admission_state\");\n const known: readonly ParticipantRole[] = [\"host\", \"cohost\", \"presenter\", \"participant\", \"viewer\"];\n const states: readonly AdmissionState[] = [\n \"waiting_host\",\n \"knocking\",\n \"admitted\",\n \"denied\",\n \"expired\",\n \"withdrawn\",\n \"removed\",\n \"ended\",\n ];\n const userId = text(row, \"participant_user_id\");\n const identity = requireText(row, \"identity\", operation);\n return {\n id: requireText(row, \"id\", operation),\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n identity: asParticipantIdentity(identity),\n // A nameless attendee is a row nobody can read. The identity is a worse\n // name than a real one and an infinitely better one than a blank.\n displayName: text(row, \"display_name\") ?? identity,\n avatarUrl: text(row, \"avatar_url\"),\n userId: userId === null ? null : asUserId(userId),\n role: known.includes(role as ParticipantRole) ? (role as ParticipantRole) : \"participant\",\n admissionState: states.includes(admission as AdmissionState)\n ? (admission as AdmissionState)\n : \"admitted\",\n knockedAt: text(row, \"knocked_at\"),\n knockExpiresAt: text(row, \"knock_expires_at\"),\n // The list read selects `metadata->>knock_flag`; a Postgres Changes row carries `metadata`.\n knockFlag:\n text(row, \"knock_flag\") ??\n (typeof row[\"metadata\"] === \"object\" && row[\"metadata\"] !== null\n ? text(row[\"metadata\"] as Row, \"knock_flag\")\n : null),\n isAgent: bool(row, \"is_agent\", false),\n joinedAt: text(row, \"joined_at\"),\n leftAt: text(row, \"left_at\"),\n sessions: projectStays(row[\"sessions\"]),\n };\n }\n\n function requireOrg(organizationId: OrganizationId, operation: string): void {\n if (typeof organizationId !== \"string\" || organizationId.length === 0) {\n throw new MeetError(\n \"misconfigured\",\n `${operation}: no organization_id`,\n \"Every meet write carries an explicit organization_id (R5). The package refuses the \" +\n \"write rather than letting an unscoped row reach the database.\",\n );\n }\n }\n\n\n /**\n * S5 (CORE-DESIGN §2.3): the note-taker and intelligence state lives on the RUN\n * (`meet_sessions.note_taker`), never on the meeting row. Every reader of\n * `meeting.metadata.note_taker` (record view's wrap-up status, the note-taker line) reads\n * the meeting's CURRENT run through this one seam — the open run, else the latest. A\n * reader with no grant on runs (a guest) keeps the row as it is; their state arrives on\n * the room's `matrx.meet.note_taker` event.\n */\n async function withRunNoteTaker(meeting: MeetingRecord): Promise<MeetingRecord> {\n const response = await db\n .from<Row>(TABLES.sessions)\n .select(\"note_taker,started_at\")\n .eq(\"meeting_id\", meeting.id)\n .order(\"started_at\", { ascending: false })\n .limit(1);\n const run = response.error === null ? (response.data ?? [])[0] : undefined;\n const noteTaker = run?.[\"note_taker\"];\n if (typeof noteTaker !== \"object\" || noteTaker === null || Array.isArray(noteTaker)) {\n return meeting;\n }\n return {\n ...meeting,\n metadata: { ...(meeting.metadata ?? {}), note_taker: noteTaker } as MeetingRecord[\"metadata\"],\n };\n }\n\n return {\n schema: MEET_SCHEMA,\n tables: TABLES,\n projectInvite,\n projectMeeting,\n projectSegment,\n projectNote,\n projectRecording,\n projectAttendee,\n projectInvitee,\n\n async scheduleMeeting(input) {\n requireOrg(input.organizationId, \"meet.scheduleMeeting\");\n const row = await guard<Row>(\"meet.scheduleMeeting\", () =>\n db.rpc<Row>(RPCS.scheduleMeeting, {\n p_organization_id: input.organizationId,\n p_host_user_id: input.hostUserId,\n p_title: input.title,\n p_scheduled_for: input.scheduledFor,\n p_time_zone: input.timeZone,\n p_duration_minutes: input.durationMinutes ?? null,\n p_agenda: input.agenda ?? null,\n p_recurrence_rule: input.recurrenceRule ?? null,\n p_settings: scheduleSettingsPatch(input.settings ?? {}, input.appPanel, input.observers),\n }),\n );\n return projectMeeting(row);\n },\n\n async updateMeeting(meetingId, changes, options = {}) {\n const row = await guard<Row>(\"meet.updateMeeting\", () =>\n db.rpc<Row>(RPCS.updateMeeting, {\n p_meeting_id: meetingId,\n p_changes: changesPatch(changes),\n p_expected_version: options.expectedVersion ?? null,\n p_by_user_id: options.byUserId ?? null,\n }),\n );\n return projectMeeting(row);\n },\n\n async cancelMeeting(meetingId, reason, byUserId = null) {\n const row = await guard<Row>(\"meet.cancelMeeting\", () =>\n db.rpc<Row>(RPCS.cancelMeeting, {\n p_meeting_id: meetingId,\n p_reason: reason,\n p_by_user_id: byUserId,\n }),\n );\n return projectMeeting(row);\n },\n\n async archiveMeeting(meetingId, byUserId = null) {\n const row = await guard<Row>(\"meet.archiveMeeting\", () =>\n db.rpc<Row>(RPCS.archiveMeeting, { p_meeting_id: meetingId, p_by_user_id: byUserId }),\n );\n return projectMeeting(row);\n },\n\n async restoreMeeting(meetingId, byUserId = null) {\n const row = await guard<Row>(\"meet.restoreMeeting\", () =>\n db.rpc<Row>(RPCS.restoreMeeting, { p_meeting_id: meetingId, p_by_user_id: byUserId }),\n );\n return projectMeeting(row);\n },\n\n async setOccurrence(args) {\n const row = await guard<Row>(\"meet.setOccurrence\", () =>\n db.rpc<Row>(RPCS.setOccurrence, {\n p_meeting_id: args.meetingId,\n p_original_start: args.originalStart,\n p_action: args.action,\n p_new_start: args.newStart ?? null,\n p_new_duration_minutes: args.newDurationMinutes ?? null,\n p_reason: args.reason ?? null,\n p_by_user_id: args.byUserId ?? null,\n }),\n );\n return projectOverride(row);\n },\n\n async meetingOccurrences(meetingId, { from, to, limit = 200 }) {\n const rows = await guard<Row[]>(\"meet.meetingOccurrences\", () =>\n db.rpc<Row[]>(RPCS.meetingOccurrences, {\n p_meeting_id: meetingId,\n p_from: from,\n p_to: to,\n p_limit: limit,\n }),\n );\n return rows.map(projectOccurrence);\n },\n\n async occurrencesBetween({ from, to, organizationId = null, limit = 500 }) {\n const rows = await guard<Row[]>(\"meet.occurrencesBetween\", () =>\n db.rpc<Row[]>(RPCS.occurrencesBetween, {\n p_from: from,\n p_to: to,\n p_organization_id: organizationId,\n p_limit: limit,\n }),\n );\n return rows.map(projectUpcoming);\n },\n\n async invitees(meetingId) {\n const response = await db\n .from<Row>(TABLES.invitees)\n .select(INVITEE_COLUMNS)\n .eq(\"meeting_id\", meetingId)\n .is(\"deleted_at\", null)\n .order(\"created_at\", { ascending: true })\n .order(\"id\", { ascending: true });\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.invitees\");\n return (response.data ?? []).map(projectInvitee);\n },\n\n async addInvitees(meetingId, people, byUserId = null) {\n const rows = await guard<Row[]>(\"meet.addInvitees\", () =>\n db.rpc<Row[]>(RPCS.addInvitees, {\n p_meeting_id: meetingId,\n p_invitees: people.map((person) => ({\n ...(person.userId !== undefined ? { user_id: person.userId } : {}),\n ...(person.email !== undefined ? { email: person.email } : {}),\n ...(person.displayName !== undefined ? { display_name: person.displayName } : {}),\n ...(person.role !== undefined ? { role: person.role } : {}),\n })),\n p_by_user_id: byUserId,\n }),\n );\n return rows.map(projectInvitee);\n },\n\n async setInviteeRole(inviteeId, role, byUserId = null) {\n const row = await guard<Row>(\"meet.setInviteeRole\", () =>\n db.rpc<Row>(RPCS.setInviteeRole, {\n p_invitee_id: inviteeId,\n p_role: role,\n p_by_user_id: byUserId,\n }),\n );\n return projectInvitee(row);\n },\n\n async removeInvitee(inviteeId, byUserId = null) {\n const row = await guard<Row>(\"meet.removeInvitee\", () =>\n db.rpc<Row>(RPCS.removeInvitee, { p_invitee_id: inviteeId, p_by_user_id: byUserId }),\n );\n return projectInvitee(row);\n },\n\n async respond(meetingId, answer, note = null) {\n const row = await guard<Row>(\"meet.respond\", () =>\n db.rpc<Row>(RPCS.respond, {\n p_meeting_id: meetingId,\n p_response: answer,\n p_note: note,\n p_by_user_id: null,\n }),\n );\n return projectInvitee(row);\n },\n\n async invitationByToken(secret) {\n const body = await guard<unknown>(\"meet.invitationByToken\", () =>\n db.rpc<unknown>(RPCS.invitationByToken, { p_secret: secret }),\n );\n return projectRsvpInvitation(body);\n },\n\n async respondByToken(secret, answer, note = null) {\n const body = await guard<unknown>(\"meet.respondByToken\", () =>\n db.rpc<unknown>(RPCS.respondByToken, {\n p_secret: secret,\n p_response: answer,\n p_note: note,\n }),\n );\n return projectRsvpInvitation(body);\n },\n\n async attendees(meetingId) {\n const response = await db\n .from<Row>(TABLES.participants)\n .select(ATTENDEE_COLUMNS)\n .eq(\"meeting_id\", meetingId)\n .order(\"joined_at\", { ascending: true })\n .order(\"id\", { ascending: true });\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.attendees\");\n return (response.data ?? []).map(projectAttendee);\n },\n\n async createCallInvite(input) {\n requireOrg(input.organizationId, \"meet.createCallInvite\");\n const row = await guard<Row>(\"meet.createCallInvite\", () =>\n db.rpc<Row>(RPCS.createCallInvite, {\n p_room_name: input.roomName,\n p_mode: input.mode,\n p_caller_user_id: input.callerUserId,\n p_caller_name: input.callerName,\n p_caller_avatar_url: input.callerAvatarUrl,\n p_callee_ids: input.calleeUserIds,\n p_organization_id: input.organizationId,\n p_conversation_id: input.conversationId,\n p_expires_at: input.expiresAt,\n }),\n );\n return projectInvite(row);\n },\n\n async settleCallInvite(id, state, declineMessage) {\n const row = await guard<Row>(\"meet.settleCallInvite\", () =>\n db.rpc<Row>(RPCS.settleCallInvite, {\n p_invite_id: id,\n p_state: state,\n p_decline_message: declineMessage,\n }),\n );\n return projectInvite(row);\n },\n\n async pendingInvites(userId) {\n const rows = await guard<Row[]>(\"meet.pendingInvites\", () =>\n db.rpc<Row[]>(RPCS.pendingCallInvites, { p_user_id: userId }),\n );\n return rows.map(projectInvite);\n },\n\n async getOrCreateMeeting(input) {\n requireOrg(input.organizationId, \"meet.getOrCreateMeeting\");\n const row = await guard<Row>(\"meet.getOrCreateMeeting\", () =>\n db.rpc<Row>(RPCS.getOrCreateMeeting, {\n p_organization_id: input.organizationId,\n p_host_user_id: input.hostUserId,\n p_title: input.title,\n p_kind: input.kind,\n p_scheduled_for: input.scheduledFor ?? null,\n p_scheduled_duration_minutes: input.scheduledDurationMinutes ?? null,\n p_lobby_enabled: input.lobbyEnabled ?? true,\n p_recording_policy: input.recordingPolicy ?? \"host-controlled\",\n p_ai_enabled: input.aiEnabled ?? true,\n p_slug: input.slug ?? null,\n }),\n );\n return projectMeeting(row);\n },\n\n async meetingBySlug(slug) {\n // A DURABLE LINK RESOLVES BEFORE, DURING, AND AFTER (R3). This RPC is not\n // filtered by `ended_at is null` on purpose: a link that dies the moment\n // the meeting ends breaks every \"here are the notes\" follow-up.\n const [row, linkRun] = await Promise.all([\n guard<Row>(\"meet.meetingBySlug\", () => db.rpc<Row>(RPCS.meetingBySlug, { p_slug: slug })),\n readLinkRun(slug),\n ]);\n // AWAITED: spreading the un-awaited Promise dropped every field but `linkRun` (no roomName → the\n // room page crashed on `meet-room requires part roomName`).\n const record = await withRunNoteTaker(projectMeeting(row));\n return linkRun === undefined ? record : { ...record, linkRun };\n },\n\n async meeting(id) {\n const response = await db\n .from<Row>(TABLES.meetings)\n .select(MEETING_COLUMNS)\n .eq(\"id\", id)\n .single();\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.meeting\");\n if (response.data === null) throw invalidResponse(\"meet.meeting\", \"no such meeting\");\n return withRunNoteTaker(projectMeeting(response.data));\n },\n\n async listMeetings({ organizationId = null, limit = 25, before = null }) {\n let query = db\n .from<Row>(TABLES.meetings)\n .select(MEETING_COLUMNS);\n if (organizationId !== null) query = query.eq(\"organization_id\", organizationId);\n query = query\n .order(\"scheduled_for\", { ascending: false })\n .order(\"id\", { ascending: false })\n // ASK FOR ONE MORE THAN THE PAGE, so `hasMore` is a fact rather than a\n // guess from \"we got a full page\".\n .limit(limit + 1);\n if (before !== null) {\n query = query.or(\n `scheduled_for.lt.${before.scheduledFor},and(scheduled_for.eq.${before.scheduledFor},id.lt.${before.id})`,\n );\n }\n const response = await query;\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.listMeetings\");\n const rows = response.data ?? [];\n return {\n items: rows.slice(0, limit).map(projectMeeting),\n hasMore: rows.length > limit,\n };\n },\n\n async decideAdmission({ meetingId, identity, decision, byUserId }) {\n const fn = decision === \"admit\" ? RPCS.admit : decision === \"deny\" ? RPCS.deny : RPCS.undeny;\n const row = await guard<Row>(`meet.${decision}`, () =>\n db.rpc<Row>(fn, {\n p_meeting_id: meetingId,\n p_identity: identity,\n p_by_user_id: byUserId,\n }),\n );\n return projectAttendee(row);\n },\n\n async admitAll(meetingId, byUserId) {\n const response = await db.rpc<Row[]>(RPCS.admitAll, {\n p_meeting_id: meetingId,\n p_by_user_id: byUserId,\n });\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.admitAll\");\n const rows = Array.isArray(response.data) ? (response.data as Row[]) : [];\n return rows.map(projectAttendee);\n },\n\n async setLock(meetingId, locked, byUserId) {\n const row = await guard<Row>(\"meet.setLock\", () =>\n db.rpc<Row>(RPCS.setLock, {\n p_meeting_id: meetingId,\n p_locked: locked,\n p_by_user_id: byUserId,\n }),\n );\n return projectMeeting(row);\n },\n\n async meetingRule(meetingId, key) {\n const response = await db.rpc<unknown>(RPCS.meetingRule, { p_meeting_id: meetingId, p_key: key });\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.meetingRule\");\n return response.data;\n },\n\n async setMeetingRule(meetingId, key, value) {\n const response = await db.rpc<Row>(RPCS.setMeetingRule, { p_meeting_id: meetingId, p_key: key, p_value: value });\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.setMeetingRule\");\n const answer = (response.data ?? {}) as Row;\n if (answer[\"ok\"] !== true) {\n throw new MeetError(\n \"forbidden\",\n typeof answer[\"detail\"] === \"string\" ? answer[\"detail\"] : \"This meeting's rule was not changed.\",\n \"Only the meeting's host or a co-host sets its rules; an organization can lock a rule for everyone.\",\n answer,\n );\n }\n return answer[\"effective_value\"];\n },\n\n async transcript(meetingId, limit = 1000) {\n const response = await db\n .from<Row>(TABLES.transcripts)\n .select(SEGMENT_COLUMNS)\n .eq(\"meeting_id\", meetingId)\n .order(\"started_at\", { ascending: true })\n .order(\"id\", { ascending: true })\n .limit(limit);\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.transcript\");\n return (response.data ?? []).map(projectSegment);\n },\n\n async notes(meetingId) {\n const response = await db\n .from<Row>(TABLES.notes)\n .select(NOTE_COLUMNS)\n .eq(\"meeting_id\", meetingId)\n .order(\"created_at\", { ascending: true })\n .order(\"id\", { ascending: true });\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.notes\");\n return (response.data ?? []).map(projectNote);\n },\n\n async recordConsent({ meetingId, identity, acknowledgedAt }) {\n await guardVoid(\"meet.recordConsent\", () =>\n db.rpc<Row>(RPCS.recordConsent, {\n p_meeting_id: meetingId,\n p_identity: identity,\n p_acknowledged_at: acknowledgedAt,\n }),\n );\n },\n\n async recordings(meetingId) {\n const response = await db\n .from<Row>(TABLES.recordings)\n .select(RECORDING_COLUMNS)\n .eq(\"meeting_id\", meetingId)\n // Meet wave 3: an archived recording is off the record as well as the library.\n .is(\"deleted_at\", null)\n .order(\"started_at\", { ascending: false });\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.recordings\");\n return (response.data ?? []).map(projectRecording);\n },\n\n async myRecordings({ includeArchived = false, limit = 200 } = {}) {\n const rows = await guard<Row[]>(\"meet.myRecordings\", () =>\n db.rpc<Row[]>(RPCS.myRecordings, { p_include_archived: includeArchived, p_limit: limit }),\n );\n return rows.map(projectLibraryRecording);\n },\n\n async searchMeetings(query, limit = 60) {\n if (query.trim().length === 0) return [];\n const rows = await guard<Row[]>(\"meet.searchMeetings\", () =>\n db.rpc<Row[]>(RPCS.search, { p_query: query, p_limit: limit }),\n );\n return rows.map(projectSearchHit);\n },\n\n async renameRecording(recordingId, title) {\n const clean = title === null ? null : title.trim().slice(0, 200) || null;\n const response = await db\n .from<Row>(TABLES.recordings)\n .update({ title: clean })\n .eq(\"id\", recordingId)\n .select(\"id\");\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.renameRecording\");\n if ((response.data ?? []).length === 0) {\n throw normalizeMeetError(\n { message: \"permission denied: only the meeting's host or an editor can rename it\", code: \"42501\" },\n \"meet.renameRecording\",\n );\n }\n },\n\n async setRecordingArchived(recordingId, archived) {\n const response = await db\n .from<Row>(TABLES.recordings)\n .update({ deleted_at: archived ? new Date().toISOString() : null })\n .eq(\"id\", recordingId)\n .select(\"id\");\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.setRecordingArchived\");\n if ((response.data ?? []).length === 0) {\n throw normalizeMeetError(\n { message: \"permission denied: only the meeting's host or an editor can archive it\", code: \"42501\" },\n \"meet.setRecordingArchived\",\n );\n }\n },\n };\n}\n\n/** One row of the recordings library (`meet_my_recordings`). */\nexport interface LibraryRecording {\n readonly id: string;\n readonly meetingId: MeetingId;\n readonly meetingTitle: string;\n readonly meetingSlug: string;\n /** The reader's strongest role on the meeting. */\n readonly role: \"host\" | \"cohost\" | \"invitee\" | \"attendee\";\n readonly state: RecordingState;\n /** A name a host gave it; null = show the meeting title. */\n readonly title: string | null;\n readonly fileId: string | null;\n readonly startedAt: string | null;\n readonly endedAt: string | null;\n readonly durationMs: number | null;\n readonly sizeBytes: number | null;\n readonly failureReason: string | null;\n readonly archivedAt: string | null;\n readonly organizationId: OrganizationId;\n}\n\nconst LIBRARY_STATES: readonly RecordingState[] = [\n \"idle\",\n \"starting\",\n \"recording\",\n \"stopping\",\n \"processing\",\n \"available\",\n \"expired\",\n \"failed\",\n];\n\nexport function projectLibraryRecording(row: Row): LibraryRecording {\n const operation = \"meet.projectLibraryRecording\";\n const state = text(row, \"state\");\n const role = text(row, \"my_role\");\n return {\n id: requireText(row, \"recording_id\", operation),\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n meetingTitle: text(row, \"meeting_title\") ?? \"Meeting\",\n meetingSlug: requireText(row, \"meeting_slug\", operation),\n role: role === \"host\" || role === \"cohost\" || role === \"invitee\" ? role : \"attendee\",\n state: LIBRARY_STATES.includes(state as RecordingState) ? (state as RecordingState) : \"failed\",\n title: text(row, \"recording_title\"),\n fileId: text(row, \"file_id\"),\n startedAt: text(row, \"started_at\"),\n endedAt: text(row, \"ended_at\"),\n durationMs: num(row, \"duration_ms\"),\n sizeBytes: num(row, \"size_bytes\"),\n failureReason: text(row, \"failure_reason\"),\n archivedAt: text(row, \"deleted_at\"),\n organizationId: asOrganizationId(requireText(row, \"organization_id\", operation)),\n };\n}\n\n/** Where a search hit was found. */\nexport type MeetingSearchSource =\n | \"title\"\n | \"summary\"\n | \"decision\"\n | \"action_item\"\n | \"note\"\n | \"transcript\";\n\n/** One hit from `meet_search`. `snippet` marks matched words with ⟦ ⟧. */\nexport interface MeetingSearchHit {\n readonly meetingId: MeetingId;\n readonly meetingTitle: string;\n readonly meetingSlug: string;\n readonly meetingAt: string | null;\n readonly source: MeetingSearchSource;\n /** The note or transcript segment id (the meeting id for a title hit). */\n readonly refId: string;\n readonly speaker: string | null;\n readonly snippet: string;\n /** When it was said / written. */\n readonly at: string | null;\n readonly rank: number;\n}\n\nconst SEARCH_SOURCES: readonly MeetingSearchSource[] = [\n \"title\",\n \"summary\",\n \"decision\",\n \"action_item\",\n \"note\",\n \"transcript\",\n];\n\nexport function projectSearchHit(row: Row): MeetingSearchHit {\n const operation = \"meet.projectSearchHit\";\n const source = text(row, \"source\");\n return {\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n meetingTitle: text(row, \"meeting_title\") ?? \"Meeting\",\n meetingSlug: requireText(row, \"meeting_slug\", operation),\n meetingAt: text(row, \"meeting_at\"),\n source: SEARCH_SOURCES.includes(source as MeetingSearchSource)\n ? (source as MeetingSearchSource)\n : \"note\",\n refId: requireText(row, \"ref_id\", operation),\n speaker: text(row, \"speaker\"),\n snippet: text(row, \"snippet\") ?? \"\",\n at: text(row, \"said_at\"),\n rank: num(row, \"rank\") ?? 0,\n };\n}\n\n/** A snippet split into plain and matched runs, for highlighting without HTML. */\nexport function snippetRuns(snippet: string): readonly { readonly text: string; readonly hit: boolean }[] {\n const runs: { text: string; hit: boolean }[] = [];\n const pattern = /⟦([^⟧]*)⟧/g;\n let last = 0;\n for (const match of snippet.matchAll(pattern)) {\n const start = match.index ?? 0;\n if (start > last) runs.push({ text: snippet.slice(last, start), hit: false });\n runs.push({ text: match[1] ?? \"\", hit: true });\n last = start + match[0].length;\n }\n if (last < snippet.length) runs.push({ text: snippet.slice(last), hit: false });\n return runs;\n}\n\n/** `meet_link_state` → the typed run state; anything not understood is \"unknown\", never a guess. */\nexport function projectLinkRun(value: unknown): LinkRunState | undefined {\n if (typeof value !== \"object\" || value === null) return undefined;\n const row = value as Record<string, unknown>;\n const state = row[\"state\"];\n if (state !== \"open\" && state !== \"not_started\" && state !== \"ended\" && state !== \"cancelled\") return undefined;\n const reason = row[\"reason\"] === \"ended\" || row[\"reason\"] === \"cancelled\" ? row[\"reason\"] : null;\n const nextStart = typeof row[\"next_start\"] === \"string\" ? row[\"next_start\"] : null;\n return { state, reason, nextStart };\n}\n","/**\n * THE APP PANEL SLOT (HR-360 wave 2, Meet register MD-15).\n *\n * A meeting can carry one APP PANEL: a surface the HOST APP registered under a key, opened\n * beside the call on one record (`meet_meetings.metadata.app_panel = {key, record_id}`,\n * written by `meet_schedule_meeting`'s `app_panel` setting). The package knows only the key\n * and the record id; the host app's registered component reads the record through the\n * person's OWN doors, so someone who cannot read the record sees nothing of it.\n *\n * Everyone in the call looks at the same SECTION of the panel: one shared focus per run,\n * kept by the server and carried on the existing collab packet (`collab.focus`,\n * action `focus_set`) — never a second channel.\n */\n\nimport { settingsPatch } from \"./repository\";\nimport type { MeetingRecord, MeetingSettings } from \"./types\";\n\nexport interface MeetingAppPanel {\n /** The registered surface key, e.g. `hr.review_360`. */\n readonly key: string;\n /** The record the panel opens on. */\n readonly recordId: string;\n}\n\n/** The app panel this meeting carries, or null. Malformed values are never guessed at. */\nexport function appPanelOf(meeting: Pick<MeetingRecord, \"metadata\"> | null | undefined): MeetingAppPanel | null {\n const raw = meeting?.metadata?.[\"app_panel\"];\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) return null;\n const key = (raw as Record<string, unknown>)[\"key\"];\n const recordId = (raw as Record<string, unknown>)[\"record_id\"];\n if (typeof key !== \"string\" || key.length === 0) return null;\n if (typeof recordId !== \"string\" || recordId.length === 0) return null;\n return { key, recordId };\n}\n\n/**\n * HR-360 wave 3: the server held capture off because the meeting is about a Confidential record\n * (`metadata.capture_hold = \"confidential_record\"`, stamped by the meet_meetings trigger) and it\n * is still off. The room says so in one short line. Once the organization turns capture back on\n * (knob `meet.confidential_capture`), it is no longer held.\n */\nexport function captureHeldOf(\n meeting: Pick<MeetingRecord, \"metadata\" | \"aiEnabled\" | \"recordingPolicy\"> | null | undefined,\n): boolean {\n if (!meeting) return false;\n return (\n meeting.metadata?.[\"capture_hold\"] === \"confidential_record\" &&\n !meeting.aiEnabled &&\n meeting.recordingPolicy === \"disabled\"\n );\n}\n\n/** The `p_settings` of `meet_schedule_meeting`, with the panel and the named observers. */\nexport function scheduleSettingsPatch(\n settings: MeetingSettings,\n appPanel: MeetingAppPanel | undefined,\n observers: readonly string[] | undefined,\n): Record<string, unknown> {\n const patch = settingsPatch(settings);\n if (appPanel !== undefined) patch[\"app_panel\"] = { key: appPanel.key, record_id: appPanel.recordId };\n if (observers !== undefined && observers.length > 0) patch[\"observers\"] = [...observers];\n return patch;\n}\n\n/** The one shared section focus of the meeting's app panel (`collab.focus`). */\nexport interface MeetAppFocus {\n readonly panel: string;\n readonly section: string;\n readonly byName: string;\n readonly at: string;\n}\n\nexport function focusFromRow(value: unknown): MeetAppFocus | null {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return null;\n const row = value as Record<string, unknown>;\n if (typeof row[\"panel\"] !== \"string\") return null;\n return {\n panel: row[\"panel\"],\n section: typeof row[\"section\"] === \"string\" ? row[\"section\"] : \"\",\n byName: typeof row[\"by_name\"] === \"string\" ? row[\"by_name\"] : \"\",\n at: typeof row[\"at\"] === \"string\" ? row[\"at\"] : \"\",\n };\n}\n","/**\n * MEETING ROOM ITEMS — polls, Q&A, the shared whiteboard and breakout rooms (Meet wave 5).\n *\n * The server decides every one of them (`aidream/services/meet/collab.py`,\n * `breakouts.py`): who may create, launch, vote, ask, answer, draw, erase, open rooms or\n * move people — and what each viewer SEES (a draft poll is the host's alone; counts\n * reach everyone only once shared; anonymous votes and questions carry no identity;\n * a dismissed question stays with the hosts). This file only holds what the server\n * said and folds the room's `matrx.meet.collab` packets into it.\n *\n * Two reads, one truth: `GET /v1/meet/collab` once per join (a late joiner sees every\n * poll, question, stroke and the open breakout plan), then packets. Every item is keyed\n * by its row id, so the same item arriving twice is one item.\n */\n\nimport { readServerRefusal } from \"./ai\";\nimport type { MeetApi } from \"./api\";\nimport { MEET_ROUTES, MEETING_PASS_HEADER } from \"./tokens\";\nimport type { ParticipantIdentity } from \"./types\";\nimport { focusFromRow, type MeetAppFocus } from \"./app-panel\";\n\nexport interface MeetPollOption {\n readonly id: string;\n readonly text: string;\n /** Present when this viewer may see counts (a host, or results shared). */\n readonly votes?: number | undefined;\n /** Who picked it — hosts only, never for an anonymous poll. */\n readonly voters?: readonly string[] | undefined;\n}\n\nexport type MeetPollState = \"draft\" | \"live\" | \"closed\";\n\nexport interface MeetPoll {\n readonly id: string;\n readonly question: string;\n readonly options: readonly MeetPollOption[];\n readonly multiple: boolean;\n readonly anonymous: boolean;\n readonly state: MeetPollState;\n readonly resultsShared: boolean;\n readonly voterCount: number | null;\n /** This viewer's own answer — kept locally, since a room packet cannot know it. */\n readonly myVote: readonly string[];\n readonly createdByName: string;\n readonly createdAt: string | null;\n}\n\nexport type MeetQuestionState = \"open\" | \"answered\" | \"dismissed\";\n\nexport interface MeetQuestion {\n readonly id: string;\n readonly text: string;\n readonly askerName: string;\n readonly anonymous: boolean;\n readonly state: MeetQuestionState;\n readonly answer: string | null;\n readonly answeredByName: string | null;\n readonly upvotes: number;\n readonly upvotedByMe: boolean;\n readonly mine: boolean;\n readonly createdAt: string | null;\n}\n\nexport interface MeetStroke {\n readonly id: string;\n /** Points in the board's own 0..1 space. */\n readonly points: readonly (readonly [number, number])[];\n readonly color: string;\n readonly width: number;\n readonly tool: \"pen\" | \"highlighter\";\n readonly by: string;\n readonly byName: string;\n /** True while this browser's stroke has not come back from the server. */\n readonly pending?: boolean | undefined;\n}\n\nexport interface MeetBreakoutRoom {\n readonly id: string;\n readonly name: string;\n readonly identities: readonly string[];\n}\n\nexport interface MeetBreakoutBroadcast {\n readonly text: string;\n readonly by: string;\n readonly at: string;\n}\n\nexport interface MeetBreakoutPlan {\n readonly id: string;\n readonly state: \"open\" | \"closed\";\n readonly rooms: readonly MeetBreakoutRoom[];\n readonly allowChoose: boolean;\n readonly minutes: number;\n readonly openedAt: string | null;\n readonly endsAt: string | null;\n /** Set while the host's closing countdown runs: everyone returns to the main meeting at this time. */\n readonly closingAt: string | null;\n /** `invite` (Join / Later) or `auto` (a countdown, then moved) — the org's `meet.breakout_move`. */\n readonly moveMode: \"invite\" | \"auto\";\n readonly closedAt: string | null;\n readonly broadcasts: readonly MeetBreakoutBroadcast[];\n}\n\n/** The org's knobs for this meeting — which controls EXIST (absent, never dead). */\nexport interface MeetCollabSettings {\n readonly pollsEnabled: boolean;\n readonly pollMaxOptions: number;\n readonly qaEnabled: boolean;\n readonly qaAnonymousAllowed: boolean;\n readonly whiteboardEnabled: boolean;\n readonly breakoutsEnabled: boolean;\n readonly breakoutMaxRooms: number;\n readonly breakoutDefaultMinutes: number;\n readonly captionTranslationEnabled: boolean;\n /** Empty = the viewer's own language. */\n readonly captionTranslationLanguage: string;\n}\n\nexport type CollabPanel = \"polls\" | \"qa\" | \"whiteboard\" | \"breakouts\";\n\nexport interface CollabSnapshot {\n readonly loaded: boolean;\n /** Why the state could not be read, in words — or null. */\n readonly error: string | null;\n readonly settings: MeetCollabSettings | null;\n readonly polls: readonly MeetPoll[];\n readonly questions: readonly MeetQuestion[];\n readonly strokes: readonly MeetStroke[];\n readonly breakout: MeetBreakoutPlan | null;\n /** The newest message a host sent to every breakout room, for the banner. */\n readonly broadcast: MeetBreakoutBroadcast | null;\n /** A host moved THIS person (or everyone came back): where to go. `undefined` = nothing asked. */\n readonly moveRequest: { readonly roomId: string | null; readonly at: number } | null;\n /** Something asked a panel to open (a poll launched, a question arrived for a host). */\n readonly panelRequest: { readonly panel: CollabPanel; readonly at: number } | null;\n /** Caption translations for THIS viewer: line id → text, for `captionLanguage`. */\n readonly captionLanguage: string | null;\n readonly captionTranslations: Readonly<Record<string, string>>;\n readonly captionTranslationError: string | null;\n /**\n * HR-360 wave 2 (MD-15): the meeting's app panel's ONE shared section focus for this run,\n * as the server keeps it (`focus_set`); optional so a snapshot built before 0.11 compiles.\n */\n readonly focus?: MeetAppFocus | null;\n}\n\nexport const EMPTY_COLLAB: CollabSnapshot = {\n loaded: false,\n error: null,\n settings: null,\n polls: [],\n questions: [],\n strokes: [],\n breakout: null,\n broadcast: null,\n moveRequest: null,\n panelRequest: null,\n captionLanguage: null,\n captionTranslations: {},\n captionTranslationError: null,\n focus: null,\n};\n\ntype Row = Record<string, unknown>;\n\nfunction obj(value: unknown): Row | null {\n return typeof value === \"object\" && value !== null && !Array.isArray(value) ? (value as Row) : null;\n}\nfunction str(value: unknown): string | null {\n return typeof value === \"string\" ? value : null;\n}\nfunction num(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null;\n}\nfunction strings(value: unknown): string[] {\n return Array.isArray(value) ? value.filter((v): v is string => typeof v === \"string\") : [];\n}\n\nexport function pollFromRow(row: Row): MeetPoll | null {\n const id = str(row[\"id\"]);\n if (id === null) return null;\n const options: MeetPollOption[] = [];\n for (const raw of Array.isArray(row[\"options\"]) ? row[\"options\"] : []) {\n const option = obj(raw);\n const optionId = option === null ? null : str(option[\"id\"]);\n if (option === null || optionId === null) continue;\n const votes = num(option[\"votes\"]);\n options.push({\n id: optionId,\n text: str(option[\"text\"]) ?? \"\",\n ...(votes !== null ? { votes } : {}),\n ...(Array.isArray(option[\"voters\"]) ? { voters: strings(option[\"voters\"]) } : {}),\n });\n }\n const state = str(row[\"state\"]);\n return {\n id,\n question: str(row[\"question\"]) ?? \"\",\n options,\n multiple: row[\"multiple\"] === true,\n anonymous: row[\"anonymous\"] === true,\n state: state === \"live\" || state === \"closed\" ? state : \"draft\",\n resultsShared: row[\"results_shared\"] === true,\n voterCount: num(row[\"voter_count\"]),\n myVote: strings(row[\"my_vote\"]),\n createdByName: str(row[\"created_by_name\"]) ?? \"\",\n createdAt: str(row[\"created_at\"]),\n };\n}\n\nexport function questionFromRow(row: Row): MeetQuestion | null {\n const id = str(row[\"id\"]);\n if (id === null) return null;\n const state = str(row[\"state\"]);\n return {\n id,\n text: str(row[\"text\"]) ?? \"\",\n askerName: str(row[\"asker_name\"]) ?? \"\",\n anonymous: row[\"anonymous\"] === true,\n state: state === \"answered\" || state === \"dismissed\" ? state : \"open\",\n answer: str(row[\"answer\"]),\n answeredByName: str(row[\"answered_by_name\"]),\n upvotes: num(row[\"upvotes\"]) ?? 0,\n upvotedByMe: row[\"upvoted_by_me\"] === true,\n mine: row[\"mine\"] === true,\n createdAt: str(row[\"created_at\"]),\n };\n}\n\nexport function strokeFromRow(row: Row): MeetStroke | null {\n const id = str(row[\"id\"]);\n if (id === null || !Array.isArray(row[\"points\"])) return null;\n const points: (readonly [number, number])[] = [];\n for (const raw of row[\"points\"]) {\n if (Array.isArray(raw) && typeof raw[0] === \"number\" && typeof raw[1] === \"number\") {\n points.push([raw[0], raw[1]]);\n }\n }\n return {\n id,\n points,\n color: str(row[\"color\"]) ?? \"#111827\",\n width: num(row[\"width\"]) ?? 3,\n tool: row[\"tool\"] === \"highlighter\" ? \"highlighter\" : \"pen\",\n by: str(row[\"by\"]) ?? \"\",\n byName: str(row[\"by_name\"]) ?? \"\",\n };\n}\n\nfunction broadcastFrom(value: unknown): MeetBreakoutBroadcast | null {\n const row = obj(value);\n const text = row === null ? null : str(row[\"text\"]);\n if (row === null || text === null) return null;\n return { text, by: str(row[\"by\"]) ?? \"\", at: str(row[\"at\"]) ?? \"\" };\n}\n\nexport function planFromRow(row: Row): MeetBreakoutPlan | null {\n const id = str(row[\"id\"]);\n if (id === null) return null;\n const rooms: MeetBreakoutRoom[] = [];\n for (const raw of Array.isArray(row[\"rooms\"]) ? row[\"rooms\"] : []) {\n const room = obj(raw);\n const roomId = room === null ? null : str(room[\"id\"]);\n if (room === null || roomId === null) continue;\n rooms.push({ id: roomId, name: str(room[\"name\"]) ?? roomId, identities: strings(room[\"identities\"]) });\n }\n return {\n id,\n state: row[\"state\"] === \"open\" ? \"open\" : \"closed\",\n rooms,\n allowChoose: row[\"allow_choose\"] === true,\n minutes: num(row[\"minutes\"]) ?? 0,\n openedAt: str(row[\"opened_at\"]),\n endsAt: str(row[\"ends_at\"]),\n closingAt: str(row[\"closing_at\"]),\n moveMode: row[\"move_mode\"] === \"auto\" ? \"auto\" : \"invite\",\n closedAt: str(row[\"closed_at\"]),\n broadcasts: (Array.isArray(row[\"broadcasts\"]) ? row[\"broadcasts\"] : [])\n .map(broadcastFrom)\n .filter((b): b is MeetBreakoutBroadcast => b !== null),\n };\n}\n\nexport function settingsFromRow(row: Row | null): MeetCollabSettings | null {\n if (row === null) return null;\n return {\n pollsEnabled: row[\"polls_enabled\"] === true,\n pollMaxOptions: num(row[\"poll_max_options\"]) ?? 10,\n qaEnabled: row[\"qa_enabled\"] === true,\n qaAnonymousAllowed: row[\"qa_anonymous_allowed\"] === true,\n whiteboardEnabled: row[\"whiteboard_enabled\"] === true,\n breakoutsEnabled: row[\"breakouts_enabled\"] === true,\n breakoutMaxRooms: num(row[\"breakout_max_rooms\"]) ?? 20,\n breakoutDefaultMinutes: num(row[\"breakout_default_minutes\"]) ?? 15,\n captionTranslationEnabled: row[\"caption_translation_enabled\"] === true,\n captionTranslationLanguage: str(row[\"caption_translation_language\"]) ?? \"\",\n };\n}\n\n/** The server's `GET /collab` body → a loaded snapshot (keeps this viewer's caption state). */\nexport function collabFromState(body: Row, previous: CollabSnapshot): CollabSnapshot {\n const list = <T>(key: string, parse: (row: Row) => T | null): T[] =>\n (Array.isArray(body[key]) ? body[key] : [])\n .map((raw) => {\n const row = obj(raw);\n return row === null ? null : parse(row);\n })\n .filter((item): item is T => item !== null);\n const breakoutRow = obj(body[\"breakout\"]);\n return {\n ...previous,\n loaded: true,\n error: null,\n settings: settingsFromRow(obj(body[\"settings\"])),\n polls: list(\"polls\", pollFromRow),\n questions: list(\"questions\", questionFromRow),\n strokes: list(\"strokes\", strokeFromRow),\n breakout: breakoutRow === null ? null : planFromRow(breakoutRow),\n focus: focusFromRow(body[\"focus\"]),\n };\n}\n\nfunction upsert<T extends { id: string }>(items: readonly T[], next: T): T[] {\n const index = items.findIndex((item) => item.id === next.id);\n if (index === -1) return [...items, next];\n const copy = items.slice();\n copy[index] = next;\n return copy;\n}\n\n/**\n * Fold one server item into the snapshot. `fromRoom` = it came on the room broadcast,\n * which cannot know THIS viewer's vote, upvote or authorship — those are kept from what\n * the viewer's own reads and answers already said.\n */\nexport function withItem(collab: CollabSnapshot, row: Row, fromRoom = false): CollabSnapshot {\n const kind = str(row[\"kind\"]);\n if (kind === \"poll\") {\n const poll = pollFromRow(row);\n if (poll === null) return collab;\n // A ROOM packet cannot know this viewer's vote; keep the one we already hold.\n const known = collab.polls.find((p) => p.id === poll.id);\n let merged = fromRoom && known !== undefined ? { ...poll, myVote: known.myVote } : poll;\n // A room copy carries no counts until results are shared; a HOST already holding\n // counts (from their own read or the hosts' packet) keeps them until newer ones land.\n if (fromRoom && known !== undefined && !poll.options.some((o) => o.votes !== undefined)) {\n const counted = new Map(known.options.map((o) => [o.id, o]));\n merged = {\n ...merged,\n voterCount: known.voterCount,\n options: merged.options.map((o) => {\n const had = counted.get(o.id);\n return had === undefined ? o : { ...o, votes: had.votes, voters: had.voters };\n }),\n };\n }\n return { ...collab, polls: upsert(collab.polls, merged) };\n }\n if (kind === \"question\") {\n const question = questionFromRow(row);\n if (question === null) return collab;\n const known = collab.questions.find((q) => q.id === question.id);\n // `mine` / `upvoted_by_me` are per viewer; a room packet says false for everyone.\n const merged =\n fromRoom && known !== undefined\n ? { ...question, mine: known.mine, upvotedByMe: known.upvotedByMe }\n : question;\n return { ...collab, questions: upsert(collab.questions, merged) };\n }\n if (kind === \"stroke\") {\n const stroke = strokeFromRow(row);\n if (stroke === null) return collab;\n return { ...collab, strokes: upsert(collab.strokes, stroke) };\n }\n if (kind === \"breakout\") {\n const plan = planFromRow(row);\n if (plan === null) return collab;\n return { ...collab, breakout: plan };\n }\n return collab;\n}\n\nfunction without(collab: CollabSnapshot, ids: readonly string[]): CollabSnapshot {\n const gone = new Set(ids);\n return {\n ...collab,\n polls: collab.polls.filter((p) => !gone.has(p.id)),\n questions: collab.questions.filter((q) => !gone.has(q.id)),\n strokes: collab.strokes.filter((s) => !gone.has(s.id)),\n };\n}\n\nexport interface CollabPacketContext {\n readonly localIdentity: ParticipantIdentity | null;\n /** Hosts and co-hosts receive a second, fuller packet; they ignore the room copy. */\n readonly isHost: boolean;\n readonly now: number;\n}\n\n/**\n * One `matrx.meet.collab` packet → the next snapshot, or `null` when it must be dropped\n * (with the reason in `dropped`). Pure; the room-event dispatcher patches the store.\n */\nexport function applyCollabPacket(\n collab: CollabSnapshot,\n payload: Row,\n context: CollabPacketContext,\n): { next: CollabSnapshot | null; dropped?: string } {\n const item = obj(payload[\"item\"]);\n if (item !== null) {\n const kind = str(item[\"kind\"]);\n // Every copy is folded — the room's AND the hosts' fuller one. `withItem` keeps a\n // host's counts when a count-less room copy arrives, so neither copy can blank the\n // other, and a host still sees an item the server sent the room only (a new question).\n let next = withItem(collab, item, true);\n if (kind === \"poll\" && str(item[\"state\"]) === \"live\") {\n const before = collab.polls.find((p) => p.id === str(item[\"id\"]));\n if (before === undefined || before.state !== \"live\") {\n next = { ...next, panelRequest: { panel: \"polls\", at: context.now } };\n }\n }\n if (kind === \"breakout\") {\n const plan = planFromRow(item);\n if (plan !== null && plan.state === \"closed\") {\n next = { ...next, moveRequest: { roomId: null, at: context.now } };\n }\n }\n const move = obj(payload[\"move\"]);\n if (move !== null && str(move[\"identity\"]) === context.localIdentity) {\n next = { ...next, moveRequest: { roomId: str(move[\"room_id\"]), at: context.now } };\n }\n return { next };\n }\n if (payload[\"focus\"] !== undefined) {\n const focus = focusFromRow(payload[\"focus\"]);\n return focus === null ? { next: null, dropped: \"a focus packet without a panel\" } : { next: { ...collab, focus } };\n }\n if (Array.isArray(payload[\"removed\"])) {\n return { next: without(collab, strings(payload[\"removed\"])) };\n }\n if (Array.isArray(payload[\"hidden\"])) {\n // A dismissed question leaves everyone's list — but a host keeps it (their packet\n // carries the dismissed state).\n return context.isHost ? { next: null, dropped: \"hosts keep dismissed questions\" } : { next: without(collab, strings(payload[\"hidden\"])) };\n }\n if (payload[\"cleared\"] === \"whiteboard\") {\n return { next: { ...collab, strokes: [] } };\n }\n const broadcast = broadcastFrom(payload[\"broadcast\"]);\n if (broadcast !== null) {\n return { next: { ...collab, broadcast } };\n }\n return { next: null, dropped: \"a collab packet with nothing this version understands\" };\n}\n\n/** The breakout room a person is assigned to, or null. */\nexport function assignedRoom(plan: MeetBreakoutPlan | null, identity: string | null): MeetBreakoutRoom | null {\n if (plan === null || plan.state !== \"open\" || identity === null) return null;\n return plan.rooms.find((room) => room.identities.includes(identity)) ?? null;\n}\n\n/**\n * Split people into `rooms` rooms, round-robin in the order given — Zoom's \"assign\n * automatically\". Hosts are left out (they visit); pass them in `exclude`.\n */\nexport function autoAssign(\n identities: readonly string[],\n rooms: number,\n exclude: readonly string[] = [],\n): string[][] {\n const count = Math.max(1, Math.floor(rooms));\n const out: string[][] = Array.from({ length: count }, () => []);\n const skip = new Set(exclude);\n let index = 0;\n for (const identity of identities) {\n if (skip.has(identity)) continue;\n out[index % count]!.push(identity);\n index += 1;\n }\n return out;\n}\n\n/** Milliseconds until the plan's timer ends, or null when it has none. */\nexport function breakoutRemainingMs(plan: MeetBreakoutPlan | null, now: number): number | null {\n if (plan === null || plan.state !== \"open\") return null;\n const ends = [plan.endsAt, plan.closingAt]\n .map((stamp) => (stamp === null ? Number.NaN : Date.parse(stamp)))\n .filter((time) => !Number.isNaN(time));\n if (ends.length === 0) return null;\n return Math.max(0, Math.min(...ends) - now);\n}\n\n/** Meet shows a 30 second countdown before rooms end; the host's Close starts one at once. */\nexport const BREAKOUT_COUNTDOWN_MS = 30_000;\n\n/**\n * The closing countdown to show, in ms — or null when the rooms are not closing. Closing\n * means the host asked (`closingAt`) or the timer is within its last 30 seconds.\n */\nexport function breakoutClosingMs(plan: MeetBreakoutPlan | null, now: number): number | null {\n if (plan === null || plan.state !== \"open\") return null;\n const remaining = breakoutRemainingMs(plan, now);\n if (remaining === null) return null;\n return plan.closingAt !== null || remaining <= BREAKOUT_COUNTDOWN_MS ? remaining : null;\n}\n\n/** The language translated captions start in: the knob, else the viewer's own. */\nexport function defaultCaptionLanguage(\n settings: MeetCollabSettings | null,\n viewerLanguage: string | null | undefined,\n): string {\n const configured = settings?.captionTranslationLanguage.trim() ?? \"\";\n if (configured.length > 0) return configured;\n const own = (viewerLanguage ?? \"\").trim();\n return own.length > 0 ? own : \"en\";\n}\n\n/** Languages offered in the captions menu (tags the translator understands). */\nexport const CAPTION_LANGUAGES: readonly { readonly tag: string; readonly name: string }[] = [\n { tag: \"en\", name: \"English\" },\n { tag: \"es\", name: \"Spanish\" },\n { tag: \"fr\", name: \"French\" },\n { tag: \"de\", name: \"German\" },\n { tag: \"it\", name: \"Italian\" },\n { tag: \"pt\", name: \"Portuguese\" },\n { tag: \"nl\", name: \"Dutch\" },\n { tag: \"pl\", name: \"Polish\" },\n { tag: \"ru\", name: \"Russian\" },\n { tag: \"uk\", name: \"Ukrainian\" },\n { tag: \"tr\", name: \"Turkish\" },\n { tag: \"ar\", name: \"Arabic\" },\n { tag: \"he\", name: \"Hebrew\" },\n { tag: \"fa\", name: \"Persian\" },\n { tag: \"hi\", name: \"Hindi\" },\n { tag: \"bn\", name: \"Bengali\" },\n { tag: \"zh\", name: \"Chinese\" },\n { tag: \"ja\", name: \"Japanese\" },\n { tag: \"ko\", name: \"Korean\" },\n { tag: \"vi\", name: \"Vietnamese\" },\n { tag: \"th\", name: \"Thai\" },\n { tag: \"id\", name: \"Indonesian\" },\n { tag: \"sv\", name: \"Swedish\" },\n];\n\nexport interface MeetingActivities {\n /** Polls (with results), questions, whiteboard strokes — or null when refused. */\n readonly collab: CollabSnapshot | null;\n readonly refusal: { readonly message: string; readonly remedy: string } | null;\n}\n\n/**\n * THE MEETING'S POLLS, Q&A AND WHITEBOARD, AFTER IT (Meet wave 5) — for the record page.\n * The same `GET /v1/meet/collab` the room reads on join; a refusal is returned as data,\n * so the record still renders everything else.\n */\nexport async function loadMeetingActivities(\n api: MeetApi,\n meetingId: string,\n meetingPass: string | null = null,\n): Promise<MeetingActivities> {\n let response: Response;\n try {\n response = await api.request({\n path: `${MEET_ROUTES.collab}?meeting_id=${encodeURIComponent(meetingId)}`,\n method: \"GET\",\n operation: \"meet.loadMeetingActivities\",\n requireAuth: meetingPass === null,\n ...(meetingPass !== null ? { headers: { [MEETING_PASS_HEADER]: meetingPass } } : {}),\n });\n } catch (error) {\n return {\n collab: null,\n refusal: {\n message: `Polls, questions and the whiteboard could not be read (${(error as Error).message}).`,\n remedy: (error as { remedy?: string }).remedy ?? \"Reload the page.\",\n },\n };\n }\n if (!response.ok) {\n let body: unknown = null;\n try {\n body = await response.json();\n } catch {\n body = null;\n }\n const said = readServerRefusal(body);\n return {\n collab: null,\n refusal: {\n message: said?.reason ?? `Polls and questions could not be read (the server answered ${response.status}).`,\n remedy: said?.remedy || \"Reload the page.\",\n },\n };\n }\n return { collab: collabFromState((await response.json()) as Row, EMPTY_COLLAB), refusal: null };\n}\n","/**\n * WHO SAID THAT — one answer, every surface, both lanes (MRI-A16).\n *\n * 🚨 **A RAW LIVEKIT IDENTITY IS NEVER A PERSON'S NAME.** On production\n * 2026-09-09 the host's caption rail read *\"Ada Lovelace: I planted a marigold\n * in the courtyard\"* and the guest's — the same meeting, the same second —\n * read *\"guest:c8-spk-1\"*, because the host resolved the name from the durable\n * rows it can read and the guest had no fallback at all and printed the token\n * (MRI-C8). Two surfaces disagreeing about who is speaking is worse than either\n * one being ugly: a person reading the transcript cannot tell whether those are\n * one speaker or two.\n *\n * The cure is that nothing resolves a name for itself. Every source that\n * happens to KNOW a name teaches this book — the LiveKit roster, a durable\n * `meet_transcript_segments` row, a durable `meet_participants` row, a chat\n * message, our own identity — and every surface asks it. Three rules:\n *\n * 1. **A name is learned, never unlearned.** Someone who leaves the room keeps\n * the name they had; a caption that lands after them still says who spoke.\n * This is the \"last known name\" rule, and it is why the book is a store\n * field rather than a derivation over the current roster.\n * 2. **An identity is not a name.** `learn` refuses a \"name\" that is the\n * identity itself — which is exactly what the LiveKit roster hands back for\n * a participant whose token carried no name, and exactly how the token got\n * onto the screen in the first place.\n * 3. **When nobody knows, SAY nobody knows.** `displayNameFor` answers with an\n * honest placeholder — \"Unnamed guest\" / \"Unnamed participant\" — never a\n * token, and never an invented name. The placeholder is deliberately not\n * unique, so surfaces that must keep two unnamed people apart group by\n * IDENTITY rather than by the rendered name.\n */\n\nimport type { ParticipantIdentity } from \"./types\";\n\n/** Identity → the best display name anything has ever known for it. */\nexport type NameBook = Readonly<Record<string, string>>;\n\n/**\n * S7 (CORE-DESIGN §2.1): a LiveKit identity is `<person key>~<tab id>` — one CONNECTION per\n * browser tab. The person key (`user:<id>` · `guest:<device>` · `agent:<capability>:<meeting>`)\n * is what every tile, row and roster entry uses. THIS is the one helper that reads the shape;\n * `names.test.ts` fails on any identity prefix test elsewhere in the package.\n */\nexport const CONNECTION_SEPARATOR = \"~\";\n\n/** The person key of any identity (a connection identity or a person key). */\nexport function personOf(identity: ParticipantIdentity | string): ParticipantIdentity {\n const value = String(identity ?? \"\").trim();\n const cut = value.indexOf(CONNECTION_SEPARATOR);\n return (cut === -1 ? value : value.slice(0, cut)) as ParticipantIdentity;\n}\n\n/** Do two identities (connections or person keys) name the same person? */\nexport function samePerson(a: ParticipantIdentity | string | null | undefined, b: ParticipantIdentity | string | null | undefined): boolean {\n if (a == null || b == null) return false;\n return personOf(a) === personOf(b);\n}\n\n/** The account id behind an identity, or null for a guest or an agent. */\nexport function userIdOfIdentity(identity: ParticipantIdentity | string): string | null {\n const person = personOf(identity);\n return person.startsWith(\"user:\") ? person.slice(\"user:\".length) : null;\n}\n\n/** A guest's identity is minted `guest:<something>` by the token route. */\nexport function isGuestIdentity(identity: ParticipantIdentity | string): boolean {\n return personOf(identity).startsWith(\"guest:\");\n}\n\n/**\n * An AGENT's identity is minted `agent:<capability>:<meeting>` by the server\n * (`services/meet/worker.py`). An agent is never an anonymous \"participant\":\n * it is a product with a name, and a room that cannot say which product is\n * listening has failed the consent notice it is required to carry (D10).\n */\nexport function isAgentIdentity(identity: ParticipantIdentity | string): boolean {\n return personOf(identity).startsWith(\"agent:\");\n}\n\n/** The note-taker's product name — `services/meet/worker.py::AGENT_DISPLAY_NAME`. */\nexport const NOTE_TAKER_PRODUCT_NAME = \"Matrx note-taker\";\n/** Any other Matrx agent whose dispatch carried no name. */\nexport const AGENT_PRODUCT_NAME = \"Matrx agent\";\n/** Somebody in the room whose name nothing has ever known. */\nexport const UNNAMED_GUEST = \"Unnamed guest\";\n/** The same, for a participant whose identity is not a guest token. */\nexport const UNNAMED_PARTICIPANT = \"Unnamed participant\";\n\n/**\n * The strings THIS MODULE invents when nobody knows a name. They are answers,\n * never facts: `isRealName` refuses them, so a placeholder can never be learned\n * as somebody's name and can never outrank a real name that arrives later.\n *\n * 🚨 This is what makes the roster safe to route through the book. A roster\n * event projects \"Unnamed guest\" for a nameless participant; a durable\n * transcript row then teaches \"Ada Lovelace\"; without this rule the placeholder\n * — a perfectly ordinary-looking string — would win, and the screen would keep\n * saying \"Unnamed guest\" about somebody the meeting plainly knows.\n */\nconst PLACEHOLDER_NAMES: readonly string[] = [\n NOTE_TAKER_PRODUCT_NAME,\n AGENT_PRODUCT_NAME,\n UNNAMED_GUEST,\n UNNAMED_PARTICIPANT,\n];\n\n/** Is this string one of the placeholders this module produces? */\nexport function isPlaceholderName(name: string | null | undefined): boolean {\n return typeof name === \"string\" && PLACEHOLDER_NAMES.includes(name.trim());\n}\n\n/** What an identity is called when NOTHING knows a name for it. Never a token. */\nexport function placeholderNameFor(identity: ParticipantIdentity | string): string {\n const key = String(identity);\n if (isAgentIdentity(key)) {\n return key.startsWith(\"agent:note_taker:\") || key.startsWith(\"agent:note-taker\")\n ? NOTE_TAKER_PRODUCT_NAME\n : AGENT_PRODUCT_NAME;\n }\n return isGuestIdentity(key) ? UNNAMED_GUEST : UNNAMED_PARTICIPANT;\n}\n\n/**\n * Would this string be a real name for this identity? A name equal to the\n * identity is the roster's way of saying it has none.\n */\nexport function isRealName(\n identity: ParticipantIdentity | string,\n name: string | null | undefined,\n): boolean {\n if (typeof name !== \"string\") return false;\n const trimmed = name.trim();\n if (trimmed.length === 0) return false;\n if (trimmed === String(identity)) return false;\n // Our own answer is not new information. See PLACEHOLDER_NAMES.\n if (isPlaceholderName(trimmed)) return false;\n return true;\n}\n\n/**\n * Teach the book one identity's name. Returns the SAME book when nothing was\n * learned, so a store commit can be skipped rather than waking every subscriber\n * on every caption.\n */\nexport function learnName(\n book: NameBook,\n identity: ParticipantIdentity | string,\n name: string | null | undefined,\n): NameBook {\n const key = String(identity);\n if (key.length === 0) return book;\n if (!isRealName(key, name)) return book;\n const learned = (name as string).trim();\n if (book[key] === learned) return book;\n return { ...book, [key]: learned };\n}\n\n/** Teach the book several at once — a roster replace, or a durable read. */\nexport function learnNames(\n book: NameBook,\n entries: readonly {\n identity: ParticipantIdentity | string;\n displayName?: string | null | undefined;\n }[],\n): NameBook {\n let next = book;\n for (const entry of entries) next = learnName(next, entry.identity, entry.displayName);\n return next;\n}\n\n/**\n * The name a surface renders for `identity`. Never a raw token, never invented.\n *\n * `preferred` is what the arriving thing itself claimed (a caption's roster\n * lookup, a durable row's `speaker` column); it wins when it is a real name,\n * because it is the freshest, and teaches the book on its way past.\n */\nexport function displayNameFor(\n book: NameBook,\n identity: ParticipantIdentity | string,\n preferred?: string | null | undefined,\n): string {\n const key = String(identity);\n if (isRealName(key, preferred)) return (preferred as string).trim();\n const known = book[key];\n if (known !== undefined) return known;\n // NOBODY KNOWS, AND THAT IS WHAT IT SAYS. A person reading this can tell the\n // difference between \"someone whose name we never learned\" and a name.\n return placeholderNameFor(key);\n}\n","/**\n * RECORDING — LiveKit Egress, host-controlled, consent-enforced (R3/R5).\n *\n * 🚨 **CONSENT IS NOT OPTIONAL AND NOT A PROP.** There is no\n * `showRecordingIndicator={false}`. Recording a person without telling them is\n * illegal in a two-party-consent jurisdiction and is indefensible everywhere\n * else, so the indicator and the join-time notice are properties of the\n * package, not decisions a consumer gets to make. The only knob is the org's\n * `recordingPolicy` — disabled / host-controlled / always-on — which decides\n * whether recording may happen at all, never whether people are told.\n *\n * 🚨 **THE RECORDING BELONGS TO THE PLATFORM, NOT TO LIVEKIT.** The server\n * lands the artifact in the platform files system and returns a FILE ID. A\n * provider URL would be an orphan the day the room is garbage collected — the\n * reason `RecordingRecord.fileId` is not `url`.\n *\n * The client half is small on purpose: start/stop are aidream calls (the API\n * secret never comes near a browser), and the truth about a recording arrives\n * from the server's webhook through the room channel. This module never\n * *guesses* that a recording started.\n */\n\nimport type { MeetApi } from \"./api\";\nimport { MeetError } from \"./errors\";\nimport type { MeetStore } from \"./store\";\nimport { MEET_ROUTES } from \"./tokens\";\nimport type {\n MeetingId,\n MeetingRecord,\n RecordingState,\n RoomName,\n UserId,\n} from \"./types\";\n\nexport interface RecordingControllerOptions {\n /** The ONE org-aware aidream client (MRI-A16). Owns base url and headers. */\n api: MeetApi;\n store: MeetStore;\n /** Announce the new state to everyone in the room (the indicator is for all). */\n announce?: ((state: RecordingState, by: UserId | null) => void) | undefined;\n /** Persist that this participant saw the notice. Consent is RECORDED (R5). */\n acknowledgeConsent?: ((meetingId: MeetingId) => Promise<void>) | undefined;\n}\n\nexport interface RecordingController {\n /** True when the org policy and the actor's role allow starting a recording. */\n canStart(meeting: MeetingRecord | null, isHost: boolean): boolean;\n start(args: { meetingId: MeetingId; roomName: RoomName; by: UserId }): Promise<void>;\n stop(args: { meetingId: MeetingId; by: UserId }): Promise<void>;\n /** The join-time notice text. One sentence, one place, every surface. */\n consentNotice(meeting: MeetingRecord | null): string | null;\n acknowledge(meetingId: MeetingId): Promise<void>;\n}\n\nexport function createRecordingController(\n options: RecordingControllerOptions,\n): RecordingController {\n async function post(route: string, body: Record<string, unknown>): Promise<void> {\n let response: Response;\n try {\n response = await options.api.request({\n path: route,\n method: \"POST\",\n // A GUEST CAN NEVER START OR STOP A RECORDING (R5) — refused here,\n // before the wire, with the same sentence the hidden control implies.\n requireAuth: true,\n operation: \"meet.recording\",\n body,\n });\n } catch (error) {\n if ((error as { code?: string }).code === \"session-unavailable\") {\n throw new MeetError(\n \"forbidden\",\n \"meet.recording: recording requires an authenticated host\",\n \"A guest can never start or stop a recording (R5). The control is hidden for guests.\",\n );\n }\n throw error;\n }\n if (response.status === 403) {\n throw new MeetError(\n \"forbidden\",\n \"meet.recording: the server refused this recording action\",\n \"The actor is not a host of this meeting, or the org policy is `disabled`. The server \" +\n \"is the authority; do not re-decide it on the client.\",\n );\n }\n if (!response.ok) {\n throw new MeetError(\n \"transport\",\n `meet.recording: the server returned ${response.status}`,\n \"Egress could not be controlled. The recording state stays where it was rather than \" +\n \"showing a recording indicator for a recording that is not running.\",\n );\n }\n }\n\n return {\n canStart(meeting, isHost) {\n if (meeting === null) return false;\n if (meeting.recordingPolicy === \"disabled\") return false;\n // `always-on` is started by the SERVER when the room opens; a host cannot\n // start (or stop) it, which is the point of the policy.\n if (meeting.recordingPolicy === \"always-on\") return false;\n return isHost;\n },\n\n async start({ meetingId, roomName, by }) {\n // OPTIMISTIC ONLY AS FAR AS \"starting\", and marked `\"local\"` so the\n // durable row overrules it the moment it lands (D16). The indicator turns\n // solid when the SERVER says the egress is running — never on a client's\n // hope, and never STUCK on that hope either: `meet_recordings` is\n // subscribed and the room state carries it to the guests (S8).\n options.store.setRecording(\"starting\", by, \"local\");\n try {\n await post(MEET_ROUTES.recordingStart, {\n meeting_id: meetingId,\n room_name: roomName,\n });\n } catch (error) {\n options.store.setRecording(\"idle\", null, \"local\");\n throw error;\n }\n options.announce?.(\"starting\", by);\n },\n\n async stop({ meetingId, by }) {\n options.store.setRecording(\"stopping\", options.store.snapshot().recordingStartedBy, \"local\");\n try {\n await post(MEET_ROUTES.recordingStop, { meeting_id: meetingId });\n } catch (error) {\n options.store.setRecording(\n \"recording\",\n options.store.snapshot().recordingStartedBy,\n \"local\",\n );\n throw error;\n }\n options.announce?.(\"stopping\", by);\n },\n\n consentNotice(meeting) {\n if (meeting === null || meeting.recordingPolicy === \"disabled\") return null;\n if (meeting.recordingPolicy === \"always-on\") {\n return \"This meeting is recorded and transcribed automatically. By joining, you consent to being recorded.\";\n }\n return \"The host can record and transcribe this meeting. You will see a recording indicator whenever it is running.\";\n },\n\n async acknowledge(meetingId) {\n await options.acknowledgeConsent?.(meetingId);\n },\n };\n}\n\n/**\n * 🚨 IS A RECORDING IN FLIGHT RIGHT NOW? One definition (MRI-A16).\n *\n * The control used to ask `state === \"idle\"`, which was survivable only while\n * the store never learned any state but its own: the moment the DURABLE states\n * started arriving, a meeting whose first recording had finished sat on\n * `available` forever and the host could never start a second one — the Start\n * button simply stopped existing. `processing`, `available` and `failed` all\n * mean \"nothing is recording\"; only these three mean it is.\n */\nexport const RECORDING_IN_FLIGHT: readonly RecordingState[] = [\n \"starting\",\n \"recording\",\n \"stopping\",\n];\n\nexport function isRecordingInFlight(state: RecordingState): boolean {\n return RECORDING_IN_FLIGHT.includes(state);\n}\n\n/** The indicator's words. One definition, so no surface invents a softer one. */\nexport function recordingLabel(state: RecordingState): string | null {\n switch (state) {\n case \"starting\":\n return \"Starting recording…\";\n case \"recording\":\n return \"Recording\";\n case \"stopping\":\n return \"Stopping recording…\";\n case \"processing\":\n return \"Processing recording…\";\n case \"available\":\n return \"Recording ready\";\n case \"expired\":\n return \"Recording expired\";\n case \"failed\":\n return \"Recording failed\";\n default:\n return null;\n }\n}\n","/**\n * THE RULES A SCREEN REDRAWS FROM WHILE THE CALL IS RUNNING (P1: chat-restrictions,\n * mod-attendee-av-disabled, time-limit-warning, rec-consent-gate).\n *\n * The host can change a meeting's rules mid-call through the meeting's own rules door, which\n * never talks to the room. The server's state read answers the live values (`live_policy`), and\n * the room reads it on a short timer while the person is in the call — so a rule changed at\n * second 40 is on every screen within one poll. Pure functions here; the timer lives in\n * `meetings.ts`.\n */\n\nimport type { ParticipantRole } from \"./types\";\n\nexport type ChatMode = \"everyone\" | \"hosts_only\" | \"off\";\nexport type AvLock = \"off\" | \"mic\" | \"camera\" | \"both\";\nexport type RecordingConsent = \"notice\" | \"ok_or_leave\" | \"view_only_until_agree\";\n/** Who may share a screen (`share_who_can`) and how many may at once (`share_concurrency`). */\nexport type ShareWhoCan = \"everyone\" | \"host_only\" | \"org\" | \"presenters\";\nexport type ShareConcurrency = \"one_takeover\" | \"one_replace\" | \"multiple\";\n\n/** What remains when no host or co-host is in the room (`headless_powers`, CORE-DESIGN §4.2). */\nexport type HeadlessPowers = \"members_admit\" | \"none\" | \"cohosts_only\";\n\nexport interface LivePolicy {\n readonly headlessPowers: HeadlessPowers;\n readonly chatMode: ChatMode;\n readonly avLock: AvLock;\n /** 0 = no limit. */\n readonly timeLimitMinutes: number;\n readonly timeLimitWarningMinutes: number;\n readonly recordingConsent: RecordingConsent;\n readonly shareWhoCan: ShareWhoCan;\n readonly shareConcurrency: ShareConcurrency;\n}\n\nexport const DEFAULT_LIVE_POLICY: LivePolicy = {\n headlessPowers: \"members_admit\",\n chatMode: \"everyone\",\n avLock: \"off\",\n timeLimitMinutes: 0,\n timeLimitWarningMinutes: 5,\n recordingConsent: \"notice\",\n shareWhoCan: \"everyone\",\n shareConcurrency: \"one_takeover\",\n};\n\nfunction oneOf<T extends string>(value: unknown, allowed: readonly T[], fallback: T): T {\n return typeof value === \"string\" && (allowed as readonly string[]).includes(value) ? (value as T) : fallback;\n}\n\nfunction whole(value: unknown, fallback: number): number {\n const n = typeof value === \"number\" ? value : typeof value === \"string\" ? Number(value) : NaN;\n return Number.isFinite(n) && n >= 0 ? Math.floor(n) : fallback;\n}\n\n/** Reads the server's `live_policy` object; a key it did not send keeps its default. */\nexport function livePolicyFrom(raw: unknown): LivePolicy {\n const o = typeof raw === \"object\" && raw !== null ? (raw as Record<string, unknown>) : {};\n return {\n headlessPowers: oneOf(o[\"headless_powers\"], [\"members_admit\", \"none\", \"cohosts_only\"], DEFAULT_LIVE_POLICY.headlessPowers),\n chatMode: oneOf(o[\"chat_mode\"], [\"everyone\", \"hosts_only\", \"off\"], DEFAULT_LIVE_POLICY.chatMode),\n avLock: oneOf(o[\"attendee_av_lock\"], [\"off\", \"mic\", \"camera\", \"both\"], DEFAULT_LIVE_POLICY.avLock),\n timeLimitMinutes: whole(o[\"time_limit_minutes\"], 0),\n timeLimitWarningMinutes: whole(o[\"time_limit_warning_minutes\"], DEFAULT_LIVE_POLICY.timeLimitWarningMinutes),\n recordingConsent: oneOf(\n o[\"recording_consent\"],\n [\"notice\", \"ok_or_leave\", \"view_only_until_agree\"],\n DEFAULT_LIVE_POLICY.recordingConsent,\n ),\n shareWhoCan: oneOf(o[\"share_who_can\"], [\"everyone\", \"host_only\", \"org\", \"presenters\"], DEFAULT_LIVE_POLICY.shareWhoCan),\n shareConcurrency: oneOf(o[\"share_concurrency\"], [\"one_takeover\", \"one_replace\", \"multiple\"], DEFAULT_LIVE_POLICY.shareConcurrency),\n };\n}\n\nexport function samePolicy(a: LivePolicy, b: LivePolicy): boolean {\n return (\n a.headlessPowers === b.headlessPowers &&\n a.chatMode === b.chatMode &&\n a.avLock === b.avLock &&\n a.timeLimitMinutes === b.timeLimitMinutes &&\n a.timeLimitWarningMinutes === b.timeLimitWarningMinutes &&\n a.recordingConsent === b.recordingConsent &&\n a.shareWhoCan === b.shareWhoCan &&\n a.shareConcurrency === b.shareConcurrency\n );\n}\n\nconst MODERATORS: ReadonlySet<ParticipantRole> = new Set([\"host\", \"cohost\"]);\n\n/** Hosts and co-hosts are never locked. */\nexport function isLockedFor(policy: LivePolicy, role: ParticipantRole, kind: \"microphone\" | \"camera\"): boolean {\n if (MODERATORS.has(role)) return false;\n return policy.avLock === \"both\" || (kind === \"microphone\" ? policy.avLock === \"mic\" : policy.avLock === \"camera\");\n}\n\n/**\n * share-who-can: may this person share a screen under the meeting's current rule? Mirrors the\n * server's `sharing.may_share` (which is what actually decides); this only decides whether the\n * control is drawn. A word the package does not know leaves it open.\n */\nexport function canShareUnder(\n policy: LivePolicy,\n role: ParticipantRole,\n standing: \"member\" | \"invited\" | \"outsider\" | \"guest\" | null,\n): boolean {\n if (role === \"viewer\") return false;\n if (MODERATORS.has(role)) return true;\n switch (policy.shareWhoCan) {\n case \"host_only\":\n return false;\n case \"presenters\":\n return role === \"presenter\";\n case \"org\":\n return role === \"presenter\" || standing === \"member\";\n default:\n return true;\n }\n}\n\n/**\n * rec-consent-gate: the person has not yet agreed to being recorded, under a rule that makes them\n * wait (`ok_or_leave` shows a dialog, `view_only_until_agree` also takes the microphone and camera).\n */\nexport function consentPending(policy: LivePolicy, role: ParticipantRole, recordingOn: boolean, given: boolean): boolean {\n return recordingOn && !given && policy.recordingConsent !== \"notice\" && !MODERATORS.has(role);\n}\n\n/** May this role write in the chat under the meeting's current mode? */\nexport function canChat(policy: LivePolicy, role: ParticipantRole): boolean {\n if (policy.chatMode === \"everyone\") return true;\n return policy.chatMode === \"hosts_only\" && MODERATORS.has(role);\n}\n\n/** Whole minutes left before the time limit, or null with no limit / unknown start. */\nexport function minutesLeft(policy: LivePolicy, startedAtMs: number | null, nowMs: number): number | null {\n if (policy.timeLimitMinutes <= 0 || startedAtMs === null) return null;\n return Math.ceil((startedAtMs + policy.timeLimitMinutes * 60_000 - nowMs) / 60_000);\n}\n\n/**\n * May this person let waiting people in right now? A host or co-host always may. When NO host or\n * co-host is in the room, the meeting's `headless_powers` decides: `members_admit` lets any admitted\n * member (the server's door enforces the same rule); `none` and `cohosts_only` add nobody.\n * (meeting-without-host)\n */\nexport function mayAdmitNow(\n role: ParticipantRole | \"none\",\n participants: readonly { readonly role: ParticipantRole }[],\n policy: LivePolicy,\n): boolean {\n if (role === \"none\") return false;\n if (role === \"host\" || role === \"cohost\") return true;\n const hostIn = participants.some((p) => p.role === \"host\" || p.role === \"cohost\");\n return !hostIn && policy.headlessPowers === \"members_admit\";\n}\n","/**\n * LISTEN-ONLY ATTENDANCE — being IN the room is never contingent on a device.\n *\n * 0.3.1 and every version before it enabled the microphone as the last step of\n * `connect()`, inside the same `try` as the join. So a denied, absent, or busy\n * microphone did not produce a muted participant — it threw, the join was\n * failed with a device remedy, and the user was dropped back to `<PreJoin>`\n * having never entered the meeting. The whole in-room surface (tiles, captions,\n * live notes, Q&A, the note-taker consent notice) was unreachable to anyone\n * without a working mic, which is ordinary: a locked-down laptop, a conference\n * room dial-in, a phone on a call, a browser profile that denies capture by\n * policy, or a person who simply wants to listen. Every competitor allows it.\n *\n * THE CLASS, and how it is closed here:\n *\n * 1. **Connecting and publishing are two different operations.** The engine\n * reaches `connected` the moment the transport is up. Media is attempted\n * AFTER that, per track, and a rejection never unwinds the join.\n * 2. **A track that could not be acquired is STATE, not an exception.** The\n * reason and its remedy — the browser's own, translated by\n * `normalizeDeviceError` — ride in the snapshot so the UI says WHY the mic\n * is off instead of showing a mute button that silently does nothing.\n * 3. **Enabling later runs the SAME code path.** `setMicrophoneEnabled(true)`\n * from the control bar records exactly what the join attempt records, so a\n * person who plugs in a headset mid-meeting is one click from speaking and\n * a person whose second attempt also fails gets the same honest sentence.\n * A separate \"retry\" path is how the two drift.\n * 4. **The notice is one sentence, defined once.** Both the package's own room\n * chrome and any host that renders its own read `listenOnlyNotice()`, so\n * there is no second wording to keep in sync.\n */\n\nimport type { MeetError } from \"./errors\";\n\n/**\n * WHY a device is unusable, in the contract's words (CORE-DESIGN §3.2, the `mic` / `camera`\n * region's `unavailable:<reason>`). The media supervisor (`media-supervisor.ts`) decides it from\n * the browser's error AND the Permissions API, because the error alone cannot tell a person's\n * Block from an operating-system refusal or a stale device id.\n */\nexport type DeviceUnavailableReason =\n | \"denied\"\n | \"blocked_site\"\n | \"blocked_os\"\n | \"none\"\n | \"busy\"\n | \"failed\"\n | \"revoked\";\n\n/** Why a local track is not publishing, in the browser's terms plus a remedy. */\nexport interface MediaUnavailable {\n readonly code: string;\n readonly message: string;\n readonly remedy: string;\n /** The region reason (S6c). Absent on states recorded before the supervisor classified them. */\n readonly reason?: DeviceUnavailableReason | undefined;\n}\n\n/** What the browser's permission store says for one device kind. `unknown` = no Permissions API. */\nexport type PermissionReading = \"granted\" | \"denied\" | \"prompt\" | \"unknown\";\n\n/** A device change the person is told about (S6c): unplugged → switched, or nothing left. */\nexport interface DeviceNotice {\n readonly id: number;\n readonly kind: \"microphone\" | \"camera\";\n /** `switched` = the meeting moved to another device; `lost` = none of this kind is left. */\n readonly outcome: \"switched\" | \"lost\";\n /** The device that went away, by label (empty when the browser hid labels). */\n readonly from: string;\n /** The device that went away, by id, so Undo can go back to it if it is plugged in again. */\n readonly fromId?: string | undefined;\n /** The device now in use (`switched` only). */\n readonly to: string | null;\n}\n\n/** A device that just appeared — offered, never switched to silently (catalog `device-default-changed`). */\nexport interface DeviceOffer {\n readonly kind: \"microphone\" | \"camera\";\n readonly deviceId: string;\n readonly label: string;\n}\n\n/**\n * THE MEDIA REGIONS THE SUPERVISOR OWNS (CORE-DESIGN §3.2 `audio_output`, and the inputs the\n * `mic` / `camera` regions are derived from). None of it can change the call phase.\n */\nexport interface MediaRegionState {\n readonly permission: { readonly microphone: PermissionReading; readonly camera: PermissionReading };\n /** `blocked` = the browser's autoplay policy is holding remote audio until a gesture. */\n readonly audioOutput: \"ok\" | \"blocked\";\n readonly notice: DeviceNotice | null;\n readonly offer: DeviceOffer | null;\n /**\n * THE PRE-JOIN CHECK per kind (P1 `perm-prompt-pending`, `perm-blocked-site`, `device-none-found`,\n * `device-busy`): what the preview found BEFORE the room exists. `prompting` = the browser was\n * asked and has not answered (Join stays usable); `unavailable` carries the classified refusal.\n */\n readonly preview: { readonly microphone: DevicePreview; readonly camera: DevicePreview };\n}\n\n/** One kind's pre-join check. `idle` = nothing was tried (or the person turned it off before). */\nexport interface DevicePreview {\n readonly state: \"idle\" | \"prompting\" | \"on\" | \"off\" | \"unavailable\";\n readonly unavailable: MediaUnavailable | null;\n}\n\nexport const IDLE_PREVIEW: DevicePreview = { state: \"idle\", unavailable: null };\n\nexport const IDLE_MEDIA_REGION: MediaRegionState = {\n permission: { microphone: \"unknown\", camera: \"unknown\" },\n audioOutput: \"ok\",\n notice: null,\n offer: null,\n preview: { microphone: IDLE_PREVIEW, camera: IDLE_PREVIEW },\n};\n\nexport interface LocalTrackState {\n /** True only when the track is actually published. Never optimistic. */\n readonly enabled: boolean;\n /**\n * Set when acquisition was ATTEMPTED and refused. `null` covers both \"on\"\n * and \"deliberately off\" — a user who unchecked the mic in pre-join is not\n * looking at an error, they are looking at a choice they made.\n */\n readonly unavailable: MediaUnavailable | null;\n}\n\nexport interface LocalMediaState {\n readonly microphone: LocalTrackState;\n readonly camera: LocalTrackState;\n}\n\nexport const IDLE_TRACK: LocalTrackState = { enabled: false, unavailable: null };\n\nexport const IDLE_MEDIA: LocalMediaState = {\n microphone: IDLE_TRACK,\n camera: IDLE_TRACK,\n};\n\nexport function unavailableFrom(error: MeetError, reason?: DeviceUnavailableReason): MediaUnavailable {\n return reason === undefined\n ? { code: error.code, message: error.message, remedy: error.remedy }\n : { code: error.code, message: error.message, remedy: error.remedy, reason };\n}\n\n/**\n * THE SENTENCE. One definition, so the room chrome, a host's own chrome and the\n * tests cannot say three different things about the same fact.\n */\nexport const LISTEN_ONLY_NOTICE =\n \"You're attending without a microphone — others can't hear you\";\n\n/**\n * True when this participant is in the room with NO USABLE microphone — the\n * device was refused or is missing. A person who simply muted themselves has a\n * working microphone and is not \"attending without\" one, so a mute never\n * counts: `unavailable` is the only signal.\n */\nexport function isListenOnly(media: LocalMediaState): boolean {\n return !media.microphone.enabled && media.microphone.unavailable !== null;\n}\n\n/**\n * The notice to render while in the room, or `null` when the mic is live.\n * Returns the plain sentence; the reason and remedy, when there is one, are\n * rendered beside it from `media.microphone.unavailable`.\n */\nexport function listenOnlyNotice(media: LocalMediaState): string | null {\n return isListenOnly(media) ? LISTEN_ONLY_NOTICE : null;\n}\n","/**\n * THE ROOM SNAPSHOT — one immutable object the UI renders and nothing else.\n *\n * Deliberately NOT Redux, NOT a context full of `useState`, and NOT a bag of\n * refs: one store with `getSnapshot`/`subscribe` is exactly what\n * `useSyncExternalStore` wants, which is what makes tearing impossible when a\n * roster event and a speaker event land in the same tick.\n *\n * The rules encoded here:\n *\n * - **The roster is replaced wholesale, never patched.** The driver hands us\n * the full truth on every media event (see `room-driver.ts`); merging deltas\n * from six event sources is how a tile for someone who left stays on screen.\n * - **Ephemera EXPIRE.** Reactions and speaking flags are time-boxed in the\n * snapshot, so a dropped \"stop\" event cannot leave a permanent 🎉 or a\n * permanently-speaking ring. (Nothing here relies on receiving an end event.)\n * - **An interim transcript line is REPLACED in place by its final.** Appending\n * both is how a transcript ends up saying everything twice.\n */\n\nimport { displayNameFor, learnName, learnNames, type NameBook } from \"./names\";\nimport { isRecordingInFlight } from \"./recording\";\nimport { DEFAULT_LEAVE_POLICY, type LeavePolicy } from \"./leave\";\nimport { DEFAULT_LIVE_POLICY, type LivePolicy } from \"./live-policy\";\nimport { IDLE_MEDIA, IDLE_MEDIA_REGION, type LocalMediaState, type LocalTrackState, type MediaRegionState } from \"./attendance\";\nimport { ROLE_RANK } from \"./types\";\nimport { nextPhase, reasonOf, type MeetPhase, type PhaseEvent } from \"./phase\";\nimport type { ObserversVisibleTo } from \"./types\";\nimport type { NoteTakerRefusal } from \"./note-taker\";\nimport { EMPTY_COLLAB, type CollabSnapshot } from \"./collab\";\nimport { mergeCaption, mergeRecord } from \"./transcript\";\nimport type {\n AdmissionState,\n ConnectionQuality,\n LobbyEntry,\n MeetParticipant,\n MeetingNote,\n MeetingRecord,\n ParticipantIdentity,\n ParticipantRole,\n ParticipantStanding,\n RecordingSource,\n RecordingState,\n StageLayout,\n TranscriptSegment,\n UserId,\n WrapUpAnnouncement,\n RoomName,\n CallInvite,\n} from \"./types\";\n\nexport interface RoomChatMessage {\n readonly id: string;\n readonly identity: ParticipantIdentity;\n readonly displayName: string;\n readonly text: string;\n readonly at: string;\n /**\n * Set for a PRIVATE message: the one identity it was addressed to (from the\n * sender's side) or this participant (from the receiver's). Private messages\n * travel the data plane only and are never saved (Meet wave 2).\n */\n readonly to?: ParticipantIdentity | null | undefined;\n /** True once the server has SAVED it — a message everyone sees, late joiners too. */\n readonly saved?: boolean | undefined;\n /** A send the server refused, with its sentence. The message stays visible, marked. */\n readonly failed?: string | undefined;\n}\n\n/**\n * A HOST ACTED ON *ME* (Meet wave 2) — the server's own `matrx.meet.moderation`\n * packet, addressed to this participant. Rendered once, dismissible, in words.\n */\nexport interface ModerationNotice {\n readonly kind: \"muted\" | \"ask-unmute\" | \"removed\" | \"role\" | \"locked\" | \"capability\";\n readonly text: string;\n readonly at: number;\n}\n\n/** What this participant chose for their own media. Remembered per browser. */\nexport interface MediaEffects {\n readonly noiseSuppression: boolean;\n readonly background: \"none\" | \"blur\" | \"image\";\n readonly backgroundImage: string | null;\n}\n\nexport const DEFAULT_EFFECTS: MediaEffects = {\n noiseSuppression: true,\n background: \"none\",\n backgroundImage: null,\n};\n\nexport interface RoomReaction {\n readonly id: string;\n readonly identity: ParticipantIdentity;\n readonly emoji: string;\n readonly at: number;\n}\n\n/**\n * 🚨 WHAT THE SERVER GRANTED THIS JOINER, straight off the token response\n * (MRI-A11). It is placed in the snapshot the instant the token lands — before\n * the LiveKit roster exists at all — so a host's controls are correct on the\n * first frame of the room rather than one roster event later. The roster entry\n * for the local participant carries the SAME decision as a LiveKit attribute;\n * the two agree because one server statement produced both, and the UI reads\n * whichever has arrived.\n */\nexport interface MeetGrants {\n readonly role: ParticipantRole;\n /** Where this joiner stands with the meeting (server-minted, beside the role). */\n readonly standing?: ParticipantStanding | null;\n readonly canRecord: boolean;\n readonly canPublish: boolean;\n readonly canSubscribe: boolean;\n /** HR-360 wave 2 (MD-16): who sees the \"Observing\" line in People (knob). */\n readonly observersVisibleTo?: ObserversVisibleTo | undefined;\n}\n\n/** No token yet grants nothing. Never \"assume host until told otherwise\". */\nexport const NO_GRANTS: MeetGrants = {\n role: \"participant\",\n standing: null,\n canRecord: false,\n canPublish: false,\n canSubscribe: false,\n};\n\n/**\n * S4 — THIS PERSON'S WAIT, as the server's state read (`GET /v1/meet/state`) last said it\n * (CORE-DESIGN §2.6). Set while the phase is a waiting one (`waiting:*`, `denied`,\n * `knock_expired`); the waiting screens read it, never a broadcast. `admission` is the row's own word: `waiting_host` · `knocking` · `denied` ·\n * `expired` · `withdrawn` · `ended` (`admitted` is never held here — it is a join).\n */\nexport interface MeetWaiting {\n readonly admission: AdmissionState;\n /** Is anyone who may admit in the room? null until the first read. */\n readonly admittersPresent: boolean | null;\n /** May a denied person ask again (`reknock_after_deny`)? */\n readonly reknockAllowed: boolean;\n readonly knockExpiresAt: string | null;\n /** While a denial's cooldown runs: the epoch-ms moment the person may ask again; else null. */\n readonly reknockAt?: number | null;\n /** How often the core reads while waiting (`waiting_poll_seconds`). */\n readonly pollSeconds: number;\n /** The run's `live_version` at the last read. */\n readonly liveVersion: number;\n}\n\n/** The core's `connection` region (CORE-DESIGN §3.2) — never a phase. */\nexport interface LocalNetwork {\n readonly path: \"direct\" | \"restricted\" | \"unknown\";\n readonly quality: \"good\" | \"poor\" | \"unknown\";\n readonly videoPaused: boolean;\n}\n\nexport const UNKNOWN_LOCAL_NETWORK: LocalNetwork = { path: \"unknown\", quality: \"unknown\", videoPaused: false };\n\nexport type ConnectionRegion = \"stable\" | \"signal_reconnecting\" | \"reconnecting\" | \"offline\" | \"server_unreachable\";\n\n/**\n * The core's `share` region (CORE-DESIGN §3.2/§3.3 SA). A refused share is a situation the person\n * is in — a notice (`share-unavailable`), never a phase.\n */\nexport interface ShareRegion {\n readonly state: \"none\" | \"picking\" | \"presenting\" | \"refused\" | \"ended\";\n /**\n * Why a share was refused (this browser cannot share, the system blocks it, or it failed) or, for\n * `ended`, why the server took it down: someone else presented instead, or the host's rule no longer lets this person share.\n */\n readonly reason: \"unsupported\" | \"blocked\" | \"failed\" | \"replaced\" | \"not_allowed\" | null;\n /** `ended` / `replaced`: who presents now. */\n readonly by?: string;\n /** The fix, in the core's words (system setting, administrator policy, …), or null. */\n readonly remedy: string | null;\n readonly at: number;\n /** While presenting: whether the share carries sound (`share-audio`). Absent = not known. */\n readonly audio?: boolean;\n}\n\nexport const NO_SHARE: ShareRegion = { state: \"none\", reason: null, remedy: null, at: 0 };\n\nexport interface MeetSnapshot {\n /**\n * WHERE THIS TAB IS — the contract's `data-meet-phase` value, verbatim (CORE-DESIGN §3.1).\n * Written ONLY by `store.dispatch(event)` through the phase machine (`core/phase.ts`);\n * `patch` cannot touch it.\n */\n readonly phase: MeetPhase;\n /** `data-meet-reason` for a refused / terminal phase; null otherwise. */\n readonly reason: string | null;\n /** A notice that came WITH the phase (`sent-to-waiting`), or null. */\n readonly phaseNotice: string | null;\n /** The room this tab last joined (or is joining) — a different meeting mounting resets. */\n readonly roomName: RoomName | null;\n /** The `connection` region: reconnecting is here, never a phase. */\n readonly connection: ConnectionRegion;\n /**\n * Whether the reconnect banner shows: the region is honest at once, the banner only after the\n * drop outlasts `reconnect_banner_delay_ms` (the connection supervisor sets it, §3.3 CS).\n */\n readonly connectionBanner: boolean;\n /**\n * P1 connection-network — THIS tab's own path, as the core judged it from its WebRTC stats\n * (`network-health.ts`): `restricted` = relayed or TCP-only (`net-udp-blocked`), `quality` =\n * the self-assessment (`net-quality-self`), `videoPaused` = the camera was paused by the\n * network to keep the audio (`net-video-auto-off`). The room engine is the one writer.\n */\n readonly localNetwork: LocalNetwork;\n /** The `share` region. */\n readonly share: ShareRegion;\n /**\n * The 1:1 call this tab is placing or answering (CORE-DESIGN §3.6) — the core `calls` slice.\n * The ringing phases (`ringing_out` / `ringing_in` / `call_*`) describe it; null = no call.\n */\n readonly call: { readonly invite: CallInvite; readonly direction: \"outgoing\" | \"incoming\" } | null;\n /** S4: this person's wait (see `MeetWaiting`); null outside the waiting room. */\n readonly waiting: MeetWaiting | null;\n readonly meeting: MeetingRecord | null;\n readonly localIdentity: ParticipantIdentity | null;\n /**\n * S7: this person is already in this meeting on another tab or device, and the pre-join offers\n * the meeting's choice — `rule` is the `second_device` knob (`ask_switch_or_join` · Meet: Switch\n * here / Join here too; `ask_transfer_or_add` · Teams: Transfer / Add). Null otherwise.\n */\n readonly secondDevice: { readonly rule: string } | null;\n /** The server's word on what THIS joiner may do. See `MeetGrants`. */\n readonly grants: MeetGrants;\n readonly participants: readonly MeetParticipant[];\n readonly activeSpeakers: readonly ParticipantIdentity[];\n readonly pinned: ParticipantIdentity | null;\n readonly layout: StageLayout;\n readonly lobby: readonly LobbyEntry[];\n readonly locked: boolean;\n /**\n * 🚨 THE DURABLE `meet_recordings.state`, not a client's hope. Through 0.4.7\n * this only ever held what THIS browser optimistically patched — `starting`\n * on the click, `stopping` on the stop — so the indicator read \"Starting\n * recording…\" for the whole meeting while a perfectly healthy egress ran, and\n * \"Stopping recording…\" forever after (MRI-C8). The server's transitions\n * arrive on two transports now: the `meet_recordings` row for a member, and\n * the room's replicated state for a guest, who has no database.\n */\n readonly recording: RecordingState;\n readonly recordingStartedBy: UserId | null;\n /**\n * WHICH transport last spoke, so the two cannot undo each other (D16). The\n * database wins: `\"record\"` is never overwritten by `\"room\"` or `\"local\"`.\n */\n readonly recordingSource: RecordingSource;\n /**\n * WHICH recording this state is about. It is what tells a LATE packet for a\n * finished recording apart from the FIRST packet of the next one — without\n * it, \"the database wins\" either blocks a second recording forever or lets a\n * stale packet drag a finished one back to `recording`.\n */\n readonly recordingId: string | null;\n readonly chat: readonly RoomChatMessage[];\n /** Whether the SAVED chat has been read for this meeting (late joiners see history). */\n readonly chatLoaded: boolean;\n /** Why the saved chat could not be read on join, in words — or null. */\n readonly chatHistoryError: string | null;\n readonly reactions: readonly RoomReaction[];\n /**\n * The participant a HOST spotlighted for everyone, from the room's metadata\n * (server-set, delivered on join). A local pin outranks it on this screen only.\n */\n readonly spotlight: ParticipantIdentity | null;\n /**\n * S8 REPLICATED ROOM STATE (CORE-DESIGN §2.6 RS), from the room's metadata — the server's\n * truth, delivered on join and after every reconnect: who holds the run's host powers\n * (`actingHost`), who is presenting, and how many people wait to be let in.\n */\n readonly actingHost: ParticipantIdentity | null;\n readonly presenter: ParticipantIdentity | null;\n readonly waitingCount: number;\n /** S8: what the host's Leave offers in this meeting (from the token's `leave_policy`). */\n readonly leavePolicy: LeavePolicy;\n /** The meeting's rules as they are RIGHT NOW (chat mode, attendee lock, time limit), read live while in the call. */\n readonly livePolicy: LivePolicy;\n /** host-reclaim: this person is the meeting's owner and may take the host powers back (Zoom rules). */\n readonly canReclaim: boolean;\n /** rec-consent-gate: this person agreed to the recording that is running. */\n readonly recordingConsentGiven: boolean;\n /** When this run started (epoch ms), so every screen counts a time limit down together. */\n readonly sessionStartedAt: number | null;\n /** True from the moment a host muted THIS microphone until the person turns it on themselves. */\n readonly mutedByHost: boolean;\n /** The last thing a host did to THIS participant, in words. Dismissible. */\n readonly moderationNotice: ModerationNotice | null;\n /**\n * The server's answer to the last host action THIS participant took (\"Muted 3\n * people\", or the refusal). One place, so whichever surface acted — a tile's\n * menu, the People panel, More options — the sentence survives the menu closing.\n */\n readonly actionNotice: { readonly text: string; readonly failed: boolean; readonly at: number } | null;\n /**\n * \"Message privately\" asked for from ANY per-person menu — a tile's, the\n * People panel's — and answered by the one `ControlBar` that owns the chat\n * panel (0.7.8). Through 0.7.7 only the People panel could open a private\n * chat, so the Board's tile menu (and the Room's) had no such item while the\n * People panel did: two menus for one person that disagreed.\n */\n readonly privateChatRequest: { readonly to: ParticipantIdentity; readonly at: number } | null;\n readonly effects: MediaEffects;\n /**\n * Captions AND durable rows, reconciled into one list (D16). A record\n * replaces the caption it supersedes; a caption never overwrites a record.\n */\n readonly transcript: readonly TranscriptSegment[];\n readonly notes: readonly MeetingNote[];\n /**\n * 🚨 IDENTITY → THE BEST NAME ANYTHING HAS EVER KNOWN (MRI-A16). Learned from\n * every source that has one — the roster, a durable segment, a durable\n * attendance row, a chat message — and never unlearned, so a caption that\n * lands after somebody leaves still says who spoke. It exists because the\n * host and the guest rendered DIFFERENT names for the same speaker in the\n * same second, one of them a raw LiveKit token (MRI-C8). See `core/names.ts`.\n */\n readonly names: NameBook;\n /**\n * The wrap-up pass ANNOUNCED itself (MRI-A12). `null` means it has not been\n * announced in this room — which is not the same as \"no summary exists\", and\n * no surface may render it as such.\n */\n readonly wrapUp: WrapUpAnnouncement | null;\n /**\n * The server's last refusal to dispatch the note-taker, in ITS words (D7).\n * Rendered verbatim; never replaced by a spinner or a house sentence.\n */\n readonly noteTakerRefusal: NoteTakerRefusal | null;\n /** `captions-toggle`: THIS viewer wants captions. Local to the viewer; never sent to the room. */\n readonly captionsOn: boolean;\n /** Why asking for captions failed, in the server's words (`captions-unavailable`). */\n readonly captionsRefusal: NoteTakerRefusal | null;\n /** `captions_start_timeout_seconds` from the token's knobs; the \"Starting captions\" ceiling. */\n readonly captionsStartTimeoutSeconds: number;\n /**\n * What the LOCAL participant is actually publishing, and why not when it is\n * not. Attendance never depends on it (see `attendance.ts`): a refused\n * microphone is a state with a remedy, not a failed join.\n */\n readonly media: LocalMediaState;\n /**\n * S6c — the media supervisor's regions: permission per kind, autoplay (`audio_output`), the\n * last device change and an offered new device. Written only by `media-supervisor.ts`.\n */\n readonly mediaRegion: MediaRegionState;\n /** The last failure, with its remedy. Never null-and-hoping. */\n readonly failure: {\n readonly message: string;\n readonly remedy: string;\n /** The join gate's reason code, when the server named one (CORE-DESIGN §2.5). */\n readonly reason?: string | null;\n /**\n * When a refusal lifts by itself (a denial's cooldown): the epoch-ms moment the person may\n * ask again. Absent for every refusal that does not lift (CORE-DESIGN §11 `wr-denied-reknock`).\n */\n readonly retryAt?: number | null;\n /**\n * `join` = the person never got into the call this time (`net-cannot-connect`): a network or\n * server failure on the way IN is \"can't connect\", not \"lost your connection\".\n */\n readonly stage?: \"join\";\n } | null;\n /**\n * Meet wave 5: polls, Q&A, the shared whiteboard and the breakout plan — what the\n * server said, per viewer (`core/collab.ts`). Read once per join, then packets.\n */\n readonly collab: CollabSnapshot;\n /** The breakout room THIS participant is in, or null for the main room. */\n readonly breakoutRoomId: string | null;\n}\n\nconst EMPTY: MeetSnapshot = {\n phase: \"resolving\",\n reason: null,\n phaseNotice: null,\n roomName: null,\n connection: \"stable\",\n connectionBanner: false,\n localNetwork: UNKNOWN_LOCAL_NETWORK,\n share: NO_SHARE,\n call: null,\n waiting: null,\n meeting: null,\n localIdentity: null,\n secondDevice: null,\n grants: NO_GRANTS,\n participants: [],\n activeSpeakers: [],\n pinned: null,\n layout: \"grid\",\n lobby: [],\n locked: false,\n recording: \"idle\",\n recordingStartedBy: null,\n recordingSource: \"local\",\n recordingId: null,\n chat: [],\n chatLoaded: false,\n chatHistoryError: null,\n reactions: [],\n spotlight: null,\n actingHost: null,\n presenter: null,\n waitingCount: 0,\n leavePolicy: DEFAULT_LEAVE_POLICY,\n livePolicy: DEFAULT_LIVE_POLICY,\n canReclaim: false,\n recordingConsentGiven: false,\n sessionStartedAt: null,\n mutedByHost: false,\n moderationNotice: null,\n actionNotice: null,\n privateChatRequest: null,\n effects: DEFAULT_EFFECTS,\n transcript: [],\n notes: [],\n names: {},\n wrapUp: null,\n noteTakerRefusal: null,\n captionsOn: false,\n captionsRefusal: null,\n captionsStartTimeoutSeconds: 20,\n media: IDLE_MEDIA,\n mediaRegion: IDLE_MEDIA_REGION,\n failure: null,\n collab: EMPTY_COLLAB,\n breakoutRoomId: null,\n};\n\n/**\n * 🚨 THE ONE ANSWER TO \"WHAT ROLE DOES THIS CLIENT'S USER HOLD\" (MRI-A11).\n *\n * Two SERVER-minted carriers, one decision: the `/meet/token` response (in the\n * store from the moment the token lands, before any roster exists) and the\n * local participant's roster entry (whose role comes from the\n * `matrx.participant.role` LiveKit attribute). The stronger wins. Every host\n * power in this package — the UI that renders it AND the code that performs it\n * — asks THIS function, so a control can never render from one source and then\n * be refused by another. That split is exactly how a \"host control\" becomes a\n * button that throws.\n */\nexport function effectiveRole(snapshot: MeetSnapshot): ParticipantRole {\n const me = snapshot.participants.find(\n (participant) => participant.identity === snapshot.localIdentity,\n );\n const fromRoster = me?.role ?? \"participant\";\n const fromToken = snapshot.grants.role;\n return ROLE_RANK[fromRoster] >= ROLE_RANK[fromToken] ? fromRoster : fromToken;\n}\n\n/** This person's standing: the token's word, else what the roster carries for them. */\nexport function effectiveStanding(snapshot: MeetSnapshot): ParticipantStanding | null {\n if (snapshot.grants.standing != null) return snapshot.grants.standing;\n const me = snapshot.participants.find((p) => p.identity === snapshot.localIdentity);\n return me?.standing ?? null;\n}\n\n/** Reactions live this long. A dropped end event cannot outlive it. */\nexport const REACTION_TTL_MS = 5_000;\n/** In-room chat is EPHEMERAL by design (R3) — this is the visible backlog. */\nconst CHAT_LIMIT = 200;\n\n/** What `patch` accepts: the snapshot minus the machine-owned fields. */\nexport type MeetPatch = Partial<Omit<MeetSnapshot, \"phase\" | \"reason\" | \"phaseNotice\">>;\n\nexport interface MeetStore {\n snapshot(): MeetSnapshot;\n subscribe(listener: () => void): () => void;\n /** Everything EXCEPT the phase. The phase moves only through `dispatch`. */\n patch(next: MeetPatch): void;\n /**\n * THE ONE WRITER OF THE PHASE (CORE-DESIGN §3.1). Applies `event` through the machine's table;\n * returns false — and changes nothing — when the table does not allow it from the current\n * phase. `reason` overrides the phase's own reason (an exit's `removed`, `displaced`, …).\n */\n dispatch(event: PhaseEvent, detail?: { readonly reason?: string | null; readonly notice?: string | null }): boolean;\n setParticipants(participants: readonly MeetParticipant[]): void;\n setSpeaking(identities: readonly ParticipantIdentity[]): void;\n setQuality(identity: ParticipantIdentity, quality: ConnectionQuality): void;\n addChat(message: RoomChatMessage): void;\n /** The saved chat, read once per join. Merged with anything that arrived live. */\n loadChat(messages: readonly RoomChatMessage[]): void;\n /** Replace a pending message (by id) with what the server saved, or mark it failed. */\n settleChat(localId: string, next: RoomChatMessage | { failed: string }): void;\n addReaction(reaction: RoomReaction): void;\n /** Drops reactions past their TTL. Called by the UI's animation frame. */\n expireReactions(now: number): void;\n /**\n * The recording reached a state, and WHO says so. `\"local\"` is this browser's\n * optimistic `starting`/`stopping`; `\"room\"` is the server's packet on the\n * media plane; `\"record\"` is the durable row. Precedence is D16's: the\n * database's word stands until the database changes it.\n */\n setRecording(\n state: RecordingState,\n by: UserId | null,\n source: RecordingSource,\n recordingId?: string | null,\n ): void;\n upsertLobby(entry: LobbyEntry): void;\n setAdmission(identity: ParticipantIdentity, state: AdmissionState, by: UserId | null): void;\n /**\n * Teach the name book. Every source that knows a name calls this, and only\n * a REAL name is learned — an identity handed back as its own name is not one.\n */\n learn(entries: readonly { identity: ParticipantIdentity; displayName?: string | null }[]): void;\n /** A live caption from the media plane. A HINT (D16). */\n ingestCaption(segment: TranscriptSegment): void;\n /** A durable `meet_transcript_segments` row. THE TRANSCRIPT (D16). */\n ingestRecord(segment: TranscriptSegment): void;\n /** Durable notes are re-written in place — a re-run rewrites the same ids. */\n upsertNote(note: MeetingNote): void;\n /** The wrap-up pass finished. Announced on the media plane only (MRI-A12). */\n announceWrapUp(announcement: WrapUpAnnouncement): void;\n /** Record what a local track ACTUALLY did — published, or refused and why. */\n setLocalTrack(kind: \"microphone\" | \"camera\", state: LocalTrackState): void;\n reset(): void;\n}\n\nexport function createMeetStore(initial: Partial<MeetSnapshot> = {}): MeetStore {\n let state: MeetSnapshot = { ...EMPTY, ...initial };\n const listeners = new Set<() => void>();\n\n function commit(next: MeetSnapshot): void {\n if (next === state) return;\n state = next;\n for (const listener of listeners) listener();\n }\n\n return {\n snapshot: () => state,\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n patch(next) {\n if (\"phase\" in next || \"reason\" in next || \"phaseNotice\" in next) {\n // The type forbids it; this catches a cast. The phase has ONE writer.\n throw new TypeError(\"meet store: the phase moves only through store.dispatch(event)\");\n }\n commit({ ...state, ...next });\n },\n dispatch(event, detail) {\n const to = nextPhase(state.phase, event);\n if (to === null) return false;\n const reason = detail?.reason !== undefined ? detail.reason : reasonOf(to);\n const phaseNotice = detail?.notice ?? null;\n if (to === state.phase && reason === state.reason && phaseNotice === state.phaseNotice) return true;\n commit({ ...state, phase: to, reason, phaseNotice });\n return true;\n },\n setParticipants(participants) {\n // The roster TEACHES the book first — a participant whose token carried a\n // real name is where most names come from — and every roster entry is then\n // RESOLVED back through it, so a participant the roster cannot name still\n // gets the best name a durable row or a caption ever taught (MRI-A18).\n const names = learnNames(state.names, participants);\n const quality = new Map(state.participants.map((p) => [p.identity, p.quality]));\n commit({\n ...state,\n names,\n participants: participants.map((participant) => ({\n ...participant,\n // 🚨 THE ONE ROSTER RESOLUTION. Every surface that renders a person —\n // the tile, the People row, the header summary, every Pin/Mute label —\n // reads `participant.displayName`, so this is the single place a raw\n // identity could reach a screen, and it is the place that asks the\n // name book. `roster-names.test.ts` fails if a token ever gets past it.\n displayName: displayNameFor(names, participant.identity, participant.displayName),\n quality:\n participant.quality === \"unknown\"\n ? quality.get(participant.identity) ?? \"unknown\"\n : participant.quality,\n })),\n });\n },\n setSpeaking(identities) {\n const speaking = new Set(identities);\n commit({\n ...state,\n activeSpeakers: identities,\n participants: state.participants.map((participant) =>\n participant.speaking === speaking.has(participant.identity)\n ? participant\n : { ...participant, speaking: speaking.has(participant.identity) },\n ),\n });\n },\n setQuality(identity, quality) {\n commit({\n ...state,\n participants: state.participants.map((participant) =>\n participant.identity === identity ? { ...participant, quality } : participant,\n ),\n });\n },\n addChat(message) {\n if (state.chat.some((existing) => existing.id === message.id)) return;\n const chat = [...state.chat, message];\n // A chat message carries its sender's name — another source, same book.\n const names = learnName(state.names, message.identity, message.displayName);\n commit({ ...state, chat: chat.slice(-CHAT_LIMIT), names });\n },\n loadChat(messages) {\n const known = new Set(state.chat.map((message) => message.id));\n const merged = [...messages.filter((message) => !known.has(message.id)), ...state.chat].sort(\n (a, b) => (a.at < b.at ? -1 : a.at > b.at ? 1 : 0),\n );\n const names = learnNames(\n state.names,\n messages.map((message) => ({ identity: message.identity, displayName: message.displayName })),\n );\n commit({ ...state, chat: merged.slice(-CHAT_LIMIT), chatLoaded: true, chatHistoryError: null, names });\n },\n settleChat(localId, next) {\n const index = state.chat.findIndex((message) => message.id === localId);\n if (index === -1) return;\n const current = state.chat[index] as RoomChatMessage;\n let replacement: RoomChatMessage;\n if (\"failed\" in next && typeof next.failed === \"string\" && !(\"id\" in next)) {\n replacement = { ...current, failed: next.failed };\n } else {\n replacement = next as RoomChatMessage;\n }\n // The saved copy may ALREADY be here (the room packet beat the response):\n // then the pending one simply goes.\n const duplicate =\n replacement.id !== localId && state.chat.some((message) => message.id === replacement.id);\n const chat = duplicate\n ? state.chat.filter((message) => message.id !== localId)\n : state.chat.map((message, position) => (position === index ? replacement : message));\n commit({ ...state, chat });\n },\n addReaction(reaction) {\n commit({ ...state, reactions: [...state.reactions, reaction] });\n },\n expireReactions(now) {\n const live = state.reactions.filter((reaction) => now - reaction.at < REACTION_TTL_MS);\n if (live.length === state.reactions.length) return;\n commit({ ...state, reactions: live });\n },\n setRecording(next, by, source, recordingId = null) {\n // 🚨 THE DATABASE WINS (D16). A member receives every transition TWICE —\n // the `meet_recordings` row over Postgres Changes and the\n // room's replicated state — and whichever is slower\n // must not be able to drag the indicator backwards.\n //\n // The rule is about ONE recording, not about the meeting: a meeting can\n // be recorded again, and the second recording's first word is not a\n // contradiction of the first one's ending. So the durable state stands\n // against anything that is about the SAME recording, and against a\n // sourceless update while a recording is still in flight.\n // `record` always wins. The other two are refused when they would\n // contradict the durable word rather than continue past it:\n //\n // * `local` is this browser acting NOW — a host pressing Start again on a\n // meeting whose last recording is over. It is refused only while a\n // recording is genuinely in flight, where a stale optimistic patch\n // could drag a live indicator backwards.\n // * `room` carries the recording's id, so a LATE packet about the\n // recording that just finished is told apart from the FIRST packet of\n // the next one. Same recording: refused. Different recording: refused\n // only while the durable one is still in flight.\n if (state.recordingSource === \"record\" && source !== \"record\") {\n const aboutTheSameRecording =\n source === \"room\" &&\n (recordingId === null ||\n state.recordingId === null ||\n recordingId === state.recordingId);\n if (aboutTheSameRecording || isRecordingInFlight(state.recording)) return;\n }\n if (\n state.recording === next &&\n state.recordingStartedBy === by &&\n state.recordingSource === source &&\n state.recordingId === (recordingId ?? state.recordingId)\n ) {\n // Identity-stable: an unchanged state must not wake every subscriber.\n return;\n }\n commit({\n ...state,\n recording: next,\n recordingStartedBy: by,\n recordingSource: source,\n recordingId: recordingId ?? state.recordingId,\n });\n },\n upsertLobby(entry) {\n const index = state.lobby.findIndex((existing) => existing.identity === entry.identity);\n const lobby =\n index === -1\n ? [...state.lobby, entry]\n : state.lobby.map((existing, position) => (position === index ? entry : existing));\n commit({ ...state, lobby });\n },\n setAdmission(identity, admissionState, by) {\n commit({\n ...state,\n lobby: state.lobby.map((entry) =>\n entry.identity === identity\n ? {\n ...entry,\n state: admissionState,\n decidedAt: new Date().toISOString(),\n decidedBy: by,\n }\n : entry,\n ),\n });\n },\n learn(entries) {\n const names = learnNames(state.names, entries);\n // Identity-stable: learning nothing must not wake every subscriber, and\n // this is called on every roster event and every durable row.\n if (names === state.names) return;\n commit({ ...state, names });\n },\n ingestCaption(segment) {\n // A caption's roster lookup is a source of names like any other.\n const names = learnName(state.names, segment.identity, segment.speaker);\n const transcript = mergeCaption(state.transcript, segment);\n if (transcript === state.transcript && names === state.names) return;\n commit({ ...state, transcript, names });\n },\n ingestRecord(segment) {\n // 🚨 THE DURABLE ROW'S `speaker` IS THE BEST NAME THERE IS: the note-taker\n // wrote it from `meet_participants`, which is where \"Ada Lovelace\" lived\n // while the guest's screen was printing `guest:c8-spk-1`.\n const names = learnName(state.names, segment.identity, segment.speaker);\n const transcript = mergeRecord(state.transcript, segment);\n if (transcript === state.transcript && names === state.names) return;\n commit({ ...state, transcript, names });\n },\n upsertNote(note) {\n const index = state.notes.findIndex((existing) => existing.id === note.id);\n if (index === -1) {\n commit({ ...state, notes: [...state.notes, note] });\n return;\n }\n const existing = state.notes[index];\n // Identity-stable: an unchanged note must not wake `useSyncExternalStore`.\n // 🚨 `source` is part of \"unchanged\" and that is load-bearing, not\n // cosmetic: when the room copy and the database copy carry the same words,\n // dropping the second arrival would leave the note marked `\"room\"` forever\n // — and the precedence rule below would then let a LATER stale packet\n // overwrite a row the database had already confirmed.\n if (\n existing !== undefined &&\n existing.text === note.text &&\n existing.kind === note.kind &&\n existing.supersededBy === note.supersededBy &&\n existing.source === note.source\n ) {\n return;\n }\n // 🚨 THE DATABASE WINS (D16, MRI-A12). The identical row arrives on two\n // transports — a `meet_notes` row over Postgres Changes and a\n // `matrx.meet.note` packet on the media plane — and whichever is slower\n // must not be able to undo the other. A `\"room\"` copy never overwrites a\n // `\"record\"` one; a `\"record\"` copy always overwrites a `\"room\"` one.\n // Without this, a re-run that rewrote a note could flip a member's screen\n // back to the superseded wording purely on arrival order.\n if (existing !== undefined && existing.source === \"record\" && note.source === \"room\") {\n return;\n }\n commit({\n ...state,\n notes: state.notes.map((entry, position) => (position === index ? note : entry)),\n });\n },\n announceWrapUp(announcement) {\n const current = state.wrapUp;\n if (current !== null && current.at === announcement.at) return;\n commit({ ...state, wrapUp: announcement });\n },\n setLocalTrack(kind, next) {\n const current = state.media[kind];\n // Identity-stable: an unchanged track must not wake `useSyncExternalStore`.\n if (\n current.enabled === next.enabled &&\n current.unavailable?.code === next.unavailable?.code &&\n current.unavailable?.reason === next.unavailable?.reason &&\n current.unavailable?.message === next.unavailable?.message\n ) {\n return;\n }\n commit({\n ...state,\n media: { ...state.media, [kind]: next },\n // The person turned their own microphone on again: the host's mute is over.\n ...(kind === \"microphone\" && next.enabled && state.mutedByHost ? { mutedByHost: false } : {}),\n });\n },\n reset() {\n // The phase goes back through THE MACHINE like every other move (`teardown`: the engine has\n // already closed its connection); everything else returns to empty. A person's own media\n // choices outlive a room: they are theirs, per browser.\n const phase = nextPhase(state.phase, { type: \"reset\", teardown: true }) ?? EMPTY.phase;\n commit({ ...EMPTY, effects: state.effects, phase, reason: reasonOf(phase), phaseNotice: null });\n },\n };\n}\n","/**\n * THE PLATFORM'S OWN VOICE IN THE ROOM — and the ONLY live feed a guest has.\n *\n * 🚨 **Why this file exists.** Everything durable a meeting produces — a live\n * note, a decision, an action item, the note-taker's state, the ending — lands\n * in `communication.meet_*`, and a signed-in member learns about it over\n * Postgres Changes. A GUEST cannot: `anon` holds no SELECT grant on\n * `meet_notes`, `meet_transcript_segments` or `meet_participants`, and MRI-A10\n * settled that widening it is forbidden, because those are org-scoped tables and\n * a meeting link is not org membership. A guest's authority is the ROOM TOKEN.\n * So the platform publishes the same rows into the room, where the token is the\n * authorization, and this file turns those packets into exactly the same store\n * calls the database path makes.\n *\n * Three rules hold it together:\n *\n * 1. **Same row, same projection, same reducer.** A `matrx.meet.note` packet\n * carries a `meet_notes` row and is projected by `projectNote` — the very\n * function the Postgres Changes handler uses — then handed to\n * `store.upsertNote`. A guest and a member therefore converge on one code\n * path rather than on two implementations that agree until they don't.\n * 2. **Keyed by the row id, so both copies are one thing.** A member receives\n * every note TWICE. They render once because the id is the row's id on both\n * transports, and where the two could disagree the database wins\n * (`store.upsertNote`'s precedence rule, D16).\n * 3. **`matrx.meet.*` is the server's namespace and nothing else's.** LiveKit\n * does not validate a data packet's sender, so any participant could publish\n * any bytes under any topic. What makes these trustworthy is that publishing\n * them requires a `roomAdmin` grant, which only the server's own control\n * token carries — a participant token cannot claim it. The in-room control\n * plane participants DO publish (hand, reaction, chat) lives under `mx.*`\n * and is handled separately, precisely so the two can never be confused.\n */\n\nimport { applyCollabPacket } from \"./collab\";\nimport { projectNote } from \"./repository\";\nimport { effectiveRole, type MeetStore, type RoomChatMessage } from \"./store\";\nimport {\n asMeetingId,\n asParticipantIdentity,\n ROLE_RANK,\n type JsonObject,\n type MeetingRecord,\n type ParticipantRole,\n type RecordingState,\n type UserId,\n type WrapUpAnnouncement,\n} from \"./types\";\n\n/** Pinned against `RecordingState`; an unknown word is dropped, never shown. */\nconst RECORDING_STATES: readonly RecordingState[] = [\n \"idle\",\n \"starting\",\n \"recording\",\n \"stopping\",\n \"processing\",\n \"available\",\n \"expired\",\n \"failed\",\n];\n\n/**\n * The server's namespace. Pinned against `aidream/services/meet/livekit.py`'s\n * `ROOM_EVENT_PREFIX` — a rename on either side must break a named test here.\n */\nexport const ROOM_EVENT_PREFIX = \"matrx.meet.\";\n\nexport const ROOM_EVENTS = {\n /** One durable `meet_notes` row was written. Payload `{ note: <row> }`. */\n note: \"matrx.meet.note\",\n /** `meet_meetings.metadata.note_taker` changed. Payload `{ meeting_id, note_taker }`. */\n noteTaker: \"matrx.meet.note_taker\",\n /** A wrap-up pass finished. Payload `{ meeting_id, note_ids, segment_count }`. */\n wrapUp: \"matrx.meet.wrap_up\",\n /** The meeting ended for everyone. Payload `{ meeting_id, ended_at, trigger }`. */\n ended: \"matrx.meet.ended\",\n /**\n * A chat message to everyone was SAVED (Meet wave 2). Payload\n * `{ message: { id, meeting_id, sender_identity, sender_name, body, created_at } }`.\n */\n chat: \"matrx.meet.chat\",\n /**\n * A host or co-host acted (Meet wave 2). Payload `{ meeting_id, kind, … }`:\n * `muted {source, by}` · `ask-unmute {by}` · `removed {by}` — addressed to the\n * person concerned; `role {identity, role}` — to everyone.\n */\n moderation: \"matrx.meet.moderation\",\n /**\n * A meeting ROOM ITEM changed (Meet wave 5): a poll, a Q&A question, a whiteboard\n * stroke or the breakout plan. Payload `{ meeting_id, item }` · `{ removed: [ids] }` ·\n * `{ hidden: [ids] }` · `{ cleared: \"whiteboard\" }` · `{ broadcast }` · `{ item, move }`.\n * A host's fuller copy of a poll or question carries `audience: \"hosts\"`.\n */\n collab: \"matrx.meet.collab\",\n} as const;\n\nexport type RoomEventTopic = (typeof ROOM_EVENTS)[keyof typeof ROOM_EVENTS];\n\nexport function isRoomEventTopic(topic: string): boolean {\n return topic.startsWith(ROOM_EVENT_PREFIX);\n}\n\ntype Payload = Record<string, unknown>;\n\nfunction object(value: unknown): Payload | null {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Payload)\n : null;\n}\n\nfunction text(value: unknown): string | null {\n return typeof value === \"string\" && value.length > 0 ? value : null;\n}\n\n/**\n * What happened to one packet. Returned rather than logged from inside, so the\n * room engine can report a drop with its own diagnostic channel and a test can\n * assert on it — a packet that is silently ignored is indistinguishable from\n * one that never arrived, which is the failure this whole item is about.\n */\nexport interface RoomEventOutcome {\n readonly handled: boolean;\n /** Present when `handled` is false. Says what was wrong, in a sentence. */\n readonly ignored?: string;\n}\n\nconst HANDLED: RoomEventOutcome = { handled: true };\n\nfunction ignored(reason: string): RoomEventOutcome {\n return { handled: false, ignored: reason };\n}\n\nexport interface ApplyRoomEventArgs {\n readonly store: MeetStore;\n readonly topic: string;\n /** The packet's already-parsed JSON body. */\n readonly payload: unknown;\n readonly now?: (() => number) | undefined;\n}\n\n/**\n * Apply one `matrx.meet.*` packet to the store. Never throws.\n *\n * A malformed packet is DROPPED with a reason, never partially applied: half a\n * note on a guest's screen is worse than no note, because the guest cannot tell\n * which half they are missing.\n */\nexport function applyRoomEvent(args: ApplyRoomEventArgs): RoomEventOutcome {\n const { store, topic } = args;\n if (!isRoomEventTopic(topic)) {\n return ignored(`${topic} is not a Meet room event`);\n }\n const payload = object(args.payload);\n if (payload === null) {\n return ignored(`${topic} carried no object payload`);\n }\n const now = args.now ?? (() => Date.now());\n\n switch (topic) {\n case ROOM_EVENTS.note:\n return applyNote(store, payload);\n case ROOM_EVENTS.noteTaker:\n return applyNoteTaker(store, payload);\n case ROOM_EVENTS.wrapUp:\n return applyWrapUp(store, payload, now);\n case ROOM_EVENTS.ended:\n return applyEnded(store, payload);\n case ROOM_EVENTS.chat:\n return applyChat(store, payload);\n case ROOM_EVENTS.moderation:\n return applyModeration(store, payload, now);\n case ROOM_EVENTS.collab:\n return applyCollab(store, payload, now);\n default:\n // A NEWER SERVER, not a bug. Ignoring an unknown event under our own\n // prefix is what lets the platform ship a new one without breaking every\n // client that has not been redeployed.\n return ignored(`${topic} is not known to this version of @ai-matrx/meet`);\n }\n}\n\nfunction applyCollab(store: MeetStore, payload: Payload, now: () => number): RoomEventOutcome {\n const snapshot = store.snapshot();\n const meeting = snapshot.meeting;\n if (meeting !== null && text(payload[\"meeting_id\"]) !== null && payload[\"meeting_id\"] !== meeting.id) {\n return ignored(\"a collab event for a different meeting than this room is showing\");\n }\n const result = applyCollabPacket(snapshot.collab, payload, {\n localIdentity: snapshot.localIdentity,\n isHost: ROLE_RANK[effectiveRole(snapshot)] >= ROLE_RANK.cohost,\n now: now(),\n });\n if (result.next === null) return ignored(result.dropped ?? \"a collab packet that changed nothing\");\n store.patch({ collab: result.next });\n return HANDLED;\n}\n\nfunction applyNote(store: MeetStore, payload: Payload): RoomEventOutcome {\n const row = object(payload[\"note\"]);\n if (row === null) return ignored(\"a note event with no `note` row\");\n if (text(row[\"id\"]) === null || text(row[\"meeting_id\"]) === null) {\n // Without the row id there is no dedupe key, and appending a note that can\n // never be matched to its database copy is exactly the double-render bug.\n return ignored(\"a note row with no id or meeting_id\");\n }\n const meeting = store.snapshot().meeting;\n if (meeting !== null && row[\"meeting_id\"] !== meeting.id) {\n return ignored(\"a note for a different meeting than this room is showing\");\n }\n try {\n // THE SAME PROJECTION THE DATABASE PATH USES — one definition of what a\n // note row means — re-stamped as the room copy so the store's precedence\n // rule can keep the database's word on top.\n store.upsertNote({ ...projectNote(row), source: \"room\" });\n } catch (error) {\n return ignored(`a note row this client could not read: ${String(error)}`);\n }\n return HANDLED;\n}\n\nfunction applyNoteTaker(store: MeetStore, payload: Payload): RoomEventOutcome {\n const noteTaker = object(payload[\"note_taker\"]);\n if (noteTaker === null) return ignored(\"a note-taker event with no state\");\n const meeting = store.snapshot().meeting;\n if (meeting === null) {\n // An ad-hoc call room has no meeting record to hang the state on, and\n // there is no note-taker in one. Stated, not silent.\n return ignored(\"a note-taker state with no meeting record to attach it to\");\n }\n const meetingId = text(payload[\"meeting_id\"]);\n if (meetingId !== null && meetingId !== meeting.id) {\n return ignored(\"a note-taker state for a different meeting\");\n }\n // MERGED, NOT REPLACED. The packet carries only the `note_taker` object — the\n // two keys a surface renders — because the rest of the row names people and\n // organizations a guest is not authorized to learn. Everything else on the\n // meeting record stays exactly as the join backfill left it.\n const next: MeetingRecord = {\n ...meeting,\n metadata: { ...meeting.metadata, note_taker: noteTaker as JsonObject },\n };\n store.patch({ meeting: next });\n return HANDLED;\n}\n\nfunction applyWrapUp(\n store: MeetStore,\n payload: Payload,\n now: () => number,\n): RoomEventOutcome {\n const meetingId = text(payload[\"meeting_id\"]);\n if (meetingId === null) return ignored(\"a wrap-up event with no meeting id\");\n const meeting = store.snapshot().meeting;\n if (meeting !== null && meetingId !== meeting.id) {\n return ignored(\"a wrap-up for a different meeting\");\n }\n const rawIds = payload[\"note_ids\"];\n const noteIds = Array.isArray(rawIds)\n ? rawIds.filter((value): value is string => typeof value === \"string\")\n : [];\n const segmentCount = payload[\"segment_count\"];\n const announcement: WrapUpAnnouncement = {\n meetingId: asMeetingId(meetingId),\n noteIds,\n segmentCount: typeof segmentCount === \"number\" ? segmentCount : 0,\n at: new Date(now()).toISOString(),\n };\n store.announceWrapUp(announcement);\n return HANDLED;\n}\n\n/**\n * S8: a recording's state arrives as REPLICATED ROOM STATE (the room's metadata, written only by\n * aidream's `room_state.publish_room_state`), which LiveKit delivers on join and after every\n * reconnect — never a one-shot packet a late joiner misses. Called by the engine's room-metadata\n * handler with the metadata's `recording` object and `meeting_id`.\n */\nexport function applyRecordingState(store: MeetStore, payload: Payload): RoomEventOutcome {\n const row = object(payload[\"recording\"]);\n if (row === null) return ignored(\"room state carries no recording\");\n const meetingId = text(payload[\"meeting_id\"]);\n const meeting = store.snapshot().meeting;\n if (meeting !== null && meetingId !== null && meetingId !== meeting.id) {\n return ignored(\"a recording state for a different meeting than this room is showing\");\n }\n const state = text(row[\"state\"]);\n if (state === null || !RECORDING_STATES.includes(state as RecordingState)) {\n // An UNKNOWN state is dropped rather than rendered: a recording indicator\n // that says a word nobody defined is worse than one that says nothing.\n return ignored(`a recording state this version does not know: ${String(row[\"state\"])}`);\n }\n const by = text(row[\"started_by\"]);\n // `\"room\"` — the durable row outranks this the moment a member's\n // subscription delivers it (D16, `store.setRecording`).\n store.setRecording(\n state as RecordingState,\n by === null ? null : (by as UserId),\n \"room\",\n text(row[\"id\"]),\n );\n return HANDLED;\n}\n\nfunction applyEnded(store: MeetStore, payload: Payload): RoomEventOutcome {\n const meetingId = text(payload[\"meeting_id\"]);\n const meeting = store.snapshot().meeting;\n if (meeting === null) return ignored(\"an ended event with no meeting record\");\n if (meetingId !== null && meetingId !== meeting.id) {\n return ignored(\"an ended event for a different meeting\");\n }\n // 🚨 THE LAST THING A GUEST HEARS. Ending a meeting deletes the LiveKit room,\n // so the server publishes this in the moment BEFORE the delete — and a client\n // that ignored it would keep saying the meeting is live while everyone is\n // disconnected. The `left` phase that follows says nothing about WHY.\n const endedAt = text(payload[\"ended_at\"]) ?? new Date().toISOString();\n if (meeting.endedAt === endedAt) return HANDLED;\n store.patch({ meeting: { ...meeting, endedAt } });\n return HANDLED;\n}\n\n/** One saved chat row as the room sees it. Shared with the history read. */\nexport function chatFromRow(row: Payload): RoomChatMessage | null {\n const id = text(row[\"id\"]);\n const identity = text(row[\"sender_identity\"]);\n const body = text(row[\"body\"]);\n if (id === null || identity === null || body === null) return null;\n return {\n id,\n identity: asParticipantIdentity(identity),\n displayName: text(row[\"sender_name\"]) ?? \"Participant\",\n text: body,\n at: text(row[\"created_at\"]) ?? new Date().toISOString(),\n // `false` only from a server whose chat table has not landed: the room heard\n // it, nobody will see it later — and the line says so.\n saved: row[\"saved\"] !== false,\n };\n}\n\nfunction applyChat(store: MeetStore, payload: Payload): RoomEventOutcome {\n const row = object(payload[\"message\"]);\n if (row === null) return ignored(\"a chat event with no `message` row\");\n const meeting = store.snapshot().meeting;\n if (meeting !== null && text(row[\"meeting_id\"]) !== null && row[\"meeting_id\"] !== meeting.id) {\n return ignored(\"a chat message for a different meeting\");\n }\n const message = chatFromRow(row);\n if (message === null) return ignored(\"a chat row with no id, sender or body\");\n store.addChat(message);\n return HANDLED;\n}\n\n/** Sentences for what a host did to THIS participant. */\nfunction moderationText(kind: string, payload: Payload): string | null {\n const by = text(payload[\"by\"]) ?? \"The host\";\n switch (kind) {\n case \"muted\": {\n // Meet names the person who did it (\"<name> muted you\"); the role word only stands in when the server sent no name.\n const source = text(payload[\"source\"]) ?? \"microphone\";\n return source === \"microphone\"\n ? `${by} muted you.`\n : source === \"camera\"\n ? `${by} turned off your camera.`\n : `${by} stopped your screen share.`;\n }\n case \"ask-unmute\":\n return `${by} is asking you to unmute.`;\n case \"removed\":\n return `${by} removed you from this meeting.`;\n default:\n return null;\n }\n}\n\nfunction applyModeration(store: MeetStore, payload: Payload, now: () => number): RoomEventOutcome {\n const snapshot = store.snapshot();\n const meetingId = text(payload[\"meeting_id\"]);\n if (snapshot.meeting !== null && meetingId !== null && meetingId !== snapshot.meeting.id) {\n return ignored(\"a moderation event for a different meeting\");\n }\n const kind = text(payload[\"kind\"]);\n switch (kind) {\n case \"role\": {\n const identity = text(payload[\"identity\"]);\n const role = text(payload[\"role\"]);\n if (identity === null || role === null || !ROLES.includes(role as ParticipantRole)) {\n return ignored(\"a role event with no identity or an unknown role\");\n }\n // THE SERVER CHANGED ITS OWN DECISION. The token's role was minted at join;\n // a co-host made or unmade, or a host handed over, must re-grant or\n // UN-grant this screen's powers now — `effectiveRole` takes the stronger of\n // token and roster, so a demotion that left the token alone would leave the\n // old host holding host controls the server refuses.\n if (identity === snapshot.localIdentity) {\n store.patch({\n grants: { ...snapshot.grants, role: role as ParticipantRole },\n moderationNotice: {\n kind: \"role\",\n text:\n role === \"host\"\n ? \"You are now the host.\"\n : role === \"cohost\"\n ? \"You are now a co-host.\"\n : role === \"presenter\"\n ? \"You are now a presenter.\"\n : snapshot.grants.role === \"presenter\"\n ? \"You are now a participant: you can no longer present.\"\n : \"You are no longer a co-host.\",\n at: now(),\n },\n });\n }\n return HANDLED;\n }\n case \"share-ended\": {\n // share-takeover / share-who-can: the SERVER took this person's share down and says why.\n const reason = text(payload[\"reason\"]) === \"not_allowed\" ? \"not_allowed\" : \"replaced\";\n const by = text(payload[\"by\"]);\n store.patch({\n share: { state: \"ended\", reason, remedy: null, at: now(), ...(by !== null ? { by } : {}) },\n });\n return HANDLED;\n }\n case \"muted\":\n case \"ask-unmute\":\n case \"removed\": {\n const sentence = moderationText(kind, payload);\n if (sentence === null) return ignored(\"a moderation event with no sentence\");\n store.patch({\n moderationNotice: { kind, text: sentence, at: now() },\n ...(kind === \"muted\" && (text(payload[\"source\"]) ?? \"microphone\") === \"microphone\" ? { mutedByHost: true } : {}),\n });\n return HANDLED;\n }\n default:\n return ignored(`a moderation kind this version does not know: ${String(kind)}`);\n }\n}\n\nconst ROLES: readonly ParticipantRole[] = [\"host\", \"cohost\", \"presenter\", \"participant\", \"viewer\"];\n","/**\n * DEVICE MANAGEMENT — the part users blame the product for.\n *\n * Every rule here exists because it is a support ticket somewhere:\n *\n * - **Labels are empty until permission is granted.** `enumerateDevices()`\n * happily returns three cameras with blank labels before the prompt. A picker\n * rendering \"Camera 1 / Camera 2\" is the classic symptom; we report\n * `labelsHidden` so the UI can say *why* instead.\n * - **A remembered device can vanish.** Unplug a headset between meetings and a\n * stored `deviceId` produces `OverconstrainedError`. The manager falls back to\n * the system default AND SAYS SO — a silent fallback is how someone joins on\n * the laptop mic while believing they are on their headset.\n * - **Selections persist per KIND, per device.** Injected storage (C10:\n * persistence-injected — this package's core purpose is not storage), with a\n * memory fallback that announces itself rather than pretending to be durable.\n * - **Enumeration is repeated on `devicechange`.** A picker that lists what was\n * plugged in at mount is wrong within seconds of a headset arriving.\n */\n\nimport { MeetError, normalizeDeviceError } from \"./errors\";\n\nexport type DeviceKind = \"audioinput\" | \"videoinput\" | \"audiooutput\";\n\nexport interface MeetDevice {\n readonly deviceId: string;\n readonly kind: DeviceKind;\n readonly label: string;\n}\n\nexport interface DeviceSnapshot {\n readonly devices: Readonly<Record<DeviceKind, readonly MeetDevice[]>>;\n readonly selected: Readonly<Record<DeviceKind, string | null>>;\n /** True when the browser is hiding labels because permission was not granted. */\n readonly labelsHidden: boolean;\n readonly permission: \"unknown\" | \"granted\" | \"denied\";\n /** Set when a remembered device was gone and we fell back. Shown, not swallowed. */\n readonly fallbackNotice: string | null;\n}\n\nexport interface DeviceStorage {\n read(): Record<string, string>;\n write(value: Record<string, string>): void;\n readonly durable: boolean;\n}\n\nexport interface MediaDevicesLike {\n enumerateDevices(): Promise<\n readonly { deviceId: string; kind: string; label: string }[]\n >;\n getUserMedia(constraints: { audio?: unknown; video?: unknown }): Promise<{\n getTracks(): readonly { stop(): void }[];\n }>;\n addEventListener?: ((type: \"devicechange\", handler: () => void) => void) | undefined;\n removeEventListener?: ((type: \"devicechange\", handler: () => void) => void) | undefined;\n}\n\nexport interface DeviceManagerOptions {\n media: MediaDevicesLike;\n storage?: DeviceStorage | undefined;\n onChange?: ((snapshot: DeviceSnapshot) => void) | undefined;\n}\n\nexport interface DeviceManager {\n /**\n * 🚨 THE SAME REFERENCE until something actually changes. This is a\n * `useSyncExternalStore` `getSnapshot` source (see `useDeviceSnapshot`), and\n * React compares snapshots by IDENTITY: a fresh object literal per call makes\n * every render report a change, which React reports as *\"The result of\n * getSnapshot should be cached\"* and then kills the tree with *\"Maximum\n * update depth exceeded\"*. `createMeetStore()` has always held this contract;\n * 0.2.0 shipped without it here and every room crashed at `<PreJoin>`.\n */\n snapshot(): DeviceSnapshot;\n /** Enumerate without prompting. Labels may be hidden; that is reported. */\n refresh(): Promise<DeviceSnapshot>;\n /** Prompt, then enumerate with real labels. The pre-join screen calls this. */\n requestPermission(kinds: { audio: boolean; video: boolean }): Promise<DeviceSnapshot>;\n select(kind: DeviceKind, deviceId: string): DeviceSnapshot;\n /** The id to actually use — the remembered one, or the default when it is gone. */\n resolve(kind: DeviceKind): string | null;\n subscribe(listener: (snapshot: DeviceSnapshot) => void): () => void;\n dispose(): void;\n}\n\nconst KINDS: readonly DeviceKind[] = [\"audioinput\", \"videoinput\", \"audiooutput\"];\n\nfunction isDeviceKind(value: string): value is DeviceKind {\n return value === \"audioinput\" || value === \"videoinput\" || value === \"audiooutput\";\n}\n\n/** localStorage when it exists; memory otherwise — and it says which. */\nexport function createWebDeviceStorage(key = \"mx.meet.devices\"): DeviceStorage {\n const store = (globalThis as { localStorage?: Storage }).localStorage;\n if (store === undefined) return createMemoryDeviceStorage();\n return {\n durable: true,\n read() {\n try {\n const raw = store.getItem(key);\n if (raw === null) return {};\n const parsed: unknown = JSON.parse(raw);\n return typeof parsed === \"object\" && parsed !== null\n ? (parsed as Record<string, string>)\n : {};\n } catch {\n return {};\n }\n },\n write(value) {\n try {\n store.setItem(key, JSON.stringify(value));\n } catch {\n // A full or blocked storage must not break joining a call.\n }\n },\n };\n}\n\nexport function createMemoryDeviceStorage(): DeviceStorage {\n let value: Record<string, string> = {};\n return {\n durable: false,\n read: () => value,\n write: (next) => {\n value = next;\n },\n };\n}\n\nexport function createDeviceManager(options: DeviceManagerOptions): DeviceManager {\n const storage = options.storage ?? createMemoryDeviceStorage();\n const listeners = new Set<(snapshot: DeviceSnapshot) => void>();\n const stored = storage.read();\n\n let devices: Record<DeviceKind, readonly MeetDevice[]> = {\n audioinput: [],\n videoinput: [],\n audiooutput: [],\n };\n let selected: Record<DeviceKind, string | null> = {\n audioinput: stored[\"audioinput\"] ?? null,\n videoinput: stored[\"videoinput\"] ?? null,\n audiooutput: stored[\"audiooutput\"] ?? null,\n };\n let labelsHidden = true;\n let permission: DeviceSnapshot[\"permission\"] = \"unknown\";\n let fallbackNotice: string | null = null;\n\n // 🚨 THE MEMOIZED SNAPSHOT. `current` IS the snapshot; readers get this exact\n // object until `commit()` replaces it, which happens only when one of the\n // five fields above actually moved. See the `DeviceManager.snapshot` doc\n // comment for the crash this prevents.\n let current: DeviceSnapshot = {\n devices,\n selected,\n labelsHidden,\n permission,\n fallbackNotice,\n };\n\n function snapshot(): DeviceSnapshot {\n return current;\n }\n\n /** Rebuild the snapshot from the current state. Call after ANY mutation. */\n function commit(): DeviceSnapshot {\n current = { devices, selected, labelsHidden, permission, fallbackNotice };\n return current;\n }\n\n function emit(): DeviceSnapshot {\n const next = commit();\n options.onChange?.(next);\n for (const listener of listeners) listener(next);\n return next;\n }\n\n async function enumerate(): Promise<DeviceSnapshot> {\n let raw: readonly { deviceId: string; kind: string; label: string }[];\n try {\n raw = await options.media.enumerateDevices();\n } catch (error) {\n throw normalizeDeviceError(error, \"meet.enumerateDevices\");\n }\n const grouped: Record<DeviceKind, MeetDevice[]> = {\n audioinput: [],\n videoinput: [],\n audiooutput: [],\n };\n let anyLabel = false;\n for (const device of raw) {\n if (!isDeviceKind(device.kind)) continue;\n if (device.label.length > 0) anyLabel = true;\n grouped[device.kind].push({\n deviceId: device.deviceId,\n kind: device.kind,\n label: device.label,\n });\n }\n devices = grouped;\n labelsHidden = !anyLabel && raw.length > 0;\n\n // THE VANISHED-DEVICE FALLBACK. A remembered id that is no longer present\n // is dropped to the system default, and the notice is part of the snapshot\n // so the UI can say \"your headset is unplugged — using the built-in mic\".\n const lost: string[] = [];\n for (const kind of KINDS) {\n const current = selected[kind];\n if (current === null) continue;\n if (!grouped[kind].some((device) => device.deviceId === current)) {\n selected = { ...selected, [kind]: null };\n lost.push(kind);\n }\n }\n fallbackNotice =\n lost.length === 0\n ? null\n : `A remembered ${lost.join(\" and \")} device is no longer connected — using the ` +\n \"system default. Pick another in the device menu.\";\n return emit();\n }\n\n const onDeviceChange = (): void => {\n void enumerate();\n };\n options.media.addEventListener?.(\"devicechange\", onDeviceChange);\n\n return {\n snapshot,\n refresh: enumerate,\n async requestPermission(kinds) {\n if (!kinds.audio && !kinds.video) {\n throw new MeetError(\n \"misconfigured\",\n \"meet.requestPermission: asked for neither audio nor video\",\n \"Request at least one kind. Prompting for nothing silently resolves and leaves \" +\n \"labels hidden, which looks like a broken picker.\",\n );\n }\n try {\n const stream = await options.media.getUserMedia({\n ...(kinds.audio ? { audio: true } : {}),\n ...(kinds.video ? { video: true } : {}),\n });\n // 🚨 STOP THE PROBE STREAM. Holding it keeps the camera light on and,\n // on Windows, makes the real join fail with NotReadableError — the\n // device is busy with our own probe.\n for (const track of stream.getTracks()) track.stop();\n permission = \"granted\";\n } catch (error) {\n permission = \"denied\";\n emit();\n throw normalizeDeviceError(error, \"meet.requestPermission\");\n }\n return enumerate();\n },\n select(kind, deviceId) {\n selected = { ...selected, [kind]: deviceId };\n fallbackNotice = null;\n storage.write({\n ...storage.read(),\n [kind]: deviceId,\n });\n return emit();\n },\n resolve(kind) {\n const current = selected[kind];\n if (current !== null && devices[kind].some((device) => device.deviceId === current)) {\n return current;\n }\n return devices[kind][0]?.deviceId ?? null;\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n dispose() {\n options.media.removeEventListener?.(\"devicechange\", onDeviceChange);\n listeners.clear();\n },\n };\n}\n","/**\n * PRESENTATION HELPERS specific to a CALL. Pure, tested, and shared by every\n * surface — so a roster line reads the same in the meeting, in the history\n * list, and in the notification.\n *\n * The GENERIC formatters this file used to define — `formatDuration`,\n * `getInitials`, `avatarPaletteIndex` — moved to `@ai-matrx/kit/format` on\n * 2026-09-07 (duplication census H1). They were byte-for-byte duplicated in\n * `@ai-matrx/messaging`, and because two packages claimed them, ~65 host\n * twins across six repos had no correct owner to point at: no surface should\n * take a dependency on the video-calling package to print a duration. Nothing\n * is re-exported from here — import `@ai-matrx/kit/format` directly.\n */\n\nimport type { CallState, MeetParticipant } from \"./types\";\n\n/** \"Ana, Ben and 3 others\" — the roster line, never a raw array join. */\nexport function participantSummary(participants: readonly MeetParticipant[]): string {\n const names = participants.filter((p) => !p.isLocal).map((p) => p.displayName);\n if (names.length === 0) return \"Just you\";\n if (names.length === 1) return names[0] ?? \"\";\n if (names.length === 2) return `${names[0]} and ${names[1]}`;\n return `${names[0]}, ${names[1]} and ${names.length - 2} ${names.length - 2 === 1 ? \"other\" : \"others\"}`;\n}\n\n/** What a settled call says in history. One sentence per terminal state. */\nexport function callStateLabel(state: CallState, outgoing: boolean): string {\n switch (state) {\n case \"dialing\":\n case \"ringing\":\n return outgoing ? \"Calling…\" : \"Incoming call\";\n case \"connecting\":\n return \"Connecting…\";\n case \"active\":\n return \"In call\";\n case \"declined\":\n return outgoing ? \"Declined\" : \"You declined\";\n case \"missed\":\n return outgoing ? \"No answer\" : \"Missed call\";\n case \"busy\":\n return outgoing ? \"On another call\" : \"You were busy\";\n case \"cancelled\":\n return outgoing ? \"Cancelled\" : \"Cancelled by caller\";\n case \"ended\":\n return \"Call ended\";\n case \"failed\":\n return \"Call failed\";\n default:\n return \"Call\";\n }\n}\n\n/** \"2 waiting\" for the lobby badge — plural handled once, not per surface. */\nexport function countLabel(count: number, singular: string, plural = `${singular}s`): string {\n return `${count} ${count === 1 ? singular : plural}`;\n}\n","/**\n * THE MESSAGING SEAM — a call invitation is an ACTIONABLE MESSAGE (D8).\n *\n * `@ai-matrx/messaging` deliberately knows nothing about meetings; its action\n * registry exists so packages like this one can add a kind without messaging\n * learning about them. This file is that registration, and it is the whole\n * coupling between the two packages.\n *\n * Why an invitation is a message and not only a ring: a ring is EPHEMERAL. If\n * you were away from your desk, a broadcast that expired is gone, and \"someone\n * tried to reach me\" is exactly the information you most needed. The message is\n * the durable half — it is still in the thread tomorrow, with the outcome\n * stamped on it.\n *\n * 🚨 **THE PAYLOAD IS NOT AUTHORIZATION.** The chip's payload says what was\n * ASKED, never what is ALLOWED. The handler re-resolves the invite through the\n * repository (an auth-checked RPC) before it joins anything. Trusting the\n * payload would turn any message anyone can send into a room grant.\n *\n * Idempotence is the registry's, not ours: two tabs and a double tap settle\n * ONCE and share one receipt.\n */\n\nimport type { ActionHandler, ActionReceipt } from \"@ai-matrx/messaging\";\nimport type { CallCenter } from \"./calls\";\nimport { MeetError } from \"./errors\";\nimport type { MeetRepository } from \"./repository\";\nimport { asCallId, asUserId, type CallInvite, type CallMode } from \"./types\";\n\nexport const CALL_INVITE_KIND = \"meet.call-invite\";\nexport const CALL_INVITE_VERSION = 1;\n\nexport interface CallInvitePayload {\n readonly invite_id: string;\n readonly room_name: string;\n readonly mode: CallMode;\n readonly caller_name: string;\n readonly expires_at: string;\n}\n\n/** The `action_data` a caller attaches to the invitation message. */\nexport function callInviteAction(invite: CallInvite): {\n readonly kind: string;\n readonly version: number;\n readonly payload: CallInvitePayload;\n} {\n return {\n kind: CALL_INVITE_KIND,\n version: CALL_INVITE_VERSION,\n payload: {\n invite_id: invite.id,\n room_name: invite.roomName,\n mode: invite.mode,\n caller_name: invite.callerName,\n expires_at: invite.expiresAt,\n },\n };\n}\n\n/** The message body. Readable on a surface that cannot render the chips. */\nexport function callInviteText(invite: CallInvite): string {\n return invite.mode === \"video\"\n ? `${invite.callerName} started a video call`\n : `${invite.callerName} started an audio call`;\n}\n\nexport interface CallInviteHandlerOptions {\n calls: CallCenter;\n repository: MeetRepository;\n /** Called after a successful accept, with the room to enter. */\n onJoin: (invite: CallInvite) => void | Promise<void>;\n now?: (() => number) | undefined;\n}\n\nexport function createCallInviteHandler(\n options: CallInviteHandlerOptions,\n): ActionHandler<CallInvitePayload> {\n const now = options.now ?? (() => Date.now());\n\n return {\n kind: CALL_INVITE_KIND,\n versions: [CALL_INVITE_VERSION],\n choices: (payload) => {\n // An EXPIRED invite renders no chips. Offering \"Join\" for a call that\n // ended twenty minutes ago is a dead end with a friendly face.\n if (Date.parse(payload.expires_at) < now()) return [];\n return [\n { id: \"join\", label: payload.mode === \"video\" ? \"Join video\" : \"Join call\", icon: payload.mode === \"video\" ? \"video\" : \"phone\", tone: \"primary\" },\n { id: \"decline\", label: \"Decline\", tone: \"neutral\" },\n ];\n },\n summarize: (payload) =>\n payload.mode === \"video\"\n ? `${payload.caller_name} started a video call`\n : `${payload.caller_name} started an audio call`,\n execute: async (payload, context): Promise<ActionReceipt> => {\n const settledAt = new Date(now()).toISOString();\n const inviteId = asCallId(payload.invite_id);\n\n // RE-RESOLVE. The payload told us what to look for; the database tells us\n // whether it is still true and whether this actor may act on it.\n // `context.actorId` is messaging's branded UserId; ours is a distinct\n // brand over the same string. Re-branding through the constructor is the\n // honest crossing — a cast between two brands would compile today and\n // hide a genuine mismatch the day either package changes its identity.\n const pending = await options.repository.pendingInvites(asUserId(String(context.actorId)));\n const live = pending.find((invite) => invite.id === inviteId) ?? null;\n\n if (live === null) {\n return {\n kind: CALL_INVITE_KIND,\n messageId: context.messageId,\n actorId: context.actorId,\n outcome: \"unavailable\",\n label: \"This call has ended\",\n settledAt,\n detail:\n \"The invite is no longer ringing — it was answered elsewhere, cancelled, or it \" +\n \"timed out. The chips are replaced by the outcome rather than left clickable.\",\n };\n }\n\n if (context.choice === \"decline\") {\n await options.calls.decline(inviteId);\n return {\n kind: CALL_INVITE_KIND,\n messageId: context.messageId,\n actorId: context.actorId,\n outcome: \"declined\",\n label: \"Declined\",\n settledAt,\n };\n }\n\n const accepted = await options.calls.accept(inviteId, live.mode);\n await options.onJoin(accepted);\n return {\n kind: CALL_INVITE_KIND,\n messageId: context.messageId,\n actorId: context.actorId,\n outcome: \"applied\",\n label: \"Joined\",\n settledAt,\n };\n },\n };\n}\n\n/**\n * A meeting invitation — the scheduled twin. Deliberately NOT the same kind: a\n * scheduled meeting's chip must never say \"Join call\" three days early, and a\n * receiver that only understands calls must render NOTHING for it rather than a\n * button it cannot honor.\n */\nexport const MEETING_INVITE_KIND = \"meet.meeting-invite\";\nexport const MEETING_INVITE_VERSION = 1;\n\nexport interface MeetingInvitePayload {\n readonly meeting_id: string;\n readonly slug: string;\n readonly title: string;\n readonly scheduled_for: string | null;\n readonly link: string;\n}\n\nexport function meetingInviteAction(payload: MeetingInvitePayload): {\n readonly kind: string;\n readonly version: number;\n readonly payload: MeetingInvitePayload;\n} {\n return { kind: MEETING_INVITE_KIND, version: MEETING_INVITE_VERSION, payload };\n}\n\nexport interface MeetingInviteHandlerOptions {\n onOpen: (payload: MeetingInvitePayload) => void | Promise<void>;\n now?: (() => number) | undefined;\n}\n\nexport function createMeetingInviteHandler(\n options: MeetingInviteHandlerOptions,\n): ActionHandler<MeetingInvitePayload> {\n const now = options.now ?? (() => Date.now());\n return {\n kind: MEETING_INVITE_KIND,\n versions: [MEETING_INVITE_VERSION],\n // A DURABLE LINK NEVER EXPIRES (R3) — the chip stays useful after the\n // meeting too, because that is when people go looking for the notes.\n choices: () => [{ id: \"open\", label: \"Open meeting\", tone: \"primary\" }],\n summarize: (payload) =>\n payload.scheduled_for === null\n ? payload.title\n : `${payload.title} — ${payload.scheduled_for}`,\n execute: async (payload, context): Promise<ActionReceipt> => {\n if (payload.slug.length === 0) {\n throw new MeetError(\n \"invalid-response\",\n \"meet.meeting-invite: the payload carries no slug\",\n \"A meeting invitation without a durable slug cannot be opened. Fix the sender; \" +\n \"never synthesize a link on the receiving side.\",\n );\n }\n await options.onOpen(payload);\n return {\n kind: MEETING_INVITE_KIND,\n messageId: context.messageId,\n actorId: context.actorId,\n outcome: \"applied\",\n label: \"Opened\",\n settledAt: new Date(now()).toISOString(),\n };\n },\n };\n}\n","/**\n * THE STAGE — who is big, who is small, and who is not rendered at all.\n *\n * This is pure arithmetic on purpose: layout is the part of a meeting UI that\n * everyone re-derives inside a component and gets subtly wrong, and it is\n * trivially testable once it stops living in JSX.\n *\n * R5 SCALE HONESTY lives here. A 200-person meeting does not render 200 video\n * tiles — every browser dies, and LiveKit's adaptive stream cannot save a\n * layout that asked for 200 subscriptions. The grid PAGES, and the ordering is\n * deliberate rather than \"whatever the roster map iterated\":\n *\n * 1. a screen share (or an explicit pin) owns the stage;\n * 2. then the active speakers, most recent first;\n * 3. then anyone with a hand raised — they are asking to be seen;\n * 4. then cameras on before cameras off (an avatar tile is cheap);\n * 5. then stable by join time, so tiles do not shuffle every time someone\n * breathes. Shuffling tiles is the single most disorienting thing a\n * meeting UI can do.\n */\n\nimport { isObserverRole, type MeetParticipant, type ParticipantIdentity, type StageLayout } from \"./types\";\n\nexport interface StageArgs {\n readonly participants: readonly MeetParticipant[];\n readonly layout: StageLayout;\n readonly pinned: ParticipantIdentity | null;\n readonly activeSpeakers: readonly ParticipantIdentity[];\n /**\n * The participant a HOST spotlighted for everyone (Meet wave 2). A local pin\n * outranks it on this screen; a screen share outranks both.\n */\n readonly spotlight?: ParticipantIdentity | null | undefined;\n /** How many tiles this viewport can honestly render. Default 12. */\n readonly pageSize?: number;\n readonly page?: number;\n}\n\nexport interface Stage {\n /** The large tile, when the layout has one. */\n readonly feature: MeetParticipant | null;\n /** What actually renders as a tile, already paged. */\n readonly tiles: readonly MeetParticipant[];\n /** Everyone not on this page. Rendered as \"+N others\", never dropped silently. */\n readonly overflow: number;\n readonly page: number;\n readonly pageCount: number;\n /** True when a screen share, not a person, is the reason for the big tile. */\n readonly presenting: boolean;\n}\n\nexport const DEFAULT_PAGE_SIZE = 12;\n\nexport function orderParticipants(\n participants: readonly MeetParticipant[],\n activeSpeakers: readonly ParticipantIdentity[],\n): readonly MeetParticipant[] {\n const speakerRank = new Map<ParticipantIdentity, number>();\n activeSpeakers.forEach((identity, index) => speakerRank.set(identity, index));\n\n return [...participants].sort((a, b) => {\n const speakerA = speakerRank.get(a.identity) ?? Number.MAX_SAFE_INTEGER;\n const speakerB = speakerRank.get(b.identity) ?? Number.MAX_SAFE_INTEGER;\n if (speakerA !== speakerB) return speakerA - speakerB;\n if (a.handRaised !== b.handRaised) return a.handRaised ? -1 : 1;\n if (a.cameraEnabled !== b.cameraEnabled) return a.cameraEnabled ? -1 : 1;\n // STABLE from here down. Join time, then identity — never Math.random,\n // never map order, both of which reshuffle the grid on every event.\n if (a.joinedAt !== b.joinedAt) return a.joinedAt < b.joinedAt ? -1 : 1;\n return a.identity < b.identity ? -1 : 1;\n });\n}\n\nexport function computeStage(args: StageArgs): Stage {\n const pageSize = Math.max(1, args.pageSize ?? DEFAULT_PAGE_SIZE);\n // A SILENT OBSERVER (MD-16) watches; it is never a tile — not on anyone's stage, its own included.\n const ordered = orderParticipants(\n args.participants.filter((participant) => !isObserverRole(participant.role)),\n args.activeSpeakers,\n );\n\n const sharer = ordered.find((participant) => participant.screenSharing) ?? null;\n const pinnedParticipant =\n args.pinned === null\n ? null\n : ordered.find((participant) => participant.identity === args.pinned) ?? null;\n\n // A SCREEN SHARE OUTRANKS A PIN, deliberately: someone presenting is the\n // reason everyone is looking, and a stale pin hiding a live demo is the\n // complaint this rule exists to prevent.\n const spotlit =\n args.spotlight == null\n ? null\n : ordered.find((participant) => participant.identity === args.spotlight) ?? null;\n\n const feature =\n sharer !== null\n ? sharer\n : pinnedParticipant !== null && args.layout !== \"grid\"\n ? pinnedParticipant\n : spotlit !== null\n ? spotlit\n : args.layout === \"grid\"\n ? null\n : ordered[0] ?? null;\n\n const rest =\n feature === null\n ? ordered\n : ordered.filter((participant) => participant.identity !== feature.identity);\n\n const pageCount = Math.max(1, Math.ceil(rest.length / pageSize));\n const page = Math.min(Math.max(0, args.page ?? 0), pageCount - 1);\n const tiles = rest.slice(page * pageSize, page * pageSize + pageSize);\n\n return {\n feature,\n tiles,\n overflow: rest.length - tiles.length,\n page,\n pageCount,\n presenting: sharer !== null,\n };\n}\n\n/** Column count for a grid of N tiles — the shape that keeps tiles squarest. */\nexport function gridColumns(count: number): number {\n if (count <= 1) return 1;\n if (count <= 4) return 2;\n if (count <= 9) return 3;\n if (count <= 16) return 4;\n return 5;\n}\n","/**\n * CG — WHAT THIS BROWSER CAN DO, probed before anything else (CORE-DESIGN §11 `browser-unsupported`,\n * `share-unsupported-mobile`). Pure over an injected scope so the rules are the tests; the web\n * reading is `detectCapabilities()` with no argument. Never touches LiveKit.\n */\n\nexport type UnsupportedReason = \"no_webrtc\";\n\nexport interface MeetCapabilities {\n /** False only when a call cannot run here at all (no WebRTC: an in-app webview, an old browser). */\n readonly canJoin: boolean;\n readonly reason: UnsupportedReason | null;\n /** The in-app browser this looks like (Instagram, Facebook, LinkedIn…), or null. */\n readonly inAppBrowser: string | null;\n /** A phone or tablet: no screen sharing from the browser there. */\n readonly mobile: boolean;\n /** `getDisplayMedia` exists AND this is not a phone/tablet. */\n readonly canShareScreen: boolean;\n}\n\ninterface ScopeLike {\n readonly RTCPeerConnection?: unknown;\n readonly webkitRTCPeerConnection?: unknown;\n readonly navigator?: {\n readonly userAgent?: string;\n readonly maxTouchPoints?: number;\n readonly mediaDevices?: { readonly getDisplayMedia?: unknown };\n };\n}\n\nconst IN_APP: readonly [RegExp, string][] = [\n [/Instagram/i, \"Instagram\"],\n [/FBAN|FBAV|FB_IAB/i, \"Facebook\"],\n [/LinkedInApp/i, \"LinkedIn\"],\n [/MicroMessenger/i, \"WeChat\"],\n [/Snapchat/i, \"Snapchat\"],\n [/\\bLine\\//i, \"LINE\"],\n [/Twitter|TwitterAndroid/i, \"X\"],\n [/TikTok|musical_ly|BytedanceWebview/i, \"TikTok\"],\n];\n\nexport function isMobileUserAgent(userAgent: string, maxTouchPoints = 0): boolean {\n if (/iPhone|iPod|Android|Mobile|iPad/i.test(userAgent)) return true;\n // iPadOS reports a Mac user agent; touch points give it away.\n return /Macintosh/.test(userAgent) && maxTouchPoints > 1;\n}\n\nexport function detectCapabilities(scope: ScopeLike | null = typeof globalThis === \"undefined\" ? null : (globalThis as ScopeLike)): MeetCapabilities {\n // Outside a browser (a server render, a test with no scope) nothing is known to be missing.\n if (scope === null || scope.navigator === undefined) {\n return { canJoin: true, reason: null, inAppBrowser: null, mobile: false, canShareScreen: false };\n }\n const ua = scope.navigator.userAgent ?? \"\";\n const webrtc = typeof scope.RTCPeerConnection === \"function\" || typeof scope.webkitRTCPeerConnection === \"function\";\n const mobile = isMobileUserAgent(ua, scope.navigator.maxTouchPoints ?? 0);\n const inAppBrowser = IN_APP.find(([pattern]) => pattern.test(ua))?.[1] ?? null;\n return {\n canJoin: webrtc,\n reason: webrtc ? null : \"no_webrtc\",\n inAppBrowser,\n mobile,\n canShareScreen: !mobile && typeof scope.navigator.mediaDevices?.getDisplayMedia === \"function\",\n };\n}\n\n/** The words for a browser that cannot join: what to open instead. Short lines (layout, not prose). */\nexport function unsupportedWords(capabilities: MeetCapabilities): { readonly title: string; readonly body: string } {\n if (capabilities.inAppBrowser !== null) {\n return {\n title: `${capabilities.inAppBrowser}'s browser can't join calls`,\n body: \"Open this link in Chrome, Safari, Edge or Firefox\",\n };\n }\n return { title: \"Meet is not compatible with your browser yet\", body: \"Open this link in Chrome, Safari, Edge or Firefox\" };\n}\n","/**\n * THE MEDIA SEAM — the ONE interface the room engine talks to.\n *\n * D1/D2: LiveKit Cloud is the media infrastructure, hardcoded. This interface\n * is NOT a provider abstraction and is not here so a second SFU can be swapped\n * in — it exists for two reasons that are both about correctness:\n *\n * 1. **The engine must be provable without a browser.** Everything expensive in\n * a calling product (ring timeouts, reconnection, admission, escalation,\n * speaker tracking) is logic, and logic proven only through a real SFU is\n * logic that is never proven. `testing/fake-driver.ts` drives every one of\n * those paths in a node test.\n * 2. **The SDK stays out of the initial chunk.** `livekit-client` is reached\n * only through a dynamic `import()` inside the default driver, so importing\n * this package during SSR — or on a page with a call BUTTON but no call —\n * costs nothing and touches no browser API.\n *\n * The default implementation ships (`createLiveKitDriver`) and is what\n * `<MeetProvider>` uses when a host injects nothing (THE ALL-INCLUSIVE LAW).\n */\n\nimport type { NetworkReading } from \"./network-health\";\nimport type { ConnectionQuality } from \"./types\";\n\n/** A participant as the media layer sees them, before roles or profiles. */\nexport interface DriverParticipant {\n readonly identity: string;\n readonly name: string;\n /** Opaque JSON string the token minted for this participant. */\n readonly metadata: string | null;\n /**\n * The participant's LiveKit attributes. Unlike `metadata` these are set by\n * the participant itself at run time, which is how the note-taker announces\n * that a consent notice is owed (D10).\n */\n readonly attributes: Readonly<Record<string, string>>;\n readonly isLocal: boolean;\n readonly micEnabled: boolean;\n readonly cameraEnabled: boolean;\n readonly screenSharing: boolean;\n}\n\nexport type DriverConnectionState =\n | \"connecting\"\n | \"connected\"\n /** Only the signal connection is being re-established; media may still flow. */\n | \"signal_reconnecting\"\n | \"reconnecting\"\n | \"disconnected\";\n\nexport type DriverTrackKind = \"camera\" | \"microphone\" | \"screen\";\n\n/**\n * ONE LIVE CAPTION off the `lk.transcription` text stream.\n *\n * `speakerIdentity` is `lk.transcribed_participant_identity` — WHO IS BEING\n * TRANSCRIBED, not who published the stream (the note-taker publishes every\n * one of them). The SFU-reported publisher identity below is independently\n * checked against the meeting's note-taker; captions remain hints and durable\n * segments remain the record (D16).\n */\nexport interface DriverTranscription {\n /** `lk.segment_id`. Interim and final share it; that is what makes replacement possible. */\n readonly segmentId: string;\n readonly speakerIdentity: string;\n /**\n * The LiveKit participant that opened this text stream. Unlike\n * `speakerIdentity`, this comes from the SFU and is the authority for who\n * published a caption.\n */\n readonly publisherIdentity: string;\n readonly text: string;\n /** `lk.transcription_final`. */\n readonly final: boolean;\n /** `lk.transcribed_track_id`, when the sender supplied it. */\n readonly trackId: string | null;\n readonly at: string;\n}\n\nexport interface RoomDriverHandlers {\n /** The FULL roster, every time. A delta protocol here drifts; a snapshot cannot. */\n participants(participants: readonly DriverParticipant[]): void;\n activeSpeakers(identities: readonly string[]): void;\n quality(identity: string, quality: ConnectionQuality): void;\n connectionState(state: DriverConnectionState): void;\n /** In-room data messages: hand raise, reactions, in-room chat, AI events. */\n data(topic: string, payload: string, fromIdentity: string | null): void;\n /** A live caption. Interim and final both arrive here (D16). */\n transcription(event: DriverTranscription): void;\n /**\n * A remote participant left the room, with LiveKit's reason by NAME (`CLIENT_INITIATED`, …) or\n * null when none was given. Fired BEFORE the roster update that drops them, so an unannounced\n * departure can be held as a `reconnecting` tile (S6b). Optional for older host drivers.\n */\n participantLeft?(identity: string, reason: string | null): void;\n /** The room ended for this client. `reason` is shown to the user verbatim. */\n disconnected(reason: string | null): void;\n /**\n * The ROOM's metadata — set only by the server (`UpdateRoomMetadata`), and\n * delivered to every joiner ON JOIN, which is why the spotlight rides it: a\n * data packet reaches only the people already in the room. Called once after\n * connect and on every change. Optional so an older host driver still works.\n */\n roomMetadata?(metadata: string): void;\n /**\n * S6c — MEDIA. Can remote audio play (`false` = the browser's autoplay policy is holding it\n * until a gesture)? Fired on every change and once right after connect. Optional for older drivers.\n */\n audioPlayback?(canPlay: boolean): void;\n /** S6c — the device list changed (plugged / unplugged), AFTER the SDK re-picked defaults. */\n devicesChanged?(): void;\n /** S6c — the SDK could not acquire or keep a local device. `kind` when the SDK knows it. */\n mediaDeviceError?(error: unknown, kind: \"audioinput\" | \"videoinput\" | null): void;\n /**\n * P1 connection-network — one sample of THIS tab's WebRTC path (round trip, loss, relay/TCP),\n * read from the peer connections' stats every couple of seconds while connected. The rules\n * that turn samples into what people see are `network-health.ts`. Optional for older drivers.\n */\n networkReading?(reading: NetworkReading): void;\n}\n\n/** A camera background effect. `image` replaces the background with `imageUrl`. */\nexport type VideoEffect =\n | { readonly kind: \"none\" }\n | { readonly kind: \"blur\"; readonly radius?: number }\n | { readonly kind: \"image\"; readonly imageUrl: string };\n\n/** Microphone processing the browser applies before anything is sent. */\nexport interface AudioProcessing {\n readonly noiseSuppression: boolean;\n readonly echoCancellation: boolean;\n readonly autoGainControl: boolean;\n}\n\nexport interface MeetRoomDriver {\n connect(serverUrl: string, token: string): Promise<void>;\n disconnect(): Promise<void>;\n setMicrophoneEnabled(enabled: boolean): Promise<void>;\n setCameraEnabled(enabled: boolean): Promise<void>;\n setScreenShareEnabled(enabled: boolean): Promise<void>;\n /**\n * Reliable, topic-addressed data. The in-room control plane. `to` addresses\n * named identities only (a private chat message); absent = everyone.\n */\n publishData(topic: string, payload: string, to?: readonly string[]): Promise<void>;\n /**\n * Apply microphone processing to the live track and to every future one.\n * Optional: a driver without it makes the control ABSENT, never dead.\n */\n setAudioProcessing?(processing: AudioProcessing): Promise<void>;\n /**\n * Apply a background effect to the live camera and to every future one.\n * Rejects with a sentence when this browser cannot run it.\n */\n setVideoEffect?(effect: VideoEffect): Promise<void>;\n /** Whether this browser can run background effects at all. */\n supportsVideoEffects?(): boolean;\n /** Whether this browser can share a screen at all (iOS cannot). Absent = assume yes. */\n supportsScreenShare?(): boolean;\n /** The screen share being published carries sound (`share-audio`). */\n screenShareHasAudio?(): boolean;\n /**\n * Attach a participant's track to a media element. Returns the detach\n * function — returning it (rather than exposing `detach`) is what makes a\n * leaked attachment impossible from the React side: the effect's cleanup IS\n * the return value.\n */\n attach(identity: string, kind: DriverTrackKind, element: HTMLMediaElement): () => void;\n /** The device the driver actually selected, so the UI never lies about it. */\n activeDevices(): Readonly<Record<\"audioinput\" | \"videoinput\" | \"audiooutput\", string | null>>;\n switchDevice(kind: \"audioinput\" | \"videoinput\" | \"audiooutput\", deviceId: string): Promise<void>;\n /**\n * S6c — resume remote audio the autoplay policy blocked. MUST run inside the person's click\n * (the gesture is what the browser checks). Optional: a driver without it never blocks.\n */\n startAudio?(): Promise<void>;\n /**\n * S7 — a \"join here too\" companion plays no remote audio (the person's other device does; two\n * speakers in one room echo). `false` removes every attached remote audio element and keeps new\n * ones from attaching. Optional: a driver without it always plays.\n */\n setRemoteAudioPlayback?(enabled: boolean): void;\n}\n\nexport type RoomDriverFactory = (\n handlers: RoomDriverHandlers,\n) => MeetRoomDriver | Promise<MeetRoomDriver>;\n\n/** Topics for the in-room control plane. One place, so senders cannot drift. */\nexport const DATA_TOPICS = {\n reaction: \"mx.reaction\",\n chat: \"mx.chat\",\n} as const;\n\n/**\n * 🚨 `mx.ai.note` and `mx.ai.transcript` WERE declared here and emitted by\n * nothing. They are gone (MRI-A12): the platform's own events are published by\n * the SERVER under `matrx.meet.*` — see `core/room-events.ts` — because a packet\n * a participant could publish is a packet a guest could forge, and these carry\n * durable rows. A participant topic for the same thing would be a second,\n * unauthenticated way to say it.\n */\n\nexport type DataTopic = (typeof DATA_TOPICS)[keyof typeof DATA_TOPICS];\n\n/**\n * THE LIVEKIT TRANSCRIPTION CONTRACT — not ours, so it is written down once\n * rather than spelled inline. The topic and these four attribute names are the\n * SDK's own (`lk.*`); the worker publishes to exactly them (MRI-A2) and a\n * rename on either side must break here, loudly, in one place.\n */\nexport const TRANSCRIPTION_TOPIC = \"lk.transcription\";\n\nexport const TRANSCRIPTION_ATTRIBUTES = {\n segmentId: \"lk.segment_id\",\n trackId: \"lk.transcribed_track_id\",\n final: \"lk.transcription_final\",\n /**\n * WHO IS BEING TRANSCRIBED. The LiveKit JS client reports the PUBLISHER of a\n * text stream, and the publisher is always the note-taker — so a caption\n * carries the speaker in this attribute and falls back to the publisher only\n * when it is absent (which attributes the line to the agent, visibly, rather\n * than to the wrong person, silently).\n */\n speakerIdentity: \"lk.transcribed_participant_identity\",\n} as const;\n","/**\n * NETWORK HEALTH (CORE-DESIGN §3.3 CS, P1 connection-network) — the pure rules behind what each\n * person sees when a network degrades. No socket, no timer, no browser global: the driver hands\n * in readings, the engine hands in the clock, and every rule here is provable in a node test.\n *\n * Why the core judges quality itself and does not only repeat LiveKit's `ConnectionQuality`:\n * LiveKit scores a TCP/TURN path with 800 ms of round trip as \"good\" (no packets are lost on\n * TCP — they are just late), and it keeps a participant whose cable was pulled \"good\" for 25 s\n * and more. Google Meet documents media quality degrading at RTT ≥ 300 ms; that is the bar here.\n *\n * Four rules:\n * 1. **Self-assessment** (`createSelfAssessor`): this tab's own path, from its WebRTC stats —\n * round trip, loss — with hysteresis so the reading does not flap.\n * 2. **The path** (`pathOf`): media relayed over TURN or carried over TCP is a restricted network\n * (`net-udp-blocked`): the call works, quality may be reduced.\n * 3. **Liveness** (`createLivenessTracker`): every client says \"alive\" on a light data heartbeat\n * that carries its own reading and whether its video is paused. A person who goes silent past\n * `staleAfterMs` is shown `reconnecting` to everyone else (`net-others-see-drop`) — long before\n * the media server gives up on them. A client that never sent a heartbeat (an agent, an older\n * client) is never judged by it.\n * 4. **Video auto-off** (`createVideoAutoOff`): a poor reading that lasts\n * `video_auto_off_after_poor_seconds` pauses this person's camera so their audio survives\n * (`net-video-auto-off`); a person who turns it back on is not paused again until the path has\n * recovered once.\n */\n\nimport type { ConnectionQuality } from \"./types\";\n\n/* ------------------------------------------------------------------ readings */\n\n/** One sample of this tab's WebRTC path, as the driver read it from `getStats()`. */\nexport interface NetworkReading {\n /** Selected candidate pair's current round trip (ms), or null when not known. */\n readonly rttMs: number | null;\n /** Fraction of packets lost since the previous sample (0..1), or null. */\n readonly lossRatio: number | null;\n /** The selected local candidate is a TURN relay. */\n readonly relayed: boolean | null;\n /** The selected local candidate carries media over TCP. */\n readonly tcp: boolean | null;\n}\n\nexport type NetworkPath = \"direct\" | \"restricted\" | \"unknown\";\n\nexport function pathOf(reading: NetworkReading): NetworkPath {\n if (reading.relayed === null && reading.tcp === null) return \"unknown\";\n return reading.relayed === true || reading.tcp === true ? \"restricted\" : \"direct\";\n}\n\n/** Google Meet's documented bar: media quality degrades at RTT ≥ 300 ms. */\nexport const POOR_RTT_MS = 300;\n/** Recovery needs clear daylight under the bar, so the reading does not flap at 299/301. */\nexport const RECOVERED_RTT_MS = 220;\nexport const POOR_LOSS = 0.08;\nexport const RECOVERED_LOSS = 0.03;\n\nexport type SelfQuality = \"good\" | \"poor\" | \"unknown\";\n\nexport interface SelfAssessor {\n /** Fold in one sample; returns the reading after it. */\n push(reading: NetworkReading): SelfQuality;\n current(): SelfQuality;\n reset(): void;\n}\n\n/**\n * Poor after `poorSamples` consecutive bad samples; good again after `goodSamples` consecutive\n * clearly-good ones. A sample with nothing measured changes nothing.\n */\nexport function createSelfAssessor(options: { poorSamples?: number; goodSamples?: number } = {}): SelfAssessor {\n const poorSamples = options.poorSamples ?? 2;\n const goodSamples = options.goodSamples ?? 3;\n let quality: SelfQuality = \"unknown\";\n let badRun = 0;\n let goodRun = 0;\n return {\n push(reading) {\n const { rttMs, lossRatio } = reading;\n if (rttMs === null && lossRatio === null) return quality;\n const bad = (rttMs !== null && rttMs >= POOR_RTT_MS) || (lossRatio !== null && lossRatio >= POOR_LOSS);\n const clear =\n (rttMs === null || rttMs < RECOVERED_RTT_MS) && (lossRatio === null || lossRatio < RECOVERED_LOSS);\n badRun = bad ? badRun + 1 : 0;\n goodRun = clear ? goodRun + 1 : 0;\n if (badRun >= poorSamples) quality = \"poor\";\n else if (quality !== \"poor\" && !bad) quality = \"good\";\n else if (quality === \"poor\" && goodRun >= goodSamples) quality = \"good\";\n return quality;\n },\n current: () => quality,\n reset() {\n quality = \"unknown\";\n badRun = 0;\n goodRun = 0;\n },\n };\n}\n\n/* ------------------------------------------------------------------ combining */\n\nconst RANK: Record<ConnectionQuality, number> = {\n lost: 0,\n poor: 1,\n good: 2,\n excellent: 3,\n unknown: 4,\n};\n\n/** The worse of two readings; `unknown` never wins over a real one. */\nexport function worstQuality(a: ConnectionQuality, b: ConnectionQuality | SelfQuality | null | undefined): ConnectionQuality {\n if (b === null || b === undefined || b === \"unknown\") return a;\n if (a === \"unknown\") return b;\n return RANK[b] < RANK[a] ? b : a;\n}\n\n/* ------------------------------------------------------------------ heartbeat */\n\n/** The participant data topic the heartbeat rides (lossy-tolerant, tiny). */\nexport const ALIVE_TOPIC = \"mx.alive\";\n\nexport interface AliveBeat {\n /** The sender's own path reading. */\n readonly q: SelfQuality;\n /** The sender's camera is paused by the network (auto-off), not by their choice. */\n readonly vp: boolean;\n}\n\nexport function encodeBeat(beat: AliveBeat): string {\n return JSON.stringify({ q: beat.q, vp: beat.vp });\n}\n\nexport function decodeBeat(payload: string): AliveBeat | null {\n try {\n const raw = JSON.parse(payload) as { q?: unknown; vp?: unknown };\n const q = raw.q === \"good\" || raw.q === \"poor\" ? raw.q : \"unknown\";\n return { q, vp: raw.vp === true };\n } catch {\n return null;\n }\n}\n\n/**\n * How often to beat. 2 s up to 25 people, then slower in proportion, so a 500-person room costs\n * every client about the same packets per second as a 25-person one.\n */\nexport function heartbeatIntervalMs(roomSize: number): number {\n return 2000 * Math.max(1, Math.ceil(Math.max(1, roomSize) / 25));\n}\n\n/** Silent this long = shown reconnecting: three missed beats and a second of slack (7 s small). */\nexport function staleAfterMs(roomSize: number): number {\n return heartbeatIntervalMs(roomSize) * 3 + 1000;\n}\n\nexport interface LivenessTracker {\n /** A beat (or any packet) arrived from `who`. */\n heard(who: string, beat: AliveBeat | null): void;\n /** People who have beaten at least once and are now silent past `staleAfterMs(roomSize)`. */\n stale(present: readonly string[], roomSize: number): Set<string>;\n /** The last reading `who` sent about their own path, or null. */\n reported(who: string): AliveBeat | null;\n /** This tab was itself cut off: everyone gets a fresh start from now (no false ghosts). */\n restartClock(): void;\n forget(who: string): void;\n clear(): void;\n}\n\nexport function createLivenessTracker(now: () => number): LivenessTracker {\n const lastHeard = new Map<string, number>();\n const beats = new Map<string, AliveBeat>();\n return {\n heard(who, beat) {\n lastHeard.set(who, now());\n if (beat !== null) beats.set(who, beat);\n },\n stale(present, roomSize) {\n const limit = staleAfterMs(roomSize);\n const at = now();\n const out = new Set<string>();\n for (const who of present) {\n const heard = lastHeard.get(who);\n if (heard !== undefined && at - heard > limit) out.add(who);\n }\n return out;\n },\n reported: (who) => beats.get(who) ?? null,\n restartClock() {\n const at = now();\n for (const who of lastHeard.keys()) lastHeard.set(who, at);\n },\n forget(who) {\n lastHeard.delete(who);\n beats.delete(who);\n },\n clear() {\n lastHeard.clear();\n beats.clear();\n },\n };\n}\n\n/* ------------------------------------------------------------------ video auto-off */\n\nexport interface VideoAutoOff {\n /**\n * One step with the current reading and whether the camera is on. Returns `\"pause\"` exactly\n * once when the camera should be paused for the network.\n */\n step(quality: SelfQuality, cameraOn: boolean): \"pause\" | null;\n /** The person turned their video back on themselves: no auto-pause until the path recovers. */\n retried(): void;\n reset(): void;\n}\n\n/** `afterMs` ≤ 0 = the knob is off: never pause. */\nexport function createVideoAutoOff(afterMs: () => number, now: () => number): VideoAutoOff {\n let poorSince: number | null = null;\n let armed = true;\n return {\n step(quality, cameraOn) {\n if (quality === \"good\") {\n poorSince = null;\n armed = true;\n return null;\n }\n if (quality !== \"poor\" || !cameraOn) {\n poorSince = quality === \"poor\" ? poorSince : null;\n return null;\n }\n const at = now();\n if (poorSince === null) poorSince = at;\n const limit = afterMs();\n if (!armed || limit <= 0 || at - poorSince < limit) return null;\n armed = false;\n return \"pause\";\n },\n retried() {\n armed = false;\n },\n reset() {\n poorSince = null;\n armed = true;\n },\n };\n}\n\n/* ------------------------------------------------------------------ stats → reading */\n\n/** The few `RTCStats` fields the reading needs (structural: no DOM types in the core). */\nexport interface StatsEntry {\n readonly id: string;\n readonly type: string;\n readonly [field: string]: unknown;\n}\n\n/**\n * One reading from a peer connection's `getStats()` entries: the SELECTED candidate pair (the\n * transport's `selectedCandidatePairId`, else the nominated succeeded pair), its round trip, its\n * local candidate's type and protocol, and the worst `fractionLost` the far end reported.\n */\nexport function readingFromStats(entries: Iterable<StatsEntry>): NetworkReading {\n const byId = new Map<string, StatsEntry>();\n for (const entry of entries) byId.set(entry.id, entry);\n let pair: StatsEntry | undefined;\n for (const entry of byId.values()) {\n if (entry.type === \"transport\" && typeof entry[\"selectedCandidatePairId\"] === \"string\") {\n pair = byId.get(entry[\"selectedCandidatePairId\"] as string);\n if (pair !== undefined) break;\n }\n }\n if (pair === undefined) {\n for (const entry of byId.values()) {\n if (entry.type === \"candidate-pair\" && entry[\"nominated\"] === true && entry[\"state\"] === \"succeeded\") {\n pair = entry;\n break;\n }\n }\n }\n let lossRatio: number | null = null;\n for (const entry of byId.values()) {\n if (entry.type === \"remote-inbound-rtp\" && typeof entry[\"fractionLost\"] === \"number\") {\n lossRatio = Math.max(lossRatio ?? 0, entry[\"fractionLost\"] as number);\n }\n }\n if (pair === undefined) return { rttMs: null, lossRatio, relayed: null, tcp: null };\n const rtt = pair[\"currentRoundTripTime\"];\n const local = typeof pair[\"localCandidateId\"] === \"string\" ? byId.get(pair[\"localCandidateId\"] as string) : undefined;\n const candidateType = local?.[\"candidateType\"];\n const protocol = local?.[\"protocol\"];\n const relayProtocol = local?.[\"relayProtocol\"];\n return {\n rttMs: typeof rtt === \"number\" ? Math.round(rtt * 1000) : null,\n lossRatio,\n relayed: typeof candidateType === \"string\" ? candidateType === \"relay\" : null,\n tcp:\n typeof protocol === \"string\" || typeof relayProtocol === \"string\"\n ? protocol === \"tcp\" || relayProtocol === \"tcp\" || relayProtocol === \"tls\"\n : null,\n };\n}\n\n/** Two transports (publisher, subscriber) → one reading: the worse round trip and loss, either path restricted. */\nexport function mergeReadings(readings: readonly NetworkReading[]): NetworkReading {\n const max = (values: (number | null)[]): number | null => {\n const known = values.filter((v): v is number => v !== null);\n return known.length === 0 ? null : Math.max(...known);\n };\n const any = (values: (boolean | null)[]): boolean | null =>\n values.every((v) => v === null) ? null : values.some((v) => v === true);\n return {\n rttMs: max(readings.map((r) => r.rttMs)),\n lossRatio: max(readings.map((r) => r.lossRatio)),\n relayed: any(readings.map((r) => r.relayed)),\n tcp: any(readings.map((r) => r.tcp)),\n };\n}\n","/**\n * THE ONE PLACE `livekit-client` IS TOUCHED.\n *\n * Everything above this file talks to `MeetRoomDriver`. That containment is\n * what lets the engine's whole behavior — ring timeouts, admission, escalation,\n * reconnection — be proven in a node test, and it is why an SDK upgrade is a\n * single-file review rather than a package-wide audit.\n *\n * 🚨 **THE IMPORT IS DYNAMIC, DELIBERATELY.** `import type` is erased, and the\n * runtime `await import(\"livekit-client\")` happens inside `connect()`. So:\n * importing `@ai-matrx/meet` during SSR, or on a page that renders a call\n * BUTTON but never a call, pulls in none of the media SDK and touches no\n * browser API (independence contract §4). A static import here would put ~400KB\n * of WebRTC into every consumer's first load and break RSC rendering.\n *\n * The mapping helpers below are exported for their own tests: an SDK enum\n * renamed upstream must fail a named test, not silently become \"unknown\" on\n * every tile.\n */\n\nimport type {\n ConnectionQuality as LiveKitQuality,\n Participant,\n Room,\n RoomOptions,\n} from \"livekit-client\";\nimport { detectCapabilities } from \"./capabilities\";\nimport { MeetError, normalizeDeviceError, normalizeScreenShareError } from \"./errors\";\nimport {\n TRANSCRIPTION_ATTRIBUTES,\n TRANSCRIPTION_TOPIC,\n type AudioProcessing,\n type DriverConnectionState,\n type DriverParticipant,\n type DriverTrackKind,\n type MeetRoomDriver,\n type RoomDriverHandlers,\n type VideoEffect,\n} from \"./room-driver\";\nimport type { ConnectionQuality } from \"./types\";\nimport { createReconnectPolicy } from \"./connection-supervisor\";\nimport { mergeReadings, readingFromStats, type NetworkReading, type StatsEntry } from \"./network-health\";\n\n/**\n * LiveKit's quality enum → ours. Mapped explicitly rather than cast: the SDK\n * has five states and our UI has five sentences, and an unmapped one must\n * degrade to `unknown` (an honest \"we don't know\") rather than to `excellent`\n * (a lie that hides a failing call).\n */\nexport function mapQuality(quality: LiveKitQuality | string): ConnectionQuality {\n switch (quality) {\n case \"excellent\":\n return \"excellent\";\n case \"good\":\n return \"good\";\n case \"poor\":\n return \"poor\";\n case \"lost\":\n return \"lost\";\n default:\n return \"unknown\";\n }\n}\n\nexport function mapConnectionState(state: string): DriverConnectionState {\n switch (state) {\n case \"connected\":\n return \"connected\";\n case \"connecting\":\n return \"connecting\";\n case \"reconnecting\":\n return \"reconnecting\";\n // Only the signal socket is down (S6b): its own region value, so the supervisor can tell a\n // signal blip from a full media reconnect.\n case \"signalReconnecting\":\n return \"signal_reconnecting\";\n default:\n return \"disconnected\";\n }\n}\n\n/**\n * A LiveKit participant → our snapshot.\n *\n * 🚨 `name` IS REPORTED EXACTLY AS LIVEKIT GAVE IT, empty string and all\n * (MRI-A18). Through 0.4.12 it fell back to the identity here, which meant a\n * driver — the thing furthest from any screen — invented a \"name\" that\n * `core/names.ts` then had to recognize and refuse. A driver reports; it does\n * not name. The one fallback lives in `projectParticipant`, and it is the name\n * book's honest placeholder rather than a token.\n */\nexport function snapshotParticipant(\n participant: Pick<\n Participant,\n \"identity\" | \"name\" | \"metadata\" | \"isMicrophoneEnabled\" | \"isCameraEnabled\" | \"isScreenShareEnabled\"\n > & { attributes?: Readonly<Record<string, string>> | undefined },\n isLocal: boolean,\n): DriverParticipant {\n const name = participant.name;\n return {\n identity: participant.identity,\n name: name ?? \"\",\n metadata: participant.metadata ?? null,\n // ATTRIBUTES ARE PART OF THE SNAPSHOT, not read ad hoc later: the note-taker\n // announces the consent notice here (D10) and the roster is the only place\n // this package looks at a participant.\n attributes: participant.attributes ?? {},\n isLocal,\n micEnabled: participant.isMicrophoneEnabled,\n cameraEnabled: participant.isCameraEnabled,\n screenSharing: participant.isScreenShareEnabled,\n };\n}\n\nexport interface LiveKitDriverOptions {\n /** Adaptive stream + dynacast default ON — R5 scale honesty, not a knob. */\n roomOptions?: RoomOptions | undefined;\n}\n\n/** The browser defaults every call product ships with. */\nexport const DEFAULT_AUDIO_PROCESSING: AudioProcessing = {\n noiseSuppression: true,\n echoCancellation: true,\n autoGainControl: true,\n};\n\n/**\n * Background effects run a segmentation model on the GPU (MediaPipe via\n * `@livekit/track-processors`). A browser without `OffscreenCanvas` or WebGL2\n * cannot — the control is then ABSENT, never a toggle that silently fails.\n */\nexport function videoEffectsSupported(): boolean {\n const scope = globalThis as unknown as {\n OffscreenCanvas?: unknown;\n document?: { createElement(tag: string): { getContext(kind: string): unknown } };\n };\n if (typeof scope.OffscreenCanvas === \"undefined\" || scope.document === undefined) return false;\n try {\n return scope.document.createElement(\"canvas\").getContext(\"webgl2\") !== null;\n } catch {\n return false;\n }\n}\n\n/**\n * Put the chosen effect on the local camera. The processor package is imported\n * lazily, exactly like `livekit-client` itself, so a meeting nobody blurs never\n * downloads a segmentation model.\n */\nasync function applyVideoEffect(target: Room, effect: VideoEffect): Promise<void> {\n const { Track } = await import(\"livekit-client\");\n const track = target.localParticipant.getTrackPublication(Track.Source.Camera)?.track as\n | {\n setProcessor(processor: unknown): Promise<void>;\n stopProcessor(): Promise<void>;\n }\n | undefined;\n if (track === undefined) return; // camera off: applied when it turns on.\n try {\n if (effect.kind === \"none\") {\n await track.stopProcessor();\n return;\n }\n const processors = await import(\"@livekit/track-processors\");\n const processor =\n effect.kind === \"blur\"\n ? processors.BackgroundBlur(effect.radius ?? 12)\n : processors.VirtualBackground(effect.imageUrl);\n await track.setProcessor(processor);\n } catch (error) {\n throw new MeetError(\n \"media-unsupported\",\n \"The background effect could not start on this camera\",\n \"Background effects need a browser with WebGL2 (current Chrome, Edge, Firefox or Safari). \" +\n \"Your video continues without the effect.\",\n error,\n );\n }\n}\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\nexport function createLiveKitDriver(\n handlers: RoomDriverHandlers,\n options: LiveKitDriverOptions = {},\n): MeetRoomDriver {\n let room: Room | null = null;\n /**\n * The roster projection for the CURRENT room, kept out here so `connect()`\n * can fire it once the transport is up. See the call site for why.\n */\n let projectRoster: (() => void) | null = null;\n /** What the person chose, kept so a re-enabled track gets it too. */\n let audioCapture: AudioProcessing = DEFAULT_AUDIO_PROCESSING;\n let videoEffect: VideoEffect = { kind: \"none\" };\n\n /**\n * P1 connection-network: sample this tab's path every 2 s while connected. `engine.pcManager`\n * is the SDK's own handle on the two peer connections; a reading is skipped (never invented)\n * while a transport is missing or reconnecting.\n */\n let statsTimer: ReturnType<typeof setInterval> | null = null;\n function stopStatsPoll(): void {\n if (statsTimer !== null) clearInterval(statsTimer);\n statsTimer = null;\n }\n function startStatsPoll(target: Room): void {\n stopStatsPoll();\n if (handlers.networkReading === undefined) return;\n const sample = async (): Promise<void> => {\n if (room !== target) return;\n const manager = (target.engine as unknown as {\n pcManager?: { publisher?: { getStats(): Promise<RTCStatsReport> | undefined }; subscriber?: { getStats(): Promise<RTCStatsReport> | undefined } };\n }).pcManager;\n if (manager === undefined) return;\n const readings: NetworkReading[] = [];\n for (const transport of [manager.publisher, manager.subscriber]) {\n try {\n const report = await transport?.getStats();\n if (report !== undefined) readings.push(readingFromStats(report.values() as Iterable<StatsEntry>));\n } catch {\n // A transport mid-restart has no stats; the next sample reads it.\n }\n }\n if (readings.length > 0 && room === target) handlers.networkReading?.(mergeReadings(readings));\n };\n statsTimer = setInterval(() => void sample(), 2000);\n }\n\n async function load(): Promise<Room> {\n if (room !== null) return room;\n let module: typeof import(\"livekit-client\");\n try {\n module = await import(\"livekit-client\");\n } catch (error) {\n throw new MeetError(\n \"misconfigured\",\n \"livekit-client could not be loaded\",\n \"`livekit-client` is a dependency of @ai-matrx/meet and is imported lazily at \" +\n \"connect time. If this failed, the bundler excluded it — remove it from any \" +\n \"`external`/`serverExternalPackages` list, or pass your own driver factory.\",\n error,\n );\n }\n const { Room: LiveKitRoom, RoomEvent, Track } = module;\n const created = new LiveKitRoom({\n // R5: large meetings degrade gracefully instead of melting a laptop.\n adaptiveStream: true,\n dynacast: true,\n // S6b: OUR retry schedule — jittered from attempt 0 (the SDK's default has no jitter on its\n // first two attempts), give-up limits in one place (`connection-supervisor.ts`).\n reconnectPolicy: createReconnectPolicy(),\n ...options.roomOptions,\n });\n /** LiveKit's numeric `DisconnectReason` → its NAME; a number means nothing outside this file. */\n const reasonName = (reason: unknown): string | null =>\n reason === undefined || reason === null\n ? null\n : ((module.DisconnectReason as unknown as Record<number, string> | undefined)?.[reason as number] ?? String(reason));\n\n const roster = (): void => {\n const local = snapshotParticipant(created.localParticipant, true);\n const remotes = [...created.remoteParticipants.values()].map((participant) =>\n snapshotParticipant(participant, false),\n );\n // The FULL roster every time. Every one of these six events changes what\n // a tile must render, and a delta protocol across six sources drifts.\n handlers.participants([local, ...remotes]);\n };\n\n projectRoster = roster;\n\n // 🚨 A REMOTE MICROPHONE IS PLAYED HERE, OR NOWHERE. `livekit-client` never\n // plays a remote audio track by itself (`webAudioMix` is off by default): a\n // subscribed track makes sound only once `track.attach()` gives it a media\n // element. Until 0.7.10 nothing attached audio, so every meeting received\n // everyone's audio packets and played none of them (production\n // re-verification, 2026-09-29). Audio is NOT tied to tiles on purpose: the\n // stage paginates and the Board strip overflows, and a person on page two\n // must still be heard.\n created\n .on(RoomEvent.TrackSubscribed, (track) => {\n if (track.kind !== Track.Kind.Audio || typeof document === \"undefined\") return;\n if (!remoteAudioPlayback) return;\n const element = track.attach();\n element.setAttribute(\"data-mx-meet-audio\", \"\");\n audioSink().appendChild(element);\n })\n .on(RoomEvent.TrackUnsubscribed, (track) => {\n if (track.kind !== Track.Kind.Audio) return;\n for (const element of track.detach()) element.remove();\n });\n\n created\n .on(RoomEvent.ParticipantConnected, roster)\n // WHY they left goes first (S6b): an unannounced drop is held as a `reconnecting` tile,\n // and the roster that follows must already know it.\n .on(RoomEvent.ParticipantDisconnected, (participant, reason) => {\n handlers.participantLeft?.(participant.identity, reasonName(reason));\n roster();\n })\n .on(RoomEvent.TrackSubscribed, roster)\n .on(RoomEvent.TrackUnsubscribed, roster)\n .on(RoomEvent.TrackMuted, roster)\n .on(RoomEvent.TrackUnmuted, roster)\n .on(RoomEvent.LocalTrackPublished, (publication) => {\n // `share-stopped-by-browser`: the browser's own \"Stop sharing\" ends the share's track;\n // unpublish it (screen and its sound) so everyone's stage and this tab's controls follow.\n if (publication.source === Track.Source.ScreenShare) {\n const ended = (): void => {\n void created.localParticipant.setScreenShareEnabled(false).catch(() => undefined);\n };\n publication.track?.mediaStreamTrack.addEventListener(\"ended\", ended, { once: true });\n }\n roster();\n })\n .on(RoomEvent.LocalTrackUnpublished, roster)\n // A SERVER DECISION CHANGED MID-MEETING (Meet wave 2): a co-host made or\n // unmade, the host handed over, a rename. The server re-stamps the\n // roster through `UpdateParticipant`; these three events carry it here.\n .on(RoomEvent.ParticipantNameChanged, roster)\n .on(RoomEvent.ParticipantAttributesChanged, roster)\n .on(RoomEvent.ParticipantMetadataChanged, roster)\n .on(RoomEvent.RoomMetadataChanged, (metadata) => {\n handlers.roomMetadata?.(metadata ?? \"\");\n })\n // S6c MEDIA (the media supervisor's inputs). Autoplay: LiveKit flips this when an\n // attached remote audio element's play() is refused, and back when startAudio() (inside\n // a click) or a later play succeeds.\n .on(RoomEvent.AudioPlaybackStatusChanged, (canPlay: boolean) => {\n handlers.audioPlayback?.(canPlay);\n })\n .on(RoomEvent.MediaDevicesChanged, () => {\n handlers.devicesChanged?.();\n })\n .on(RoomEvent.MediaDevicesError, (error: Error, kind?: MediaDeviceKind) => {\n handlers.mediaDeviceError?.(error, kind === \"audioinput\" || kind === \"videoinput\" ? kind : null);\n })\n .on(RoomEvent.ActiveSpeakersChanged, (speakers) => {\n handlers.activeSpeakers(speakers.map((speaker) => speaker.identity));\n })\n .on(RoomEvent.ConnectionQualityChanged, (quality, participant) => {\n handlers.quality(participant.identity, mapQuality(quality));\n })\n .on(RoomEvent.ConnectionStateChanged, (state) => {\n handlers.connectionState(mapConnectionState(state));\n })\n .on(RoomEvent.DataReceived, (payload, participant, _kind, topic) => {\n handlers.data(\n topic ?? \"\",\n decoder.decode(payload),\n participant?.identity ?? null,\n );\n })\n .on(RoomEvent.Disconnected, (reason) => {\n stopStatsPoll();\n audioContainer?.remove();\n audioContainer = null;\n // §3.4 ER reads LiveKit's reason by NAME (`DUPLICATE_IDENTITY`, `PARTICIPANT_REMOVED`, …):\n // the enum is numeric on the wire, and a number means nothing outside this file.\n handlers.disconnected(reasonName(reason));\n });\n\n // LIVE CAPTIONS (D16). The agent publishes one text stream per utterance on\n // `lk.transcription`, re-sending the same `lk.segment_id` as the words firm\n // up, and the FINAL flag says which one is the last. The chunks are read as\n // they arrive — waiting for `readAll()` would make captions appear only\n // once the sentence was over, which is not a caption.\n //\n // 🚨 REGISTERED BEFORE `connect()`, deliberately: LiveKit buffers data\n // packets that arrive before the room is connected and replays them, but a\n // handler registered after the first stream header lands misses that\n // utterance outright — the first thing anyone says in the meeting.\n try {\n created.registerTextStreamHandler(TRANSCRIPTION_TOPIC, (reader, participantInfo) => {\n const attributes: Record<string, string> = { ...(reader.info.attributes ?? {}) };\n const segmentId =\n attributes[TRANSCRIPTION_ATTRIBUTES.segmentId] ?? reader.info.id;\n const trackId = attributes[TRANSCRIPTION_ATTRIBUTES.trackId] ?? null;\n const final = attributes[TRANSCRIPTION_ATTRIBUTES.final] === \"true\";\n // `lk.transcribed_participant_identity` is WHO IS BEING TRANSCRIBED.\n // The SFU's publisher identity stays the note-taker, so using it here\n // would attribute the whole meeting to the agent. Do not use the\n // sender-identity override: LiveKit accepts it from an agent publisher,\n // but aidream deliberately does not set it.\n const speakerIdentity =\n attributes[TRANSCRIPTION_ATTRIBUTES.speakerIdentity] ?? participantInfo.identity;\n const at = new Date(reader.info.timestamp || Date.now()).toISOString();\n void (async () => {\n let text = \"\";\n try {\n for await (const chunk of reader) {\n text += chunk;\n // Emit as it grows: a caption that only appears when the sentence\n // ends is a transcript, not a caption.\n handlers.transcription({\n segmentId,\n speakerIdentity,\n publisherIdentity: participantInfo.identity,\n text,\n final,\n trackId,\n at,\n });\n }\n } catch (error) {\n // A caption stream that dies is a caption we do not have. It is\n // never fatal to the meeting and never silent.\n handlers.data(\n TRANSCRIPTION_TOPIC,\n JSON.stringify({ error: String((error as Error)?.message ?? error) }),\n participantInfo.identity,\n );\n }\n })();\n });\n } catch {\n // Registering twice throws in the SDK. `load()` is single-flight through\n // `room`, so this can only happen on a re-entrant remount — in which case\n // the existing handler is already delivering captions.\n }\n\n // Kept for `attach`, which needs the SDK's Source enum at call time.\n sources = {\n camera: Track.Source.Camera,\n microphone: Track.Source.Microphone,\n screen: Track.Source.ScreenShare,\n };\n trackEvents = {\n subscribed: RoomEvent.TrackSubscribed,\n localPublished: RoomEvent.LocalTrackPublished,\n };\n room = created;\n return created;\n }\n\n let sources: Record<DriverTrackKind, string> | null = null;\n /** The SDK's event names `attach` listens on, captured with `sources`. */\n let trackEvents: { subscribed: string; localPublished: string } | null = null;\n\n /** The hidden container every remote audio element of THIS room lives in. */\n let audioContainer: HTMLElement | null = null;\n /** S7: false for a companion connection — no remote audio element is ever attached. */\n let remoteAudioPlayback = true;\n function audioSink(): HTMLElement {\n if (audioContainer !== null && audioContainer.isConnected) return audioContainer;\n const container = document.createElement(\"div\");\n container.setAttribute(\"data-mx-meet-audio-sink\", \"\");\n container.setAttribute(\"aria-hidden\", \"true\");\n container.style.display = \"none\";\n document.body.appendChild(container);\n audioContainer = container;\n return container;\n }\n\n function requireRoom(operation: string): Room {\n if (room === null) {\n throw new MeetError(\n \"transport\",\n `${operation}: the room is not connected`,\n \"Call connect() first. The engine never reaches this — it gates every media call \" +\n \"on its own phase — so seeing it means a consumer drove the driver directly.\",\n );\n }\n return room;\n }\n\n return {\n async connect(serverUrl, token) {\n const target = await load();\n try {\n await target.connect(serverUrl, token);\n } catch (error) {\n // A connect failure is either media permission (user-actionable) or\n // transport. Both must reach the UI with a sentence, never a spinner.\n throw normalizeDeviceError(error, \"meet.connect\");\n }\n // 🚨 PROJECT THE ROSTER ONCE, HERE. Every other roster source is an\n // EVENT, and a participant who publishes nothing into an empty room\n // generates none of them: no `LocalTrackPublished`, no\n // `ParticipantConnected`, no track events. Until 0.3.2 the join always\n // published a microphone, so `LocalTrackPublished` fired and hid this —\n // and the moment listen-only attendance became possible, a listen-only\n // host was absent from their own People panel, had no tile, and got NO\n // host controls at all (the note-taker could not be started), because\n // host powers are read off the local participant's roster entry.\n projectRoster?.();\n // The room's metadata arrives WITH the join (the spotlight lives there),\n // and `RoomMetadataChanged` fires only for later changes.\n handlers.roomMetadata?.(target.metadata ?? \"\");\n // S6c: the autoplay state as of the join — `AudioPlaybackStatusChanged` fires only on change.\n handlers.audioPlayback?.(target.canPlaybackAudio);\n startStatsPoll(target);\n },\n async disconnect() {\n if (room === null) return;\n const target = room;\n room = null;\n try {\n target.unregisterTextStreamHandler(TRANSCRIPTION_TOPIC);\n } catch {\n // Already gone. Teardown is idempotent by contract.\n }\n projectRoster = null;\n stopStatsPoll();\n audioContainer?.remove();\n audioContainer = null;\n await target.disconnect();\n },\n async setMicrophoneEnabled(enabled) {\n try {\n await requireRoom(\"setMicrophoneEnabled\").localParticipant.setMicrophoneEnabled(\n enabled,\n audioCapture,\n );\n } catch (error) {\n throw normalizeDeviceError(error, \"meet.setMicrophoneEnabled\");\n }\n },\n async setCameraEnabled(enabled) {\n try {\n const target = requireRoom(\"setCameraEnabled\");\n await target.localParticipant.setCameraEnabled(enabled);\n // A REMEMBERED EFFECT FOLLOWS THE CAMERA. Turning the camera back on must\n // not quietly show the room behind someone who chose to blur it.\n if (enabled && videoEffect.kind !== \"none\") await applyVideoEffect(target, videoEffect);\n } catch (error) {\n throw normalizeDeviceError(error, \"meet.setCameraEnabled\");\n }\n },\n async setScreenShareEnabled(enabled) {\n try {\n // Ask for the tab's sound too (`share-audio`): Chrome then offers \"Also share tab audio\".\n await requireRoom(\"setScreenShareEnabled\").localParticipant.setScreenShareEnabled(\n enabled,\n enabled ? { audio: true, systemAudio: \"include\" } : undefined,\n );\n } catch (error) {\n throw normalizeScreenShareError(error, \"meet.setScreenShareEnabled\");\n }\n },\n async publishData(topic, payload, to) {\n await requireRoom(\"publishData\").localParticipant.publishData(\n // `reliable` is not optional for a control plane: a dropped\n // \"hand lowered\" leaves a hand up on every other screen forever.\n encoder.encode(payload) as Uint8Array<ArrayBuffer>,\n to === undefined || to.length === 0\n ? { reliable: true, topic }\n : { reliable: true, topic, destinationIdentities: [...to] },\n );\n },\n async setAudioProcessing(processing) {\n audioCapture = { ...audioCapture, ...processing };\n const target = room;\n if (target === null) return;\n const { Track } = await import(\"livekit-client\");\n const publication = target.localParticipant.getTrackPublication(Track.Source.Microphone);\n const track = publication?.track;\n if (track === undefined || publication?.isMuted === true) return;\n try {\n // The browser's own processing, applied to the LIVE track: a restart\n // with new constraints, the same device, no renegotiation.\n await (track as unknown as { restartTrack(options: object): Promise<void> }).restartTrack({\n ...audioCapture,\n ...(target.getActiveDevice(\"audioinput\") !== undefined\n ? { deviceId: target.getActiveDevice(\"audioinput\") }\n : {}),\n });\n } catch (error) {\n throw normalizeDeviceError(error, \"meet.setAudioProcessing\");\n }\n },\n async setVideoEffect(effect) {\n videoEffect = effect;\n const target = room;\n if (target === null) return;\n await applyVideoEffect(target, effect);\n },\n supportsVideoEffects() {\n return videoEffectsSupported();\n },\n screenShareHasAudio() {\n const target = room;\n if (target === null) return false;\n // `Track.Source.ScreenShareAudio`, by value: the SDK enum is not in scope outside connect().\n return target.localParticipant.getTrackPublications().some((p) => String(p.source) === \"screen_share_audio\" && p.track !== undefined);\n },\n supportsScreenShare() {\n // iOS Safari and most phones have no `getDisplayMedia` at all.\n // A phone browser that exposes it still cannot present (`share-unsupported-mobile`).\n return detectCapabilities().canShareScreen;\n },\n attach(identity, kind, element) {\n const target = requireRoom(\"attach\");\n const source = sources?.[kind];\n if (source === undefined) return () => undefined;\n // 🚨 THE TRACK MAY NOT EXIST YET, AND MAY BE REPLACED LATER. A tile\n // renders as soon as the roster says the camera is on — which is known\n // from the PUBLICATION, before the track is SUBSCRIBED — and the tile's\n // element stays mounted, so its ref never runs again. Until 0.7.10 an\n // attach that found no track returned a no-op for good: a person\n // admitted from the waiting room was a black tile for everyone already\n // inside (production re-verification, 2026-09-29). So the attach keeps\n // listening for this participant's track and (re)attaches whenever it\n // arrives or is republished; the returned cleanup stops listening too.\n let current: { detach(element: HTMLMediaElement): unknown } | null = null;\n const sync = (): void => {\n const participant =\n target.localParticipant.identity === identity\n ? target.localParticipant\n : target.remoteParticipants.get(identity);\n const track = participant?.getTrackPublication(source as never)?.track;\n if (track === undefined || track === current) return;\n current?.detach(element);\n track.attach(element);\n current = track;\n };\n const onTrack = (...args: unknown[]): void => {\n // TrackSubscribed(track, publication, participant) /\n // LocalTrackPublished(publication, participant) — the participant is\n // always the last argument.\n const who = args[args.length - 1] as { identity?: string } | undefined;\n if (who?.identity === identity) sync();\n };\n sync();\n const events = trackEvents;\n if (events !== null) {\n target.on(events.subscribed as never, onTrack as never);\n target.on(events.localPublished as never, onTrack as never);\n }\n // The DETACH IS THE RETURN VALUE — a React effect's cleanup cannot forget\n // to call something it was handed.\n return () => {\n if (events !== null) {\n target.off(events.subscribed as never, onTrack as never);\n target.off(events.localPublished as never, onTrack as never);\n }\n current?.detach(element);\n current = null;\n };\n },\n activeDevices() {\n const target = room;\n if (target === null) {\n return { audioinput: null, videoinput: null, audiooutput: null };\n }\n return {\n audioinput: target.getActiveDevice(\"audioinput\") ?? null,\n videoinput: target.getActiveDevice(\"videoinput\") ?? null,\n audiooutput: target.getActiveDevice(\"audiooutput\") ?? null,\n };\n },\n async switchDevice(kind, deviceId) {\n try {\n await requireRoom(\"switchDevice\").switchActiveDevice(kind, deviceId);\n } catch (error) {\n throw normalizeDeviceError(error, \"meet.switchDevice\");\n }\n },\n setRemoteAudioPlayback(enabled) {\n remoteAudioPlayback = enabled;\n if (enabled || audioContainer === null) return;\n for (const element of Array.from(audioContainer.querySelectorAll(\"[data-mx-meet-audio]\"))) {\n if (element instanceof HTMLMediaElement) element.srcObject = null;\n element.remove();\n }\n },\n async startAudio() {\n // S6c: inside the person's click. LiveKit plays every attached remote audio element and\n // resumes its AudioContext; success flips `AudioPlaybackStatusChanged` back to true.\n const target = room;\n if (target === null) return;\n await target.startAudio();\n },\n };\n}\n\n/**\n * THE PRE-JOIN CAMERA PREVIEW (Meet wave 2) — the same SDK track and the same\n * background processor the room will use, so what someone sees before joining\n * is exactly what the meeting will see. Lives here because this file is the one\n * place `livekit-client` is touched.\n */\nexport interface CameraPreview {\n attach(element: HTMLVideoElement): void;\n setEffect(effect: VideoEffect): Promise<void>;\n stop(): void;\n}\n\nexport async function createCameraPreview(\n deviceId: string | null,\n effect: VideoEffect,\n): Promise<CameraPreview> {\n let module: typeof import(\"livekit-client\");\n try {\n module = await import(\"livekit-client\");\n } catch (error) {\n throw new MeetError(\n \"misconfigured\",\n \"livekit-client could not be loaded for the camera preview\",\n \"The bundler excluded `livekit-client`; remove it from any `external` list.\",\n error,\n );\n }\n let track: Awaited<ReturnType<typeof module.createLocalVideoTrack>>;\n try {\n track = await module.createLocalVideoTrack(deviceId === null ? {} : { deviceId });\n } catch (error) {\n throw normalizeDeviceError(error, \"meet.cameraPreview\");\n }\n const target = track as unknown as {\n setProcessor(processor: unknown): Promise<void>;\n stopProcessor(): Promise<void>;\n };\n async function apply(next: VideoEffect): Promise<void> {\n if (next.kind === \"none\") {\n await target.stopProcessor();\n return;\n }\n const processors = await import(\"@livekit/track-processors\");\n await target.setProcessor(\n next.kind === \"blur\"\n ? processors.BackgroundBlur(next.radius ?? 12)\n : processors.VirtualBackground(next.imageUrl),\n );\n }\n if (effect.kind !== \"none\" && videoEffectsSupported()) {\n try {\n await apply(effect);\n } catch {\n // The preview still shows the camera; the settings say the effect failed.\n }\n }\n return {\n attach(element) {\n track.attach(element);\n },\n setEffect: apply,\n stop() {\n track.stop();\n track.detach();\n },\n };\n}\n","/**\n * MEETINGS — the lobby, the host powers, and the durable link.\n *\n * A meeting is a room plus scheduling, lobby and host metadata (D3). Everything\n * here is about the moments OUTSIDE the media session, which is exactly why it\n * cannot ride LiveKit's data plane: a guest waiting in the lobby is not in the\n * LiveKit room yet, by construction. The lobby therefore lives on the meet-room\n * realtime channel.\n *\n * 🚨 THE THREE THINGS THAT MAKE GUEST JOIN SAFE (D6)\n *\n * 1. **The link is an identity, not a session.** A durable slug resolves before,\n * during, and after the scheduled time, and a recurring meeting keeps ONE\n * link. Nothing here mints a per-occurrence URL.\n * 2. **A guest's capability is the TOKEN, and nothing else.** They are scoped to\n * exactly one room. There is no guest row this client writes that could grant\n * anything, and no client-side \"is this person allowed\" branch.\n * 3. **Admission is a SERVER decision, recorded.** `admit` is an RPC; the\n * broadcast that tells the knocker is a NOTIFICATION of that decision, never\n * the decision itself. Two hosts clicking admit is the ordinary case, and the\n * RPC is what makes it idempotent.\n *\n * Host powers are checked against the role the SERVER minted into the token\n * (see `room.ts`), and the UI hides what an actor cannot do rather than\n * rendering a dead button (no dead ends).\n */\n\nimport { isWaitingPhase, type ExitServerWord } from \"./phase\";\nimport type { ChannelHandle, RealtimeManager } from \"@ai-matrx/realtime\";\nimport { MEET_EVENTS, meetingTopic, roomTopic } from \"./channels\";\nimport type { MeetWaiting } from \"./store\";\nimport type { MeetApi } from \"./api\";\nimport { MeetError } from \"./errors\";\nimport { readServerRefusal } from \"./ai\";\nimport type { MeetRepository } from \"./repository\";\nimport { effectiveRole, type MeetStore } from \"./store\";\nimport { MEET_ROUTES, MEETING_PASS_HEADER } from \"./tokens\";\nimport { livePolicyFrom, samePolicy } from \"./live-policy\";\nimport {\n asParticipantIdentity,\n isHostRole,\n isGuest,\n WAITING_ADMISSION_STATES,\n type AdmissionState,\n ROLE_RANK,\n type LobbyEntry,\n type MeetIdentity,\n type MeetParticipant,\n type MeetingAttendee,\n type MeetingId,\n type MeetingRecord,\n type ParticipantIdentity,\n type ParticipantRole,\n type RecordingState,\n type RoomName,\n type UserId,\n} from \"./types\";\n\nexport interface MeetingSessionOptions {\n manager: RealtimeManager;\n repository: MeetRepository;\n store: MeetStore;\n identity: MeetIdentity;\n /**\n * The ONE org-aware aidream client (MRI-A16) — `endForEveryone` is a SERVER\n * decision (MRI-A6), and every request it makes carries the organization.\n */\n api: MeetApi;\n onDiagnostic?: ((event: { level: \"info\" | \"warn\" | \"error\"; message: string; remedy?: string }) => void) | undefined;\n /**\n * 🚨 THE HOST ADMITTED *US*. Called exactly once per admission verdict that\n * names this participant, and only for `admitted`.\n *\n * Admission is not a UI state change: a lobby token is minted with\n * `canPublish: false` and `canSubscribe: false`, so the waiting room can only\n * become a room by minting a NEW token. Before this existed the waiting room\n * said \"you will join automatically once you are admitted\" and then waited\n * forever — a sentence on screen that was not true. The provider wires this\n * to `engine.rejoin()`.\n */\n onAdmitted?: (() => void) | undefined;\n /**\n * S4: the meeting pass this device holds (`x-meet-pass`) — what the state read and\n * `/leave` are authorized by. Null when no pass is held yet.\n */\n resolvePass?: (() => string | null) | undefined;\n /** S4: mint again (Ask to join again / Rejoin from the waiting screens). */\n rejoin?: (() => Promise<void>) | undefined;\n}\n\nexport interface MeetingSession {\n /** Subscribe to the room's out-of-band plane. Hosts see knocks; guests see verdicts. */\n watch(roomName: RoomName): void;\n /**\n * Subscribe to the MEETING RECORD's durable feed — transcript segments, notes,\n * and the meeting row itself (which is where the note-taker's state lives).\n * Reconnecting IS re-reading: the backfill door re-reads all three, because\n * realtime has no replay and a socket blip would otherwise leave a transcript\n * permanently missing the minute nobody noticed.\n */\n watchMeeting(meetingId: MeetingId): void;\n admit(entry: LobbyEntry, admitted: boolean): Promise<void>;\n /** S4: lift a denial so the person may knock again (`meet_undeny`). */\n undeny(entry: LobbyEntry): Promise<void>;\n admitAll(): Promise<void>;\n /**\n * 🚨 S4 — THE WAIT READS SERVER TRUTH (CORE-DESIGN §2.6). While this tab waits, the state\n * read (`GET /v1/meet/state`) runs every `waiting_poll_seconds`, on every\n * `mx.state.changed` hint, on `online` and on visibility return. Admitted → a fresh mint\n * (`rejoin`); denied / expired / ended are what the row says, never a broadcast's guess.\n */\n startWaiting(): void;\n stopWaiting(): void;\n /** One state read now (idempotent while one is in flight). */\n readState(): Promise<void>;\n /** One read of the meeting's rules now (chat mode, attendee lock, time limit); resolves once they are on the store. */\n readLivePolicy(): Promise<void>;\n /** rec-consent-gate: tell the SERVER this person agreed to being recorded (a row fact; it gives publish back). */\n acknowledgeConsent(): Promise<void>;\n /**\n * §3.4 ER: what the server recorded for THIS connection's exit, and the run's phase. null =\n * the read failed (offline / unreachable); undefined = no meeting or no pass to ask with.\n */\n readExit(connection: string, pass?: string | null): Promise<ExitServerWord | null | undefined>;\n /** Stop waiting: the row becomes `withdrawn` (`POST /v1/meet/leave {intent: \"withdraw\"}`). */\n withdraw(): Promise<void>;\n /** Ask again after a denial or an expired knock (only offered when allowed). */\n askAgain(): Promise<void>;\n /**\n * S8: the HOST's leave intents through `POST /v1/meet/leave` — end the meeting for everyone,\n * or hand the run's host powers to `assignTo` (a person key) before leaving. The server\n * decides from the pass whether this person holds the run's host powers.\n */\n hostLeave(intent: \"end_for_all\" | { readonly assignTo: string }): Promise<void>;\n setLock(locked: boolean): Promise<MeetingRecord>;\n /** `wr-policy-who-waits`: one rule of the meeting on screen (null when unreadable). */\n meetingRule(key: string): Promise<unknown>;\n /** A host changes one rule for this meeting; waiting people are told to read again. */\n setMeetingRule(key: string, value: string | number | boolean): Promise<unknown>;\n /** End it for everyone, through the server (MRI-A6). */\n endForEveryone(): Promise<EndMeetingResult>;\n dispose(): void;\n}\n\n/** Can `actor` moderate `target`? A host outranks a co-host outranks everyone. */\nexport function canModerateParticipant(\n actor: MeetParticipant | undefined,\n target: MeetParticipant | undefined,\n): boolean {\n if (actor === undefined || target === undefined) return false;\n if (actor.role === \"participant\") return false;\n if (actor.identity === target.identity) return false;\n // A co-host cannot remove the host. Without this rank check, \"host controls\"\n // is a mutual-eviction button.\n return ROLE_RANK[actor.role] > ROLE_RANK[target.role];\n}\n\nexport function roleOf(\n participants: readonly MeetParticipant[],\n identity: ParticipantIdentity | null,\n): ParticipantRole {\n if (identity === null) return \"participant\";\n return participants.find((p) => p.identity === identity)?.role ?? \"participant\";\n}\n\n/**\n * What ending a meeting produced. `served: false` is the honest answer from a\n * server that has not landed `POST /v1/meet/end` yet (MRI-A6) — the control is\n * real, the refusal is a sentence, and nothing pretends the meeting ended.\n */\nexport interface EndMeetingResult {\n readonly endedAt: string | null;\n readonly served: boolean;\n}\n\n/** The sentence a server without MRI-A6 earns. Pinned so every surface says it. */\nexport const END_MEETING_NOT_SERVED =\n \"Ending a meeting is not available on this server yet\";\n\n/**\n * One durable `meet_participants` row as a lobby entry, or `null` when it is\n * not one. A row that has been decided is not a knocker; the panel shows people\n * who are WAITING, and `store.upsertLobby` keyed by identity is what lets the\n * table and the broadcast be two copies of one person rather than two rows.\n */\nfunction lobbyEntryFrom(attendee: MeetingAttendee, now: number = Date.now()): LobbyEntry | null {\n if (!WAITING_ADMISSION_STATES.includes(attendee.admissionState)) return null;\n // A knock past its expiry IS expired (the server CASes it on the knocker's next read).\n const expires = attendee.knockExpiresAt ? Date.parse(attendee.knockExpiresAt) : Number.NaN;\n if (!Number.isNaN(expires) && expires <= now) return null;\n return {\n identity: attendee.identity,\n displayName: attendee.displayName,\n avatarUrl: attendee.avatarUrl,\n userId: attendee.userId,\n state: \"knocking\",\n knockedAt: attendee.knockedAt ?? attendee.joinedAt ?? new Date().toISOString(),\n knockExpiresAt: attendee.knockExpiresAt ?? null,\n decidedAt: null,\n decidedBy: null,\n flag: attendee.knockFlag ?? null,\n };\n}\n\n/**\n * A DENIED row as a lobby entry, so the host can lift the denial (`meet_undeny`) — the only door\n * that lets a refused person knock again under the `until_lifted` rule. Null for every other row.\n */\nfunction deniedEntryFrom(attendee: MeetingAttendee): LobbyEntry | null {\n if (attendee.admissionState !== \"denied\") return null;\n return {\n identity: attendee.identity,\n displayName: attendee.displayName,\n avatarUrl: attendee.avatarUrl,\n userId: attendee.userId,\n state: \"denied\",\n knockedAt: attendee.knockedAt ?? attendee.joinedAt ?? new Date().toISOString(),\n knockExpiresAt: null,\n decidedAt: null,\n decidedBy: null,\n };\n}\n\n/** One row → the door's list: a knocker waits, a denied person can be let back in; the rest clear. */\nfunction applyDoorRow(store: MeetStore, attendee: MeetingAttendee): void {\n const entry = lobbyEntryFrom(attendee) ?? deniedEntryFrom(attendee);\n if (entry !== null) {\n store.upsertLobby(entry);\n return;\n }\n // 🚨 A DECIDED ROW MUST CLEAR THE DOOR. Admitting a guest from another tab writes `admitted`\n // here, and ignoring the row would leave this host's panel offering to admit somebody who is\n // already in the meeting — a ghost knocker who can never be dismissed.\n store.setAdmission(attendee.identity, decidedState(attendee.admissionState), null);\n}\n\n/** The row's word for a person no longer waiting, as the host's lobby list records it. */\nfunction decidedState(state: AdmissionState): AdmissionState {\n return WAITING_ADMISSION_STATES.includes(state) ? \"expired\" : state;\n}\n\n/** The server's state read, as this package reads it. */\ninterface StateRead {\n readonly admission_state?: string | null;\n readonly admitters_present?: boolean;\n readonly reknock_allowed?: boolean;\n readonly reknock_in_seconds?: number | null;\n readonly knock_expires_at?: string | null;\n readonly session_phase?: string;\n readonly meeting_phase?: string;\n readonly live_version?: number;\n readonly poll_seconds?: number;\n readonly exit_reason?: string | null;\n readonly live_policy?: Record<string, unknown>;\n readonly consent_given?: boolean;\n readonly session_started_at?: string | null;\n}\n\n/** How often a person in the call re-reads the meeting's rules. */\nexport const LIVE_POLICY_POLL_MS = 3000;\n\nexport function createMeetingSession(options: MeetingSessionOptions): MeetingSession {\n const { manager, repository, store, identity } = options;\n let channel: ChannelHandle | null = null;\n let meetingChannel: ChannelHandle | null = null;\n\n /**\n * The meeting row, for anyone who may be in it. A member or a person the\n * meeting was shared with reads it under RLS; a signed-in person from ANOTHER\n * organization who followed the link cannot, and gets the same row through\n * the link door (`meet_meeting_by_slug`) a guest uses — the server lets them\n * in by the link, so the screen must not fail them for holding it.\n */\n async function readMeeting(meetingId: MeetingId): Promise<MeetingRecord> {\n try {\n return await repository.meeting(meetingId);\n } catch (error) {\n const slug = store.snapshot().meeting?.slug;\n if (slug === undefined) throw error;\n return repository.meetingBySlug(slug);\n }\n }\n\n function requireHostUser(operation: string): UserId {\n // THE SAME PREDICATE THE UI RENDERS FROM (MRI-A11). Reading only the roster\n // here — while the control renders from the token grant — is how a host\n // control becomes a button that throws the moment it is used.\n if (identity.userId === null || !isHostRole(effectiveRole(store.snapshot()))) {\n throw new MeetError(\n \"forbidden\",\n `${operation}: this participant is not a host`,\n \"Host powers come from the server-minted token metadata and are enforced by the RPC. \" +\n \"Hide the control rather than rendering a button that will be refused.\",\n );\n }\n return identity.userId;\n }\n\n function requireMeeting(operation: string): MeetingRecord {\n const meeting = store.snapshot().meeting;\n if (meeting === null) {\n throw new MeetError(\n \"misconfigured\",\n `${operation}: no meeting is loaded`,\n \"Ad-hoc call rooms have no meeting record and therefore no lobby or lock. Use \" +\n \"joinMeeting()/scheduleMeeting() when you need those powers.\",\n );\n }\n return meeting;\n }\n\n // ── S4: the waiting supervisor ─────────────────────────────────────────\n let polling = false;\n let pollTimer: ReturnType<typeof setTimeout> | null = null;\n let reading: Promise<void> | null = null;\n let admittedOnce = false;\n let unlisten: (() => void) | null = null;\n\n function report(level: \"info\" | \"warn\" | \"error\", message: string, remedy?: string): void {\n options.onDiagnostic?.(remedy === undefined ? { level, message } : { level, message, remedy });\n }\n\n function pollMs(): number {\n const seconds = store.snapshot().waiting?.pollSeconds ?? 3;\n return Math.max(1, Math.min(60, seconds)) * 1000;\n }\n\n function schedule(ms: number): void {\n if (pollTimer !== null) clearTimeout(pollTimer);\n if (!polling) return;\n pollTimer = setTimeout(() => {\n pollTimer = null;\n void readState().finally(() => schedule(pollMs()));\n }, ms);\n }\n\n function listen(): void {\n if (unlisten !== null || typeof window === \"undefined\") return;\n const nudge = (): void => {\n if (typeof document !== \"undefined\" && document.visibilityState === \"hidden\") return;\n void readState();\n };\n // Closing the tab while waiting withdraws the knock (beacon: no headers, pass in the body).\n const goodbye = (): void => {\n const meeting = store.snapshot().meeting;\n const pass = options.resolvePass?.() ?? null;\n const waiting = store.snapshot().waiting;\n if (meeting === null || pass === null || waiting === null) return;\n if (!WAITING_ADMISSION_STATES.includes(waiting.admission)) return;\n try {\n navigator.sendBeacon?.(\n options.api.url(MEET_ROUTES.leave),\n new Blob([JSON.stringify({ meeting_id: meeting.id, intent: \"withdraw\", meeting_pass: pass })], {\n type: \"application/json\",\n }),\n );\n } catch {\n // Best effort: an unanswered knock still lapses on its own (knock_timeout_minutes).\n }\n };\n window.addEventListener(\"online\", nudge);\n window.addEventListener(\"pagehide\", goodbye);\n document.addEventListener(\"visibilitychange\", nudge);\n unlisten = () => {\n window.removeEventListener(\"online\", nudge);\n window.removeEventListener(\"pagehide\", goodbye);\n document.removeEventListener(\"visibilitychange\", nudge);\n };\n }\n\n function stopWaiting(): void {\n polling = false;\n if (pollTimer !== null) clearTimeout(pollTimer);\n pollTimer = null;\n unlisten?.();\n unlisten = null;\n }\n\n function patchWaiting(next: Partial<MeetWaiting>): void {\n const current = store.snapshot().waiting;\n const base: MeetWaiting = current ?? {\n admission: \"knocking\",\n admittersPresent: null,\n reknockAllowed: true,\n knockExpiresAt: null,\n pollSeconds: 3,\n liveVersion: 0,\n };\n store.patch({ waiting: { ...base, ...next } });\n }\n\n function apply(read: StateRead): void {\n const snapshot = store.snapshot();\n if (!isWaitingPhase(snapshot.phase)) return;\n const admission = (read.admission_state ?? null) as AdmissionState | null;\n const ended =\n admission === \"ended\" || read.session_phase === \"ended\" || read.meeting_phase === \"ended\";\n if (admission === \"admitted\" && !ended) {\n if (admittedOnce) return;\n admittedOnce = true;\n stopWaiting();\n // ADMISSION IS A JOIN: a fresh mint with the grants the server now gives.\n options.onAdmitted?.();\n return;\n }\n if (ended) {\n stopWaiting();\n patchWaiting({ admission: \"ended\" });\n const meeting = snapshot.meeting;\n if (read.meeting_phase === \"ended\" && meeting !== null && meeting.endedAt === null) {\n store.patch({ meeting: { ...meeting, endedAt: new Date().toISOString() } });\n }\n store.dispatch({ type: \"admission\", admission: \"ended\" }, { reason: \"ended\" });\n return;\n }\n if (admission === null) return;\n patchWaiting({\n admission,\n admittersPresent: read.admitters_present ?? null,\n reknockAllowed: read.reknock_allowed ?? true,\n reknockAt: typeof read.reknock_in_seconds === \"number\" && read.reknock_in_seconds > 0 ? Date.now() + read.reknock_in_seconds * 1000 : null,\n knockExpiresAt: read.knock_expires_at ?? null,\n pollSeconds: read.poll_seconds ?? snapshot.waiting?.pollSeconds ?? 3,\n liveVersion: read.live_version ?? 0,\n });\n // THE SERVER'S WORD MOVES THE PHASE (§3.1): one machine event, never a patch.\n store.dispatch({ type: \"admission\", admission });\n // A decision that is not \"yes\" ends the wait; the screen offers what the rules allow.\n if (admission === \"denied\" || admission === \"expired\" || admission === \"withdrawn\" || admission === \"removed\") {\n stopWaiting();\n }\n }\n\n // ── P1: the rules, live, while in the call ─────────────────────────────\n let policyTimer: ReturnType<typeof setInterval> | null = null;\n let policyReading = false;\n let policyInFlight: Promise<void> | null = null;\n\n async function readLivePolicy(): Promise<void> {\n const meeting = store.snapshot().meeting;\n const pass = options.resolvePass?.() ?? null;\n if (meeting === null || pass === null) return;\n // A read already in flight may have been asked BEFORE the host changed a rule (it was sent,\n // then the rule moved, then its answer landed): the caller needs a read that starts now, so\n // wait for that one and then run a fresh one.\n if (policyReading) {\n await (policyInFlight ?? undefined);\n return readLivePolicy();\n }\n policyReading = true;\n let done: () => void = () => undefined;\n policyInFlight = new Promise<void>((resolve) => (done = resolve));\n try {\n const response = await options.api.request({\n path: `${MEET_ROUTES.state}?meeting_id=${encodeURIComponent(meeting.id)}`,\n method: \"GET\",\n requireAuth: false,\n operation: \"meet.state(policy)\",\n headers: { [MEETING_PASS_HEADER]: pass },\n });\n if (!response.ok) return;\n const read = (await response.json()) as StateRead;\n const next = livePolicyFrom(read.live_policy);\n const started = read.session_started_at ? Date.parse(read.session_started_at) : NaN;\n const snapshot = store.snapshot();\n const startedAt = Number.isFinite(started) ? started : snapshot.sessionStartedAt;\n if (!samePolicy(snapshot.livePolicy, next) || startedAt !== snapshot.sessionStartedAt) {\n store.patch({ livePolicy: next, sessionStartedAt: startedAt });\n }\n // The server's row is the consent, not this screen's memory (reload, second tab, rejoin).\n if (read.consent_given === true && !store.snapshot().recordingConsentGiven) {\n store.patch({ recordingConsentGiven: true });\n }\n } catch {\n // Offline: the next tick asks again; the rules on screen stay as they were.\n } finally {\n policyReading = false;\n policyInFlight = null;\n done();\n }\n }\n\n function syncLivePolicyTimer(): void {\n const inCall = store.snapshot().phase === \"in_call\";\n if (inCall && policyTimer === null) {\n void readLivePolicy();\n policyTimer = setInterval(() => void readLivePolicy(), LIVE_POLICY_POLL_MS);\n } else if (!inCall && policyTimer !== null) {\n clearInterval(policyTimer);\n policyTimer = null;\n }\n }\n const unsubscribePolicy = store.subscribe(syncLivePolicyTimer);\n\n async function readState(): Promise<void> {\n if (reading !== null) return reading;\n const meeting = store.snapshot().meeting;\n const pass = options.resolvePass?.() ?? null;\n if (meeting === null || pass === null) return;\n reading = (async () => {\n try {\n const response = await options.api.request({\n path: `${MEET_ROUTES.state}?meeting_id=${encodeURIComponent(meeting.id)}`,\n method: \"GET\",\n requireAuth: false,\n operation: \"meet.state\",\n headers: { [MEETING_PASS_HEADER]: pass },\n });\n if (!response.ok) {\n let body: unknown = null;\n try {\n body = await response.json();\n } catch {\n body = null;\n }\n const refusal = readServerRefusal(body);\n report(\n \"warn\",\n refusal?.reason ?? `meet.state: the server answered ${response.status}`,\n refusal?.remedy ?? \"The waiting room keeps asking; nothing on this screen changes until the server answers.\",\n );\n return;\n }\n apply((await response.json()) as StateRead);\n } catch (error) {\n // Offline or the server is unreachable: the next tick (or `online`) asks again.\n report(\"info\", `meet.state: ${String(error)}`, \"Waiting continues; the state is read again shortly.\");\n } finally {\n reading = null;\n }\n })();\n return reading;\n }\n\n async function postLeave(\n intent: \"withdraw\" | \"leave\" | \"end_for_all\" | \"assign_and_leave\",\n extra: Record<string, unknown> = {},\n ): Promise<void> {\n const meeting = requireMeeting(`meet.${intent}`);\n const pass = options.resolvePass?.() ?? null;\n const response = await options.api.request({\n path: MEET_ROUTES.leave,\n method: \"POST\",\n requireAuth: false,\n operation: `meet.${intent}`,\n body: { meeting_id: meeting.id, intent, ...extra },\n ...(pass !== null ? { headers: { [MEETING_PASS_HEADER]: pass } } : {}),\n });\n if (!response.ok) {\n let body: unknown = null;\n try {\n body = await response.json();\n } catch {\n body = null;\n }\n const refusal = readServerRefusal(body);\n throw new MeetError(\n \"transport\",\n refusal?.reason ?? `meet.${intent}: the server answered ${response.status}`,\n refusal?.remedy ??\n (intent === \"withdraw\" ? \"You are still in the waiting room. Try again.\" : \"Nothing was changed. Try again.\"),\n );\n }\n }\n\n /** After a member-client admission write the CLIENT sends the hint (CORE-DESIGN §2.4). */\n function hint(): void {\n channel?.send(MEET_EVENTS.stateChanged, { v: null });\n }\n\n /**\n * After a member-client admission DOOR the SERVER republishes the room's replicated state\n * (the waiting count everyone's People badge shows) — only the server writes room state\n * (CORE-DESIGN §2.4, §2.6 RS). Truth first, then the hint. A failed tell is reported, never\n * thrown: the decision stands, and the count catches up on the next server write.\n */\n async function tellServerAdmissionChanged(operation: string): Promise<void> {\n const meeting = store.snapshot().meeting;\n if (meeting === null) return;\n const pass = options.resolvePass?.() ?? null;\n try {\n const response = await options.api.request({\n path: MEET_ROUTES.admissionChanged,\n method: \"POST\",\n requireAuth: false,\n operation,\n body: { meeting_id: meeting.id },\n ...(pass !== null ? { headers: { [MEETING_PASS_HEADER]: pass } } : {}),\n });\n if (!response.ok) {\n report(\n \"warn\",\n `${operation}: the server answered ${response.status} when told about the admission change`,\n \"The decision was saved. The waiting count in the room may lag until the next change.\",\n );\n }\n } catch (error) {\n report(\n \"warn\",\n `${operation}: ${String(error)}`,\n \"The decision was saved. The waiting count in the room may lag until the next change.\",\n );\n }\n }\n\n /** The host's second carrier: on a hint, re-read who is at the door. */\n async function refreshQueue(): Promise<void> {\n const meeting = store.snapshot().meeting;\n if (meeting === null || isGuest(identity) || !isHostRole(effectiveRole(store.snapshot()))) return;\n try {\n const attendees = await repository.attendees(meeting.id);\n store.learn(attendees);\n for (const attendee of attendees) applyDoorRow(store, attendee);\n } catch {\n // Postgres Changes on the same rows is the other carrier; a miss here costs nothing.\n }\n }\n\n return {\n watch(roomName) {\n channel?.close();\n channel = manager.open({\n topic: roomTopic(roomName),\n broadcast: [\n {\n // 🚨 PAYLOAD-FREE. Every receiver reads again; a spoofed hint only causes a read.\n event: MEET_EVENTS.stateChanged,\n onMessage: () => {\n if (isWaitingPhase(store.snapshot().phase)) void readState();\n void refreshQueue();\n },\n },\n ],\n // THE BACKFILL DOOR. A socket blip must not lose a decision: re-read the meeting\n // row, and a waiting tab re-reads its own state.\n onBackfill: async () => {\n const meeting = store.snapshot().meeting;\n if (meeting === null) return;\n const fresh = isGuest(identity)\n ? await repository.meetingBySlug(meeting.slug)\n : await readMeeting(meeting.id);\n store.patch({ meeting: fresh, locked: fresh.locked });\n if (isWaitingPhase(store.snapshot().phase)) void readState();\n },\n });\n },\n\n watchMeeting(meetingId) {\n meetingChannel?.close();\n meetingChannel = null;\n if (isGuest(identity)) {\n options.onDiagnostic?.({\n level: \"info\",\n message: \"Guest notes and captions arrive through the live meeting room; saved history is not available without a session.\",\n remedy: \"Sign in with access to this meeting to reload saved notes and transcript history.\",\n });\n return;\n }\n meetingChannel = manager.open({\n topic: meetingTopic(meetingId),\n postgresChanges: [\n {\n event: \"*\",\n schema: repository.schema,\n table: repository.tables.transcripts,\n filter: `meeting_id=eq.${meetingId}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: ({ row }) => {\n // D16: THE ROW IS THE TRANSCRIPT. It replaces the caption that\n // showed the same words a moment earlier.\n if (row !== null) store.ingestRecord(repository.projectSegment(row));\n },\n },\n {\n event: \"*\",\n schema: repository.schema,\n table: repository.tables.notes,\n filter: `meeting_id=eq.${meetingId}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: ({ row }) => {\n // Upsert, not append: a re-run of the wrap-up rewrites the SAME\n // row ids by design, and appending would show every note twice.\n if (row !== null) store.upsertNote(repository.projectNote(row));\n },\n },\n {\n // 🚨 THE LOBBY IS A TABLE, NOT ONLY A CHANNEL (MRI-A16). A guest\n // knocks by writing `admission_state='knocking'` AND broadcasting;\n // through 0.4.7 the host's People panel rendered only the broadcast,\n // so a knocker whose packet landed before this browser opened the\n // channel — a host who joined second, reloaded, or slept a laptop —\n // was INVISIBLE while the guest sat in the waiting room forever. A\n // host can read this table; a guest cannot, and gets no lobby list\n // at all, which is correct.\n event: \"*\",\n schema: repository.schema,\n table: repository.tables.participants,\n filter: `meeting_id=eq.${meetingId}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: ({ row }) => {\n if (row === null) return;\n const attendee = repository.projectAttendee(row);\n store.learn([attendee]);\n // The durable row is the verdict, wherever it was made (see `applyDoorRow`).\n applyDoorRow(store, attendee);\n },\n },\n {\n event: \"*\",\n schema: repository.schema,\n table: repository.tables.recordings,\n filter: `meeting_id=eq.${meetingId}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: ({ row }) => {\n // 🚨 THE INDICATOR RENDERS THE DURABLE STATE, not this browser's\n // hope. Through 0.4.7 nothing subscribed here, so the only\n // transitions the store ever saw were the optimistic `starting`\n // and `stopping` THIS client patched — and the indicator sat on\n // \"Starting recording…\" for a whole meeting while a healthy\n // egress ran, then \"Stopping recording…\" forever (MRI-C8).\n if (row === null) return;\n const recording = repository.projectRecording(row);\n store.setRecording(recording.state, recording.startedBy, \"record\", recording.id);\n },\n },\n {\n event: \"*\",\n schema: repository.schema,\n table: repository.tables.meetings,\n filter: `id=eq.${meetingId}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: ({ row }) => {\n // The note-taker's state lives on this row's metadata (D9/MRI-A2),\n // so this subscription is what keeps each server-recorded state\n // current on screen without a reload.\n if (row === null) return;\n const meeting = repository.projectMeeting(row);\n store.patch({ meeting, locked: meeting.locked });\n },\n },\n ],\n // THE BACKFILL DOOR. Realtime has no replay: everything that happened\n // while the socket was down is gone. Re-reading all three is what keeps\n // a recovered surface from looking healthy and being wrong.\n onBackfill: async () => {\n const [meeting, transcript, notes, recordings, attendees] = await Promise.all([\n readMeeting(meetingId),\n repository.transcript(meetingId),\n repository.notes(meetingId),\n repository.recordings(meetingId),\n repository.attendees(meetingId),\n ]);\n store.patch({ meeting, locked: meeting.locked });\n for (const segment of transcript) store.ingestRecord(segment);\n for (const note of notes) store.upsertNote(note);\n // A socket blip must not leave the indicator saying something the\n // database stopped agreeing with ten minutes ago. `recordings` is\n // newest first.\n const latest = recordings[0];\n if (latest !== undefined) {\n store.setRecording(latest.state, latest.startedBy, \"record\", latest.id);\n }\n // Everyone the durable rows know a name for, and every knocker still\n // waiting. A host who reloads must not lose a person at the door.\n store.learn(attendees);\n // The same rule on the backfill door: a host who reloads must not find people at the\n // door who were admitted while they were gone, and finds the people they declined.\n for (const attendee of attendees) applyDoorRow(store, attendee);\n },\n });\n // The FIRST read is the same read: a surface that joins a meeting already\n // in progress must see what was already said.\n meetingChannel.requestBackfill(\"manual\");\n },\n\n async admit(entry, admitted) {\n const by = requireHostUser(\"meet.admit\");\n const meeting = requireMeeting(\"meet.admit\");\n // THE RPC IS THE DECISION, and it returns the row as it now stands — that row (not\n // this click) is what the panel shows. Two hosts deciding at once is the ordinary\n // case: the second sees the first's verdict.\n const row = await repository.decideAdmission({\n meetingId: meeting.id,\n identity: entry.identity,\n decision: admitted ? \"admit\" : \"deny\",\n byUserId: by,\n });\n store.upsertLobby({\n ...entry,\n state: decidedState(row.admissionState),\n decidedAt: new Date().toISOString(),\n decidedBy: by,\n });\n await tellServerAdmissionChanged(\"meet.admit\");\n hint();\n },\n\n async undeny(entry) {\n const by = requireHostUser(\"meet.undeny\");\n const meeting = requireMeeting(\"meet.undeny\");\n const row = await repository.decideAdmission({\n meetingId: meeting.id,\n identity: entry.identity,\n decision: \"undeny\",\n byUserId: by,\n });\n store.upsertLobby({ ...entry, state: decidedState(row.admissionState), decidedAt: new Date().toISOString(), decidedBy: by });\n await tellServerAdmissionChanged(\"meet.undeny\");\n hint();\n },\n\n async admitAll() {\n const by = requireHostUser(\"meet.admitAll\");\n const meeting = requireMeeting(\"meet.admitAll\");\n const rows = await repository.admitAll(meeting.id, by);\n for (const row of rows) {\n store.setAdmission(row.identity, row.admissionState, by);\n }\n await tellServerAdmissionChanged(\"meet.admitAll\");\n hint();\n },\n\n startWaiting() {\n if (polling) return;\n polling = true;\n admittedOnce = false;\n listen();\n schedule(0);\n },\n\n stopWaiting,\n readState,\n readLivePolicy,\n\n async acknowledgeConsent() {\n const meeting = requireMeeting(\"meet.consent\");\n const pass = options.resolvePass?.() ?? null;\n const response = await options.api.request({\n path: MEET_ROUTES.consent,\n method: \"POST\",\n requireAuth: false,\n operation: \"meet.consent\",\n body: { meeting_id: meeting.id },\n ...(pass !== null ? { headers: { [MEETING_PASS_HEADER]: pass } } : {}),\n });\n if (!response.ok) {\n throw new MeetError(\"transport\", `meet.consent: the server answered ${response.status}`, \"Your agreement was not saved. Try again.\");\n }\n },\n\n async readExit(connection, held) {\n const meeting = store.snapshot().meeting;\n const pass = held ?? options.resolvePass?.() ?? null;\n if (meeting === null || pass === null) return undefined;\n try {\n const response = await options.api.request({\n path: `${MEET_ROUTES.state}?meeting_id=${encodeURIComponent(meeting.id)}&connection=${encodeURIComponent(connection)}`,\n method: \"GET\",\n requireAuth: false,\n operation: \"meet.state(exit)\",\n headers: { [MEETING_PASS_HEADER]: pass },\n });\n if (!response.ok) return null;\n const read = (await response.json()) as StateRead;\n return {\n exitReason: read.exit_reason ?? null,\n sessionPhase: read.session_phase ?? null,\n meetingPhase: read.meeting_phase ?? null,\n };\n } catch {\n return null;\n }\n },\n\n async withdraw() {\n stopWaiting();\n await postLeave(\"withdraw\");\n patchWaiting({ admission: \"withdrawn\" });\n store.dispatch({ type: \"admission\", admission: \"withdrawn\" });\n hint();\n },\n\n async hostLeave(intent) {\n if (intent === \"end_for_all\") await postLeave(\"end_for_all\");\n else await postLeave(\"assign_and_leave\", { assign_to: intent.assignTo });\n },\n\n async askAgain() {\n // A new mint IS the new knock (the server upserts the person row per the re-knock rule).\n await options.rejoin?.();\n },\n\n async meetingRule(key) {\n const meeting = requireMeeting(\"meet.meetingRule\");\n return repository.meetingRule(meeting.id, key);\n },\n\n async setMeetingRule(key, value) {\n requireHostUser(\"meet.setMeetingRule\");\n const meeting = requireMeeting(\"meet.setMeetingRule\");\n const effective = await repository.setMeetingRule(meeting.id, key, value);\n hint();\n return effective;\n },\n\n async setLock(locked) {\n const by = requireHostUser(\"meet.setLock\");\n const meeting = requireMeeting(\"meet.setLock\");\n const updated = await repository.setLock(meeting.id, locked, by);\n store.patch({ meeting: updated, locked: updated.locked });\n // The lock is the meeting row (members see it through Postgres Changes); waiting people\n // are told to read again.\n hint();\n return updated;\n },\n\n /**\n * END IT FOR EVERYONE — a SERVER decision, not a row this client updates.\n *\n * 0.2.1 called the `meet_end_meeting` RPC directly, which ended the meeting\n * in the database while LiveKit kept the room open and the note-taker kept\n * writing into an ended meeting. `POST /v1/meet/end` re-checks the host,\n * closes the room, and triggers the wrap-up — one decision, one place\n * (MRI-A6).\n *\n * 🚨 A SERVER THAT HAS NOT LANDED THAT ROUTE ANSWERS 404, and that is\n * reported as a sentence, never as a dead control and never as a success.\n */\n async endForEveryone() {\n requireHostUser(\"meet.endForEveryone\");\n const meeting = requireMeeting(\"meet.endForEveryone\");\n const response = await options.api.request({\n path: MEET_ROUTES.meetingEnd,\n method: \"POST\",\n requireAuth: true,\n operation: \"meet.endForEveryone\",\n body: {\n meeting_id: meeting.id,\n organization_id: identity.organizationId,\n },\n });\n if (response.status === 404) {\n return { endedAt: null, served: false };\n }\n if (!response.ok) {\n let body: unknown = null;\n try {\n body = await response.json();\n } catch {\n body = null;\n }\n const refusal = readServerRefusal(body);\n throw new MeetError(\n response.status === 403 ? \"forbidden\" : \"transport\",\n refusal?.reason ?? `meet.endForEveryone: the server answered ${response.status}`,\n refusal?.remedy !== undefined && refusal.remedy.length > 0\n ? refusal.remedy\n : \"The meeting is still running. Only a host may end it, and the server \" +\n \"re-checks that — being a host on this screen is not the decision.\",\n );\n }\n let payload: unknown = null;\n try {\n payload = await response.json();\n } catch {\n payload = null;\n }\n const row =\n typeof payload === \"object\" && payload !== null\n ? (payload as Record<string, unknown>)\n : {};\n const endedAt =\n typeof row[\"ended_at\"] === \"string\" ? row[\"ended_at\"] : new Date().toISOString();\n const current = store.snapshot().meeting;\n if (current !== null) store.patch({ meeting: { ...current, endedAt } });\n return { endedAt, served: true };\n },\n\n dispose() {\n stopWaiting();\n unsubscribePolicy();\n if (policyTimer !== null) clearInterval(policyTimer);\n policyTimer = null;\n channel?.close();\n channel = null;\n meetingChannel?.close();\n meetingChannel = null;\n },\n };\n}\n\n/**\n * A durable public slug. Lowercase, hyphenated, and deliberately readable —\n * people read these aloud on phone calls. The alphabet excludes the four\n * characters that get misheard (0/O, 1/l).\n */\nconst SLUG_ALPHABET = \"abcdefghijkmnpqrstuvwxyz23456789\";\n\nexport function meetingSlug(random: () => number = Math.random): string {\n const group = (length: number): string =>\n Array.from({ length }, () => {\n const index = Math.floor(random() * SLUG_ALPHABET.length);\n return SLUG_ALPHABET[index] ?? \"a\";\n }).join(\"\");\n return `${group(3)}-${group(4)}-${group(3)}`;\n}\n\n/** The link a host copies. ONE shape, so every surface produces the same URL. */\nexport function meetingLink(baseUrl: string, slug: string): string {\n return `${baseUrl.replace(/\\/+$/, \"\")}/meet/${slug}`;\n}\n","/**\n * THE ONE WAY THIS PACKAGE TALKS TO AIDREAM (MRI-A16).\n *\n * 🚨 **Every request to the aidream server carries `x-organization-id`, and the\n * only way to guarantee \"every\" is for there to be ONE place that builds the\n * request.** aidream's org-admission middleware refuses an authenticated\n * request that does not name the organization it is acting in — 400\n * `organization_required`, before the router is ever reached.\n *\n * 0.1.3 learned that lesson and put the header on the three meet callers by\n * hand. That fixed the instances and left the CLASS wide open, and on\n * 2026-09-09 the class collected its next victim: `<RecordingPlayer>` fetched\n * `GET /files/{id}/download?inline=1` through a bare `fetch` with only a bearer\n * token, so a perfectly landed 1.38 MB recording could not be played by the\n * host who made it (MRI-C8; the identical URL returned `200 video/mp4` the\n * moment the header was added by hand). A fourth caller was written months\n * after the lesson and never heard it, because a lesson written into three call\n * sites is not a lesson, it is three copies of a workaround.\n *\n * So: this module owns the base URL, the organization header and the bearer\n * token, and `api.test.ts` holds a guard that FAILS THE BUILD if any other file\n * in the package calls `fetch(` against a base URL or points an element `src=`\n * at one. A new caller cannot be written without the header any more.\n *\n * 🚨 **HOW A `<video>` GETS ITS BYTES, given that signed URLs are eradicated.**\n * A browser media element cannot attach a header, so a bare\n * `src=\"https://server…/files/{id}/download\"` carries no session and no\n * organization and is refused — which is precisely what MRI-C8 saw. The\n * platform's answer is NOT a signed URL (`aidream/api/routers/files` mints\n * none, by law) but the FILE-SESSION COOKIE: `POST /files/session` mints an\n * HttpOnly `SameSite=None` cookie that the auth middleware accepts on exactly\n * the GET byte-serving routes (`COOKIE_AUTH_PATH_RE` in\n * `matrx_connect/middleware/session_cookie.py`), and a cookie rides a media\n * request that a header cannot. Every private `<img>`/`<video>` in the platform\n * already renders this way.\n *\n * So `fileMedia` mints the cookie and hands back a STREAMING url — which is\n * what lets a person scrub a two-hour recording instead of waiting for a\n * gigabyte to arrive. When the mint is refused (no session, an older server,\n * a browser blocking third-party cookies) it falls back to reading the bytes\n * with the organization header into a `blob:` url — and it SAYS it did, with\n * the reason, because a silent fallback that downloads a gigabyte is exactly\n * the kind of automatic intervention that must announce itself.\n */\n\nimport { MeetError, normalizeMeetError } from \"./errors\";\nimport type { OrganizationId } from \"./types\";\n\n/**\n * How the host tells us who is asking. Returns null before sign-in resolves,\n * and for a guest, who has no session at all and is still a first-class joiner.\n *\n * It lives HERE rather than in `tokens.ts` (where it was born) because this is\n * now the one module that turns a session into a request.\n */\nexport type AccessTokenSource = () => Promise<string | null>;\n\n/** The header aidream's org-admission middleware demands. One definition. */\nexport const ORGANIZATION_HEADER = \"x-organization-id\";\n\nexport interface MeetApiOptions {\n /** aidream's origin. Never a LiveKit or provider URL. */\n baseUrl: string;\n /** Named on EVERY request; the server refuses one that omits it. */\n organizationId: OrganizationId;\n accessToken: AccessTokenSource;\n fetchImpl?: typeof fetch | undefined;\n}\n\nexport interface MeetApiRequest {\n /** An absolute path on the aidream origin, e.g. `/api/v1/meet/token`. */\n path: string;\n method?: \"GET\" | \"POST\" | undefined;\n /** JSON body. Serialized here so no caller can forget the content type. */\n body?: Record<string, unknown> | undefined;\n /**\n * `true` — refuse before the wire when there is no session, with the given\n * operation named. `false` — send whatever session exists, or none (a guest\n * minting a room token is a real, authorized case).\n */\n requireAuth?: boolean | undefined;\n /** Names the caller in every error this raises. */\n operation: string;\n /** Extra headers. May not override the organization header. */\n headers?: Record<string, string> | undefined;\n /**\n * `\"include\"` for the ONE request that mints the file-session cookie — a\n * `Set-Cookie` on a cross-origin response is dropped without it.\n */\n credentials?: RequestCredentials | undefined;\n /** Cancels the request. The Q&A stream is abortable by the person asking. */\n signal?: AbortSignal | undefined;\n}\n\nexport interface MeetApi {\n readonly baseUrl: string;\n readonly organizationId: OrganizationId;\n /** The URL a request to `path` would go to. For diagnostics, never for fetching. */\n url(path: string): string;\n /** One request, with the organization header and the session already on it. */\n request(input: MeetApiRequest): Promise<Response>;\n /**\n * Read an authenticated file into a `blob:` URL an element can load. The\n * caller MUST `URL.revokeObjectURL` it — a leaked one pins the whole file in\n * memory for the life of the tab.\n */\n fetchObjectUrl(input: MeetApiRequest): Promise<string>;\n /**\n * A url a `<video>`/`<img>` can load for a platform file. Streams when the\n * file-session cookie can be minted; otherwise reads the bytes with the\n * organization header and says why it had to.\n */\n fileMedia(fileId: string, operation: string): Promise<MeetFileMedia>;\n /** Re-mint the file-session cookie. Returns whether it now exists. */\n refreshFileSession(): Promise<boolean>;\n}\n\n/** Where a media element's bytes are coming from, and whether that is plan A. */\nexport interface MeetFileMedia {\n readonly url: string;\n /** `stream` seeks and starts instantly; `bytes` is a fully downloaded blob. */\n readonly kind: \"stream\" | \"bytes\";\n /**\n * Set only for `bytes`, and never null-and-hoping: the sentence a surface\n * shows to explain why it had to download the whole file first.\n */\n readonly fallbackReason: string | null;\n}\n\n/** `POST /files/session` — mints the `mx_files_session` cookie (files law). */\nexport const FILE_SESSION_PATH = \"/files/session\";\n\nexport function createMeetApi(options: MeetApiOptions): MeetApi {\n const doFetch = options.fetchImpl ?? globalThis.fetch;\n const base = options.baseUrl.replace(/\\/+$/, \"\");\n\n async function headersFor(input: MeetApiRequest): Promise<Record<string, string>> {\n const token = await options.accessToken();\n if (token === null && input.requireAuth === true) {\n throw new MeetError(\n \"session-unavailable\",\n `${input.operation}: this request requires a signed-in session`,\n \"Render the signed-out or guest surface rather than a control that will be refused.\",\n );\n }\n return {\n ...(input.headers ?? {}),\n ...(input.body !== undefined ? { \"content-type\": \"application/json\" } : {}),\n // 🚨 LAST, so nothing a caller passes can shadow it.\n [ORGANIZATION_HEADER]: options.organizationId,\n ...(token !== null ? { authorization: `Bearer ${token}` } : {}),\n };\n }\n\n return {\n baseUrl: base,\n organizationId: options.organizationId,\n\n url(path) {\n return `${base}${path}`;\n },\n\n async request(input) {\n const headers = await headersFor(input);\n try {\n return await doFetch(`${base}${input.path}`, {\n method: input.method ?? (input.body !== undefined ? \"POST\" : \"GET\"),\n headers,\n ...(input.credentials !== undefined ? { credentials: input.credentials } : {}),\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n ...(input.body !== undefined ? { body: JSON.stringify(input.body) } : {}),\n });\n } catch (error) {\n throw normalizeMeetError(error, input.operation);\n }\n },\n\n async fetchObjectUrl(input) {\n const response = await this.request(input);\n if (response.status === 401 || response.status === 403) {\n throw new MeetError(\n \"forbidden\",\n `${input.operation}: you are not allowed to read this file`,\n \"The file is safe — this is a read decision, not a lost file. Sign in with an \" +\n \"account in this meeting's organization and try again.\",\n );\n }\n if (!response.ok) {\n throw new MeetError(\n \"transport\",\n `${input.operation}: the server returned ${response.status}`,\n \"The file itself is unaffected. Try again; if it repeats, the platform files \" +\n \"route is the thing to look at, not the recording.\",\n );\n }\n return URL.createObjectURL(await response.blob());\n },\n\n async refreshFileSession() {\n try {\n const response = await this.request({\n path: FILE_SESSION_PATH,\n method: \"POST\",\n // `Set-Cookie` on a cross-origin response is DROPPED without this.\n credentials: \"include\",\n requireAuth: true,\n operation: \"meet.files.session\",\n });\n return response.ok;\n } catch {\n // A refused mint is a fallback, never a failure: the caller reads the\n // bytes with the header instead and says so.\n return false;\n }\n },\n\n async fileMedia(fileId, operation) {\n const path = fileDownloadPath(fileId);\n if (await this.refreshFileSession()) {\n return { url: `${base}${path}`, kind: \"stream\", fallbackReason: null };\n }\n return {\n url: await this.fetchObjectUrl({ path, operation }),\n kind: \"bytes\",\n fallbackReason:\n \"This file was downloaded in full before playing, because the browser session that \" +\n \"lets it stream could not be established. Downloading can take longer, and seeking \" +\n \"is unavailable until it finishes.\",\n };\n },\n };\n}\n\n/** `GET /files/{id}/download?inline=1` — the platform files route (R3). */\nexport function fileDownloadPath(fileId: string): string {\n return `/files/${encodeURIComponent(fileId)}/download?inline=1`;\n}\n","/**\n * AFTER THE MEETING (Meet wave 3) — the pure pieces every client needs once the\n * room is gone: the attendance report, the transcript and notes as documents,\n * a moment in the transcript as a moment in the recording, search within one\n * transcript, and the two server calls behind the recap email and the saved\n * chat.\n *\n * Nothing here computes MEANING (same rule as `record.ts`): the summary,\n * decisions and action items are the wrap-up's durable rows; this file only\n * arranges, measures and formats them. Every function is framework-free so a\n * worker, a test and a browser produce byte-identical exports.\n */\n\nimport { toDelimited } from \"@ai-matrx/kit/delimited\";\nimport { formatDurationMs } from \"@ai-matrx/kit/format\";\nimport { readServerRefusal, refusalFromResponse } from \"./ai\";\nimport type { MeetApi } from \"./api\";\nimport { MeetError } from \"./errors\";\nimport { displayNameFor, type NameBook } from \"./names\";\nimport type { MeetingRecordBundle } from \"./record\";\nimport { chatFromRow } from \"./room-events\";\nimport type { RoomChatMessage } from \"./store\";\nimport { MEET_ROUTES, meetingPath, MEETING_PASS_HEADER } from \"./tokens\";\nimport type {\n MeetingAttendee,\n MeetingId,\n MeetingNote,\n MeetingRecord,\n RecordingRecord,\n TranscriptSegment,\n} from \"./types\";\n\n/* ─────────────────────────── Attendance ─────────────────────────── */\n\n/**\n * Who someone was, in the words an attendance report uses. `signed-in guest` is\n * an account from outside the meeting's organization that followed the link;\n * `guest` has no account at all.\n */\nexport type AttendanceKind = \"host\" | \"cohost\" | \"member\" | \"signed-in guest\" | \"guest\" | \"assistant\";\n\nexport interface AttendanceRow {\n readonly id: string;\n readonly identity: string;\n readonly name: string;\n readonly userId: string | null;\n readonly kind: AttendanceKind;\n /** First time they were in the room; null when they knocked and never came in. */\n readonly joinedAt: string | null;\n /** When they left; null while still in the room or when the leave was never recorded. */\n readonly leftAt: string | null;\n /**\n * Time in the room. A missing `leftAt` is measured to the meeting's end —\n * the room closed around them — and `null` only when neither is known.\n */\n readonly durationMs: number | null;\n /** True when `durationMs` was measured to the meeting's end, not a recorded leave. */\n readonly durationToEnd: boolean;\n readonly admission: MeetingAttendee[\"admissionState\"];\n /** How many times this person came in (a rejoin or a second device). */\n readonly joins: number;\n}\n\nfunction kindOf(person: MeetingAttendee): AttendanceKind {\n if (person.isAgent) return \"assistant\";\n if (person.role === \"host\") return \"host\";\n if (person.role === \"cohost\") return \"cohost\";\n // The durable row stores a link guest as `participant` with no account, so\n // \"no account\" is what makes someone a guest here — not the role word.\n if (person.userId === null) return \"guest\";\n return \"member\";\n}\n\nfunction ms(iso: string | null): number | null {\n if (iso === null) return null;\n const at = new Date(iso).getTime();\n return Number.isNaN(at) ? null : at;\n}\n\n/**\n * The attendance report of ONE meeting, host first then by arrival. People who\n * knocked and were never let in are kept (with no join time) — \"who asked to\n * come in\" is part of the record — but a denied knock has no duration.\n */\n/**\n * WHO a participant row is, as a PERSON (verifier, 2026-09-27: an export listed\n * the same attendee twice under \"attended\"). A signed-in person is their\n * account — every device and every rejoin is one person. A guest without an\n * account gets a fresh `guest:<random>` identity on every join, so the name\n * they typed is the only thread between their joins (Zoom's attendee report\n * merges link guests the same way); two different accountless guests who\n * typed the identical name read as one, which is the lesser error.\n */\nexport function attendeeKey(person: MeetingAttendee, names: NameBook = {}): string {\n if (person.userId !== null) return `user:${person.userId}`;\n if (person.isAgent) return `agent:${person.identity}`;\n const name = displayNameFor(names, person.identity, person.displayName).trim().toLowerCase();\n return name.length > 0 ? `guest-name:${name}` : `identity:${person.identity}`;\n}\n\n/**\n * One row per PERSON, in first-seen order: the row that actually joined (the\n * earliest) represents them. For a list of names or a head-count; the\n * attendance REPORT merges times as well (`attendanceReport`).\n */\nexport function uniqueAttendees(\n attendees: readonly MeetingAttendee[],\n names: NameBook = {},\n): readonly MeetingAttendee[] {\n const byKey = new Map<string, MeetingAttendee>();\n for (const person of attendees) {\n const key = attendeeKey(person, names);\n const kept = byKey.get(key);\n if (kept === undefined) {\n byKey.set(key, person);\n continue;\n }\n const keptJoin = ms(kept.joinedAt);\n const join = ms(person.joinedAt);\n if (join !== null && (keptJoin === null || join < keptJoin)) byKey.set(key, person);\n }\n return [...byKey.values()];\n}\n\nconst KIND_RANK: Record<AttendanceKind, number> = {\n host: 0,\n cohost: 1,\n member: 2,\n \"signed-in guest\": 3,\n guest: 4,\n assistant: 5,\n};\n\n/** Total time covered by a set of [start, end] intervals — overlaps count once. */\nfunction coveredMs(intervals: readonly (readonly [number, number])[]): number {\n const sorted = [...intervals].sort((a, b) => a[0] - b[0]);\n let total = 0;\n let from = -Infinity;\n let to = -Infinity;\n for (const [start, end] of sorted) {\n if (start > to) {\n if (to > from) total += to - from;\n from = start;\n to = end;\n } else if (end > to) {\n to = end;\n }\n }\n if (to > from) total += to - from;\n return total;\n}\n\nexport function attendanceReport(\n meeting: MeetingRecord,\n attendees: readonly MeetingAttendee[],\n names: NameBook = {},\n): readonly AttendanceRow[] {\n const end = ms(meeting.endedAt);\n // EVERY STAY COUNTS (verifier, 2026-09-29: a rejoin overwrote the first join,\n // so fifty minutes in the room read as ten). A row carries its stays; a row\n // written before stays were kept is one stay, first join to last leave.\n const measuredOf = new Map<AttendanceRow, (readonly [number, number])[]>();\n const perJoin = attendees.map((person): AttendanceRow => {\n const stays =\n person.sessions !== undefined && person.sessions.length > 0\n ? person.sessions.map((stay) => [ms(stay.joinedAt), ms(stay.leftAt)] as const)\n : [[ms(person.joinedAt), ms(person.leftAt)] as const];\n let toEnd = false;\n const measured: (readonly [number, number])[] = [];\n for (const [joined, left] of stays) {\n const until = left ?? end;\n if (joined === null || until === null || until < joined) continue;\n if (left === null) toEnd = true;\n measured.push([joined, until]);\n }\n const duration = measured.length === 0 ? null : coveredMs(measured);\n const row: AttendanceRow = {\n id: person.id,\n identity: person.identity,\n name: displayNameFor(names, person.identity, person.displayName),\n userId: person.userId,\n kind: kindOf(person),\n joinedAt: person.joinedAt,\n leftAt: person.leftAt,\n durationMs: duration,\n durationToEnd: duration !== null && toEnd,\n admission: person.admissionState,\n joins: Math.max(1, person.sessions?.length ?? 0),\n };\n measuredOf.set(row, measured);\n return row;\n });\n // ONE ROW PER PERSON: several joins (a rejoin, a second device) are one\n // attendee with their first arrival, last departure and total time — time\n // in the room twice at once counts once.\n const groups = new Map<string, { rows: AttendanceRow[]; people: MeetingAttendee[] }>();\n attendees.forEach((person, index) => {\n const key = attendeeKey(person, names);\n const group = groups.get(key) ?? { rows: [], people: [] };\n group.rows.push(perJoin[index]!);\n group.people.push(person);\n groups.set(key, group);\n });\n const rows = [...groups.values()].map(({ rows: joins }): AttendanceRow => {\n if (joins.length === 1) return joins[0]!;\n const came = joins.filter((row) => row.joinedAt !== null);\n const first =\n [...came].sort((a, b) => (ms(a.joinedAt) ?? 0) - (ms(b.joinedAt) ?? 0))[0] ?? joins[0]!;\n const kind = joins.reduce<AttendanceKind>(\n (best, row) => (KIND_RANK[row.kind] < KIND_RANK[best] ? row.kind : best),\n first.kind,\n );\n const open = came.some((row) => row.leftAt === null);\n const lastLeave = came.reduce<number | null>((latest, row) => {\n const left = ms(row.leftAt);\n return left !== null && (latest === null || left > latest) ? left : latest;\n }, null);\n const intervals = came.flatMap((row) => measuredOf.get(row) ?? []);\n return {\n ...first,\n kind,\n leftAt: open ? null : lastLeave === null ? null : new Date(lastLeave).toISOString(),\n durationMs: intervals.length === 0 ? null : coveredMs(intervals),\n durationToEnd: came.some((row) => row.durationToEnd),\n joins:\n came.length === 0 ? joins.length : came.reduce((total, row) => total + row.joins, 0),\n };\n });\n const rank = KIND_RANK;\n return rows.sort((a, b) => {\n if (rank[a.kind] !== rank[b.kind]) return rank[a.kind] - rank[b.kind];\n return (ms(a.joinedAt) ?? Number.MAX_SAFE_INTEGER) - (ms(b.joinedAt) ?? Number.MAX_SAFE_INTEGER);\n });\n}\n\n/** Headline numbers for the top of a report. The assistant is never a person. */\nexport function attendanceTotals(rows: readonly AttendanceRow[]): {\n readonly people: number;\n readonly guests: number;\n readonly neverAdmitted: number;\n readonly averageMs: number | null;\n} {\n const humans = rows.filter((row) => row.kind !== \"assistant\");\n const attended = humans.filter((row) => row.joinedAt !== null);\n const timed = attended.filter((row) => row.durationMs !== null);\n return {\n people: attended.length,\n guests: attended.filter((row) => row.kind === \"guest\" || row.kind === \"signed-in guest\").length,\n neverAdmitted: humans.filter((row) => row.joinedAt === null).length,\n averageMs:\n timed.length === 0\n ? null\n : Math.round(timed.reduce((sum, row) => sum + (row.durationMs ?? 0), 0) / timed.length),\n };\n}\n\nexport const ATTENDANCE_CSV_HEADER = [\n \"Meeting\",\n \"Meeting started\",\n \"Name\",\n \"Attended as\",\n \"Joined\",\n \"Left\",\n \"Minutes in the meeting\",\n \"Admission\",\n] as const;\n\n/**\n * CSV for a spreadsheet: one header row, one row per person, ISO timestamps (a\n * spreadsheet parses them; a formatted local clock loses the zone). Several\n * meetings — a series — concatenate with one header.\n */\nexport function attendanceCsv(\n reports: readonly { meeting: MeetingRecord; rows: readonly AttendanceRow[] }[],\n): string {\n // THE one writer (kit `toDelimited`): CRLF records, a trailing CRLF, names formula-guarded.\n return toDelimited(\n [\n ATTENDANCE_CSV_HEADER,\n ...reports.flatMap(({ meeting, rows }) =>\n rows.map((row) => [\n meeting.title,\n meeting.startedAt ?? meeting.scheduledFor,\n row.name,\n row.kind,\n row.joinedAt,\n row.leftAt,\n row.durationMs === null ? null : Math.round(row.durationMs / 6_000) / 10,\n row.admission,\n ]),\n ),\n ],\n { lineEnding: \"\\r\\n\", trailingNewline: true },\n );\n}\n\n/* ───────────────────── A moment in the recording ───────────────────── */\n\n/**\n * Where an instant of the meeting falls in the RECORDING, in milliseconds from\n * its start — or `null` when the recording does not cover it (a recording is\n * often a few minutes of a longer meeting). Never a clamp: seeking a line that\n * was said before recording began to 0:00 would play the wrong words.\n */\nexport function recordingOffsetMs(at: string, recording: RecordingRecord | null): number | null {\n if (recording === null || recording.state !== \"available\") return null;\n const start = ms(recording.startedAt);\n const moment = ms(at);\n if (start === null || moment === null || moment < start) return null;\n const end = ms(recording.endedAt);\n if (end !== null && moment > end) return null;\n return moment - start;\n}\n\n/** `m:ss` or `h:mm:ss` — a position in a recording or a meeting. */\nexport function clockPosition(offsetMs: number): string {\n return formatDurationMs(Math.max(0, offsetMs), { style: \"clock\" });\n}\n\n/* ───────────────────── Search within one transcript ───────────────────── */\n\nexport interface TranscriptMatch {\n readonly segmentId: string;\n /** [start, end) character ranges inside the segment's text, in order. */\n readonly ranges: readonly (readonly [number, number])[];\n}\n\n/**\n * Case-insensitive search of the transcript text AND the speaker's\n * name. Returns matches in transcript order. An empty query matches nothing.\n */\nexport function searchTranscript(\n segments: readonly TranscriptSegment[],\n query: string,\n names: NameBook = {},\n): readonly TranscriptMatch[] {\n const needle = fold(query.trim());\n if (needle.length === 0) return [];\n const out: TranscriptMatch[] = [];\n for (const segment of segments) {\n const hay = fold(segment.text);\n const ranges: [number, number][] = [];\n let from = 0;\n for (;;) {\n const at = hay.indexOf(needle, from);\n if (at < 0) break;\n ranges.push([at, at + needle.length]);\n from = at + needle.length;\n }\n const speaker = fold(displayNameFor(names, segment.identity, segment.speaker));\n if (ranges.length > 0 || speaker.includes(needle)) {\n out.push({ segmentId: segment.id, ranges });\n }\n }\n return out;\n}\n\n/** Lowercase, strip combining marks; length-preserving for the Latin range. */\nfunction fold(value: string): string {\n return value\n .normalize(\"NFD\")\n .replace(/[̀-ͯ]/g, (mark) => (mark.length === 1 ? \"\" : mark))\n .toLowerCase();\n}\n\n/* ───────────────────────── Documents ───────────────────────── */\n\nexport type TranscriptFormat = \"txt\" | \"md\" | \"vtt\";\n\ninterface ExportOptions {\n readonly names?: NameBook;\n /** Timestamps as a clock of day (default) or offsets from `zero`. */\n readonly locale?: string;\n readonly timeZone?: string;\n}\n\nfunction clockOfDay(iso: string, options: ExportOptions): string {\n const at = new Date(iso);\n if (Number.isNaN(at.getTime())) return \"\";\n return at.toLocaleTimeString(options.locale, {\n hour: \"numeric\",\n minute: \"2-digit\",\n second: \"2-digit\",\n ...(options.timeZone !== undefined ? { timeZone: options.timeZone } : {}),\n });\n}\n\nfunction vttStamp(offsetMs: number): string {\n const total = Math.max(0, Math.round(offsetMs));\n const h = Math.floor(total / 3_600_000);\n const m = Math.floor((total % 3_600_000) / 60_000);\n const s = Math.floor((total % 60_000) / 1000);\n const milli = total % 1000;\n return `${String(h).padStart(2, \"0\")}:${String(m).padStart(2, \"0\")}:${String(s).padStart(2, \"0\")}.${String(milli).padStart(3, \"0\")}`;\n}\n\n/**\n * The transcript as a document. `txt` and `md` group consecutive lines by one\n * speaker with the clock of day; `vtt` is WebVTT cued from the recording's\n * start when a recording exists (so it plays as captions over it), otherwise\n * from the meeting's start — and a cue ends where the next line begins.\n */\nexport function transcriptDocument(\n bundle: Pick<MeetingRecordBundle, \"meeting\" | \"transcript\" | \"recording\" | \"names\">,\n format: TranscriptFormat,\n options: ExportOptions = {},\n): string {\n const names = options.names ?? bundle.names;\n const lines = bundle.transcript.filter((line) => line.text.trim().length > 0);\n const speaker = (line: TranscriptSegment) => displayNameFor(names, line.identity, line.speaker);\n\n if (format === \"vtt\") {\n const zeroIso =\n bundle.recording?.state === \"available\" && bundle.recording.startedAt !== null\n ? bundle.recording.startedAt\n : (bundle.meeting.startedAt ?? lines[0]?.startedAt ?? null);\n const zero = ms(zeroIso) ?? 0;\n const cues = [\"WEBVTT\", \"\"];\n lines.forEach((line, index) => {\n const start = (ms(line.startedAt) ?? zero) - zero;\n if (start < 0) return;\n const next = lines[index + 1];\n const nextStart = next !== undefined ? (ms(next.startedAt) ?? zero) - zero : start + 4000;\n const end = Math.max(start + 500, Math.min(nextStart, start + 10_000));\n cues.push(`${vttStamp(start)} --> ${vttStamp(end)}`);\n cues.push(`<v ${speaker(line).replace(/[<>]/g, \"\")}>${line.text.replace(/-->/g, \"->\")}`);\n cues.push(\"\");\n });\n return cues.join(\"\\n\");\n }\n\n const out: string[] = [];\n const title = bundle.meeting.title;\n out.push(format === \"md\" ? `# ${title} — transcript` : `${title} — transcript`);\n out.push(\"\");\n let last: string | null = null;\n for (const line of lines) {\n if (line.identity !== last) {\n if (last !== null) out.push(\"\");\n const who = speaker(line);\n const clock = clockOfDay(line.startedAt, options);\n out.push(format === \"md\" ? `**${who}** · ${clock}` : `${who} (${clock})`);\n last = line.identity;\n }\n out.push(line.text.trim());\n }\n if (lines.length === 0) out.push(\"No transcript was captured for this meeting.\");\n return `${out.join(\"\\n\")}\\n`;\n}\n\nfunction bullet(note: MeetingNote): string {\n const owner = note.assigneeDisplayName !== null ? ` — ${note.assigneeDisplayName}` : \"\";\n return `- ${note.text.trim()}${owner}`;\n}\n\n/**\n * The meeting's notes as Markdown: when, who attended, the summary, decisions\n * and action items (with owners), and the link back. What a person pastes into\n * a doc or an email; also the source for PDF / Word export.\n */\nexport function notesDocument(\n bundle: Pick<\n MeetingRecordBundle,\n \"meeting\" | \"summary\" | \"decisions\" | \"actionItems\" | \"attendees\" | \"names\"\n >,\n options: { readonly link?: string | null; readonly when?: string | null } = {},\n): string {\n const out: string[] = [`# ${bundle.meeting.title}`, \"\"];\n if (options.when) out.push(`_${options.when}_`, \"\");\n // One name per PERSON — a rejoin is not a second attendee.\n const people = uniqueAttendees(\n bundle.attendees.filter((person) => !person.isAgent && person.joinedAt !== null),\n bundle.names,\n ).map((person) => displayNameFor(bundle.names, person.identity, person.displayName));\n if (people.length > 0) out.push(`**Attended:** ${people.join(\", \")}`, \"\");\n out.push(\"## Summary\", \"\", bundle.summary?.text.trim() || \"No summary was written.\", \"\");\n out.push(\"## Decisions\", \"\");\n out.push(...(bundle.decisions.length > 0 ? bundle.decisions.map(bullet) : [\"None recorded.\"]), \"\");\n out.push(\"## Action items\", \"\");\n out.push(\n ...(bundle.actionItems.length > 0 ? bundle.actionItems.map(bullet) : [\"None recorded.\"]),\n \"\",\n );\n if (options.link) out.push(`[Open the meeting record](${options.link})`, \"\");\n return out.join(\"\\n\");\n}\n\n/** How long the meeting ran, in words (\"42 minutes\"), or null when unknown. */\nexport function meetingLength(meeting: MeetingRecord): string | null {\n const start = ms(meeting.startedAt);\n const end = ms(meeting.endedAt);\n if (start === null || end === null || end < start) return null;\n return formatDurationMs(end - start, { style: \"long\" });\n}\n\n/* ───────────────────────── Saved chat ───────────────────────── */\n\nexport interface MeetingChatHistory {\n readonly messages: readonly RoomChatMessage[];\n /** The server's refusal, in its words, when the history could not be read. */\n readonly refusal: { readonly message: string; readonly remedy: string } | null;\n}\n\n/**\n * The saved chat of a finished meeting (`GET /v1/meet/chat`). Private messages\n * are never saved, by design, so they are never here. A refusal is returned as\n * data — the record still renders everything else.\n */\nexport async function loadMeetingChat(\n api: MeetApi,\n meetingId: MeetingId,\n meetingPass: string | null = null,\n): Promise<MeetingChatHistory> {\n const operation = \"meet.loadMeetingChat\";\n let response: Response;\n try {\n response = await api.request({\n path: `${MEET_ROUTES.chat}?meeting_id=${encodeURIComponent(meetingId)}`,\n method: \"GET\",\n operation,\n requireAuth: meetingPass === null,\n ...(meetingPass !== null ? { headers: { [MEETING_PASS_HEADER]: meetingPass } } : {}),\n });\n } catch (error) {\n return {\n messages: [],\n refusal: {\n message: `The chat could not be read (${(error as Error).message}).`,\n remedy: (error as { remedy?: string }).remedy ?? \"Reload the page.\",\n },\n };\n }\n if (!response.ok) {\n let body: unknown = null;\n try {\n body = await response.json();\n } catch {\n body = null;\n }\n const said = readServerRefusal(body);\n return {\n messages: [],\n refusal: {\n message: said?.reason ?? `The chat could not be read (the server answered ${response.status}).`,\n remedy: said?.remedy || \"Reload the page.\",\n },\n };\n }\n const payload = (await response.json()) as { messages?: unknown };\n const rows = Array.isArray(payload.messages) ? payload.messages : [];\n return {\n messages: rows\n .map((row) =>\n typeof row === \"object\" && row !== null ? chatFromRow(row as Record<string, unknown>) : null,\n )\n .filter((message): message is RoomChatMessage => message !== null),\n refusal: null,\n };\n}\n\n/* ───────────────────────── The recap email ───────────────────────── */\n\n/** One person the recap can go to, as the server resolved them. */\nexport interface FollowUpRecipient {\n /** Stable key for the choice: `user:<id>` or `email:<address>`. */\n readonly key: string;\n readonly name: string;\n /** The address, when this reader may see it (the host sees their guests'). */\n readonly email: string | null;\n readonly userId: string | null;\n /** `invitee`, `attendee` or both — where the server found them. */\n readonly sources: readonly (\"invitee\" | \"attendee\")[];\n /** False when the server has no way to reach them (no account, no address). */\n readonly reachable: boolean;\n}\n\n/** The draft the host reviews. The text is the wrap-up's own, arranged. */\nexport interface FollowUpDraft {\n readonly subject: string;\n readonly body: string;\n readonly recipients: readonly FollowUpRecipient[];\n /** False while the wrap-up has not been written — the draft says so. */\n readonly wrapUpWritten: boolean;\n /** When a recap for this meeting was last sent, and to how many. */\n readonly lastSent: { readonly at: string; readonly count: number } | null;\n}\n\nexport interface FollowUpSendResult {\n readonly dryRun: boolean;\n /** Recipients the send queued (or, on a dry run, would queue). */\n readonly queued: readonly string[];\n /** Recipient keys skipped, each with the reason. */\n readonly skipped: readonly { readonly key: string; readonly reason: string }[];\n}\n\nfunction refusalOf(response: Response, operation: string): Promise<MeetError> {\n return refusalFromResponse(response, operation, \"Nothing was sent. Try again.\");\n}\n\nfunction asRecipient(row: Record<string, unknown>): FollowUpRecipient {\n const sources = Array.isArray(row[\"sources\"]) ? (row[\"sources\"] as string[]) : [];\n return {\n key: String(row[\"key\"] ?? \"\"),\n name: String(row[\"name\"] ?? \"\"),\n email: typeof row[\"email\"] === \"string\" ? row[\"email\"] : null,\n userId: typeof row[\"user_id\"] === \"string\" ? row[\"user_id\"] : null,\n sources: sources.filter((s): s is \"invitee\" | \"attendee\" => s === \"invitee\" || s === \"attendee\"),\n reachable: row[\"reachable\"] !== false,\n };\n}\n\n/** The recap draft for a finished meeting. The host, a co-host or an editor. */\nexport async function loadFollowUpDraft(api: MeetApi, meetingId: MeetingId): Promise<FollowUpDraft> {\n const operation = \"meet.loadFollowUpDraft\";\n const response = await api.request({\n path: meetingPath(MEET_ROUTES.meetingFollowUp, meetingId),\n method: \"GET\",\n requireAuth: true,\n operation,\n });\n if (!response.ok) throw await refusalOf(response, operation);\n const body = (await response.json()) as Record<string, unknown>;\n const last = body[\"last_sent\"] as Record<string, unknown> | null | undefined;\n return {\n subject: String(body[\"subject\"] ?? \"\"),\n body: String(body[\"body\"] ?? \"\"),\n recipients: (Array.isArray(body[\"recipients\"]) ? body[\"recipients\"] : []).map((row) =>\n asRecipient(row as Record<string, unknown>),\n ),\n wrapUpWritten: body[\"wrap_up_written\"] === true,\n lastSent:\n last && typeof last[\"at\"] === \"string\"\n ? { at: last[\"at\"], count: typeof last[\"count\"] === \"number\" ? last[\"count\"] : 0 }\n : null,\n };\n}\n\n/**\n * Send the recap the host reviewed. `dryRun` resolves every recipient and\n * renders every email but queues nothing — how a test proves the path without\n * mailing a real inbox. Never called automatically.\n */\nexport async function sendFollowUp(\n api: MeetApi,\n meetingId: MeetingId,\n input: {\n readonly subject: string;\n readonly body: string;\n readonly recipientKeys: readonly string[];\n readonly dryRun?: boolean;\n },\n): Promise<FollowUpSendResult> {\n const operation = \"meet.sendFollowUp\";\n const response = await api.request({\n path: meetingPath(MEET_ROUTES.meetingFollowUp, meetingId),\n method: \"POST\",\n requireAuth: true,\n operation,\n body: {\n subject: input.subject,\n body: input.body,\n recipient_keys: [...input.recipientKeys],\n dry_run: input.dryRun === true,\n },\n });\n if (!response.ok) throw await refusalOf(response, operation);\n const body = (await response.json()) as Record<string, unknown>;\n return {\n dryRun: body[\"dry_run\"] === true,\n queued: Array.isArray(body[\"queued\"]) ? (body[\"queued\"] as string[]) : [],\n skipped: Array.isArray(body[\"skipped\"])\n ? (body[\"skipped\"] as Record<string, unknown>[]).map((row) => ({\n key: String(row[\"key\"] ?? \"\"),\n reason: String(row[\"reason\"] ?? \"\"),\n }))\n : [],\n };\n}\n","/**\n * MEET WAVE 1 — the two server calls a management screen makes (0.8).\n *\n * Every WRITE (schedule, edit, reschedule, cancel, archive, occurrence change,\n * invite, answer) goes straight to the database through `MeetRepository` — the\n * database decides who may. What a browser cannot do is send an email with a\n * calendar object in it, so after any of those writes a screen calls\n * `announceMeeting`: the server tells each invitee exactly what they have not\n * been told (invitation, update or cancellation, each with its `.ics`), and\n * re-queues reminders. It is idempotent — call it after every save.\n */\n\nimport type { MeetApi } from \"./api\";\nimport { refusalFromResponse } from \"./ai\";\nimport { MeetError } from \"./errors\";\nimport { MEET_ROUTES, meetingPath } from \"./tokens\";\nimport type { MeetingId } from \"./types\";\n\nexport interface AnnounceResult {\n readonly invitations: number;\n readonly updates: number;\n readonly cancellations: number;\n readonly alreadyCurrent: number;\n readonly remindersQueued: number;\n readonly remindersCancelled: number;\n}\n\nfunction refusal(response: Response, operation: string): Promise<MeetError> {\n return refusalFromResponse(\n response,\n operation,\n \"The meeting itself is saved — only the telling failed. Try again; the call is idempotent.\",\n );\n}\n\nfunction count(body: Record<string, unknown>, key: string): number {\n const value = body[key];\n return typeof value === \"number\" ? value : 0;\n}\n\n/** Bring every invitee's inbox and calendar up to date. The host, a co-host or an editor. */\nexport async function announceMeeting(api: MeetApi, meetingId: MeetingId): Promise<AnnounceResult> {\n const operation = \"meet.announceMeeting\";\n const response = await api.request({\n path: meetingPath(MEET_ROUTES.meetingAnnounce, meetingId),\n method: \"POST\",\n requireAuth: true,\n operation,\n });\n if (!response.ok) throw await refusal(response, operation);\n const body = (await response.json()) as Record<string, unknown>;\n return {\n invitations: count(body, \"invitations\"),\n updates: count(body, \"updates\"),\n cancellations: count(body, \"cancellations\"),\n alreadyCurrent: count(body, \"already_current\"),\n remindersQueued: count(body, \"reminders_queued\"),\n remindersCancelled: count(body, \"reminders_cancelled\"),\n };\n}\n\n/**\n * Put a one-off meeting on the caller's own connected Google Calendar. GATED by\n * the server to Google's review accounts while the Calendar scope is in review —\n * a refusal is a `forbidden` MeetError carrying the server's sentence. Everyone\n * already gets the meeting into any calendar through the invitation's `.ics`.\n */\nexport async function pushMeetingToGoogleCalendar(\n api: MeetApi,\n meetingId: MeetingId,\n target: { connectionId: string; calendarId?: string },\n): Promise<Record<string, unknown>> {\n const operation = \"meet.pushMeetingToGoogleCalendar\";\n const response = await api.request({\n path: meetingPath(MEET_ROUTES.meetingGoogleCalendar, meetingId),\n method: \"POST\",\n requireAuth: true,\n operation,\n body: { connection_id: target.connectionId, calendar_id: target.calendarId ?? \"primary\" },\n });\n if (!response.ok) throw await refusal(response, operation);\n return (await response.json()) as Record<string, unknown>;\n}\n","/**\n * MS — THE MEDIA SUPERVISOR (CORE-DESIGN §3.3, step S6c).\n *\n * Owns the media regions of §3.2 that are about THIS person's devices and speakers:\n *\n * - **Permission per kind.** The browser's error alone cannot tell a person's Block from an\n * operating-system refusal or a stale device id (`NotAllowedError` is all three in Chrome), so\n * every refusal is classified against the Permissions API: `denied` there is the person's (or\n * the site's) Block; a refusal while the browser says `granted` is the operating system. A kind\n * whose permission is `denied` reads `unavailable:denied` even when nothing tried to open it —\n * a camera the browser will not hand over is not \"off by choice\".\n * - **Live device switching.** LiveKit falls back to the default for a lost MICROPHONE by itself\n * but MUTES a lost camera (client-sdk-js #2035). On every device change the supervisor checks\n * the devices in use against the devices present: a vanished camera moves to the next camera;\n * a vanished microphone is reported with the device LiveKit moved to; nothing left of a kind is\n * `unavailable:none`. Each is a notice — never a silent switch. A NEW device is offered, never\n * taken (a person mid-sentence does not want their mic to jump to a headset being charged).\n * - **Autoplay.** `audio_output` = `blocked` while the browser holds remote audio until a gesture;\n * `enableAudio()` runs inside the person's click; the check is repeated when the tab comes back.\n *\n * 🚨 NOTHING HERE CHANGES THE CALL PHASE (§3.1 invariant). Every outcome is region state.\n * 🚨 NO BROWSER GLOBAL IS TOUCHED HERE: the browser half is the `MediaPlatform` port (§3.5) —\n * `webMediaPlatform()` below is the web adapter; a React Native host passes its own.\n */\n\nimport type {\n DevicePreview,\n DeviceUnavailableReason,\n LocalTrackState,\n MediaRegionState,\n MediaUnavailable,\n PermissionReading,\n} from \"./attendance\";\nimport { unavailableFrom } from \"./attendance\";\nimport { normalizeDeviceError, type MeetError } from \"./errors\";\nimport type { MeetRoomDriver } from \"./room-driver\";\nimport type { MeetStore } from \"./store\";\n\nexport type MediaKind = \"microphone\" | \"camera\";\nexport type BrowserFamily = \"chrome\" | \"edge\" | \"firefox\" | \"safari\" | \"other\";\nexport type OsFamily = \"mac\" | \"windows\" | \"ios\" | \"android\" | \"linux\" | \"other\";\n\nexport interface PlatformDevice {\n readonly deviceId: string;\n readonly kind: string;\n readonly label: string;\n}\n\n/** The slice of §3.5's `MeetPlatform` the media supervisor needs. */\nexport interface MediaPlatform {\n readonly browser: BrowserFamily;\n readonly os: OsFamily;\n /** The Permissions API reading, or `unknown` where the browser has none for this kind. */\n queryPermission(kind: MediaKind): Promise<PermissionReading>;\n /** Call `onChange` whenever the permission for `kind` changes. Returns the unsubscribe. */\n watchPermission(kind: MediaKind, onChange: (reading: PermissionReading) => void): () => void;\n listDevices(): Promise<readonly PlatformDevice[]>;\n /** The tab became visible again (autoplay is re-checked then). Returns the unsubscribe. */\n onVisible(callback: () => void): () => void;\n}\n\n/* ------------------------------------------------------------ classification */\n\n/**\n * The region reason for a refused device, from the error AND the permission store.\n * Pure — the rules are the tests.\n */\nexport function classifyDeviceFailure(\n error: { readonly code: string; readonly message: string },\n permission: PermissionReading,\n): DeviceUnavailableReason {\n if (permission === \"denied\") return \"denied\";\n switch (error.code) {\n case \"permission-denied\":\n // Chrome names an OS refusal (\"Permission denied by system\"); everywhere else a refusal\n // while the browser itself says `granted` can only be the operating system.\n if (/by system/i.test(error.message) || permission === \"granted\") return \"blocked_os\";\n return \"denied\";\n case \"device-not-found\":\n return \"none\";\n case \"device-in-use\":\n return \"busy\";\n case \"insecure-context\":\n return \"blocked_site\";\n default:\n return \"failed\";\n }\n}\n\n/** The contract notice code for an unusable device (HARNESS-CONTRACT §4). */\nexport function deviceNoticeCode(kind: MediaKind, reason: DeviceUnavailableReason): \"mic-blocked\" | \"camera-blocked\" | \"device-lost\" {\n if (reason === \"denied\" || reason === \"blocked_site\" || reason === \"blocked_os\") {\n return kind === \"microphone\" ? \"mic-blocked\" : \"camera-blocked\";\n }\n return \"device-lost\";\n}\n\n/**\n * The region reason this track is in, or null when the device is usable (on, or off by choice).\n * A kind the browser's permission store denies is `denied` even when nothing tried to open it.\n */\nexport function deviceUnavailableReason(track: LocalTrackState, permission: PermissionReading): DeviceUnavailableReason | null {\n if (track.enabled) return null;\n if (permission === \"denied\") return \"denied\";\n const refused = track.unavailable;\n if (refused === null) return null;\n return refused.reason ?? classifyDeviceFailure({ code: refused.code, message: refused.message }, permission);\n}\n\n/** The short chip text — the skin's words may differ; the reason is the fact. */\nexport function deviceChipLabel(kind: MediaKind, reason: DeviceUnavailableReason): string {\n const what = kind === \"microphone\" ? \"Mic\" : \"Camera\";\n switch (reason) {\n case \"denied\":\n case \"blocked_site\":\n case \"blocked_os\":\n return `${what} blocked`;\n case \"none\":\n return kind === \"microphone\" ? \"No microphone found\" : \"No camera found\";\n case \"busy\":\n return `${what} in use by another app`;\n case \"revoked\":\n return `${what} access was turned off`;\n default:\n return `${what} didn't start`;\n }\n}\n\n/**\n * THE REAL REMEDY, per browser and operating system: the steps a person actually follows. Short\n * lines (interface text is layout); the last step is always the Try again the screen offers.\n */\nexport function deviceFixSteps(\n kind: MediaKind,\n reason: DeviceUnavailableReason,\n where: { readonly browser: BrowserFamily; readonly os: OsFamily },\n): readonly string[] {\n const device = kind === \"microphone\" ? \"Microphone\" : \"Camera\";\n const lower = device.toLowerCase();\n switch (reason) {\n case \"denied\":\n case \"revoked\":\n case \"blocked_site\":\n switch (where.browser) {\n case \"firefox\":\n return [`Click the crossed-out ${lower} icon in the address bar`, \"Clear the Blocked setting\", \"Press Try again and choose Allow\"];\n case \"safari\":\n return where.os === \"ios\"\n ? [`Settings › Apps › Safari › ${device}`, \"Choose Allow\", \"Return here and press Try again\"]\n : [\"Safari › Settings for This Website\", `Set ${device} to Allow`, \"Press Try again\"];\n default:\n return [\"Click the site settings icon in the address bar\", `Set ${device} to Allow`, \"Press Try again\"];\n }\n case \"blocked_os\": {\n const app = where.browser === \"chrome\" ? \"Google Chrome\" : where.browser === \"edge\" ? \"Microsoft Edge\" : where.browser === \"firefox\" ? \"Firefox\" : \"your browser\";\n if (where.os === \"mac\") return [`System Settings › Privacy & Security › ${device}`, `Turn on ${app}`, \"Reopen the browser if asked, then Try again\"];\n if (where.os === \"windows\") return [`Settings › Privacy & security › ${device}`, `Turn on ${device} access for apps`, \"Press Try again\"];\n return [`Allow ${lower} access for ${app} in your system settings`, \"Press Try again\"];\n }\n case \"busy\":\n return [`Close other apps using the ${lower} (Zoom, Teams, another tab)`, \"Press Try again\"];\n case \"none\":\n return [`Connect a ${lower}`, \"Press Try again\"];\n default:\n return [`Pick another ${lower} in Settings`, \"Press Try again\"];\n }\n}\n\n/* ------------------------------------------------------------ the web adapter */\n\nexport function detectBrowser(userAgent: string): BrowserFamily {\n if (/Edg\\//.test(userAgent)) return \"edge\";\n if (/Firefox\\//.test(userAgent)) return \"firefox\";\n if (/Chrome\\/|Chromium\\/|CriOS\\//.test(userAgent)) return \"chrome\";\n if (/Safari\\//.test(userAgent)) return \"safari\";\n return \"other\";\n}\n\nexport function detectOs(userAgent: string, maxTouchPoints = 0): OsFamily {\n if (/iPhone|iPad|iPod/.test(userAgent)) return \"ios\";\n // iPadOS reports a Mac user agent; touch points give it away (catalog `mobile-ipad-detect`).\n if (/Macintosh/.test(userAgent)) return maxTouchPoints > 1 ? \"ios\" : \"mac\";\n if (/Android/.test(userAgent)) return \"android\";\n if (/Windows/.test(userAgent)) return \"windows\";\n if (/Linux|CrOS/.test(userAgent)) return \"linux\";\n return \"other\";\n}\n\ninterface PermissionStatusLike {\n state: string;\n onchange: (() => void) | null;\n}\n\n/** The browser adapter, or null outside a browser (SSR, a test, React Native without one). */\nexport function webMediaPlatform(): MediaPlatform | null {\n const scope = globalThis as unknown as {\n navigator?: {\n userAgent?: string;\n maxTouchPoints?: number;\n permissions?: { query(descriptor: { name: string }): Promise<PermissionStatusLike> };\n mediaDevices?: { enumerateDevices(): Promise<readonly PlatformDevice[]> };\n };\n document?: { visibilityState?: string; addEventListener(t: string, f: () => void): void; removeEventListener(t: string, f: () => void): void };\n };\n const nav = scope.navigator;\n if (nav === undefined) return null;\n const ua = nav.userAgent ?? \"\";\n const reading = (state: string | undefined): PermissionReading =>\n state === \"granted\" || state === \"denied\" || state === \"prompt\" ? state : \"unknown\";\n const query = async (kind: MediaKind): Promise<PermissionStatusLike | null> => {\n try {\n return (await nav.permissions?.query({ name: kind })) ?? null;\n } catch {\n // Firefox before 2024 and older Safari have no camera/microphone descriptor.\n return null;\n }\n };\n return {\n browser: detectBrowser(ua),\n os: detectOs(ua, nav.maxTouchPoints ?? 0),\n async queryPermission(kind) {\n return reading((await query(kind))?.state);\n },\n watchPermission(kind, onChange) {\n let status: PermissionStatusLike | null = null;\n let stopped = false;\n void query(kind).then((found) => {\n if (stopped || found === null) return;\n status = found;\n found.onchange = () => onChange(reading(found.state));\n });\n return () => {\n stopped = true;\n if (status !== null) status.onchange = null;\n };\n },\n async listDevices() {\n try {\n return (await nav.mediaDevices?.enumerateDevices()) ?? [];\n } catch {\n return [];\n }\n },\n onVisible(callback) {\n const doc = scope.document;\n if (doc === undefined) return () => undefined;\n const handler = (): void => {\n if (doc.visibilityState === \"visible\") callback();\n };\n doc.addEventListener(\"visibilitychange\", handler);\n return () => doc.removeEventListener(\"visibilitychange\", handler);\n },\n };\n}\n\n/* ------------------------------------------------------------ the supervisor */\n\nexport interface MediaSupervisorOptions {\n readonly store: MeetStore;\n readonly platform: MediaPlatform | null;\n /** The live room's driver, or null between rooms. */\n readonly driver: () => MeetRoomDriver | null;\n readonly report: (level: \"info\" | \"warn\" | \"error\", message: string, remedy?: string) => void;\n}\n\nexport interface MediaSupervisor {\n /** Begin watching permissions and visibility (idempotent). */\n start(): void;\n stop(): void;\n /** The `MediaUnavailable` for a refused device: the browser's words + the region reason. */\n unavailable(error: unknown, kind: MediaKind, operation: string): Promise<MediaUnavailable>;\n /** Driver handlers (wired by the engine). */\n onAudioPlayback(canPlay: boolean): void;\n onDevicesChanged(): void;\n onDeviceError(error: unknown, kind: \"audioinput\" | \"videoinput\" | null): void;\n /** The engine connected a room: remember which devices are in use now. */\n onConnected(): void;\n /** The room is gone: autoplay and device notices belong to the room. */\n onDisconnected(): void;\n /** \"Enable sound\" — call from inside the person's click. */\n enableAudio(): Promise<void>;\n /**\n * The pre-join preview's outcome for one kind: `prompting` when it asks the browser, `on` / `off`\n * when it settles or is turned off, or the error it was refused with (classified here, against\n * the permission store, exactly as an in-room refusal is).\n */\n preview(kind: MediaKind, outcome: \"prompting\" | \"on\" | \"off\" | { readonly error: unknown }): Promise<void>;\n /** Use the offered new device. */\n acceptOffer(): Promise<void>;\n dismissOffer(): void;\n dismissNotice(): void;\n /** Undo on a \"switched\" notice: go back to the device that went away, when it is available again. */\n undoNotice(): Promise<void>;\n}\n\nconst INPUT_KIND: Record<MediaKind, \"audioinput\" | \"videoinput\"> = { microphone: \"audioinput\", camera: \"videoinput\" };\n\n/** A browser's pseudo-devices: they follow the system choice and are never a \"new device\". */\nfunction isAlias(deviceId: string): boolean {\n return deviceId === \"default\" || deviceId === \"communications\" || deviceId === \"\";\n}\n\nexport function createMediaSupervisor(options: MediaSupervisorOptions): MediaSupervisor {\n const { store, platform } = options;\n let unsubscribers: (() => void)[] = [];\n let started = false;\n let noticeId = 0;\n /** The devices present at the last look, per input kind (ids → label). */\n let known: Record<\"audioinput\" | \"videoinput\", Map<string, string>> | null = null;\n /** The device in use per kind at the last look (to name what went away). */\n let inUse: Record<\"audioinput\" | \"videoinput\", string | null> = { audioinput: null, videoinput: null };\n\n function region(): MediaRegionState {\n return store.snapshot().mediaRegion;\n }\n function setRegion(next: Partial<MediaRegionState>): void {\n const current = region();\n const merged = { ...current, ...next };\n if (\n merged.audioOutput === current.audioOutput &&\n merged.notice === current.notice &&\n merged.offer === current.offer &&\n merged.preview === current.preview &&\n merged.permission.microphone === current.permission.microphone &&\n merged.permission.camera === current.permission.camera\n ) {\n return;\n }\n store.patch({ mediaRegion: merged });\n }\n\n function setPermission(kind: MediaKind, reading: PermissionReading): void {\n const current = region().permission;\n if (current[kind] === reading) return;\n setRegion({ permission: { ...current, [kind]: reading } });\n // Allowed again while the device sits refused: say what is true now (the person presses\n // Try again — LiveKit itself resumes a REVOKED track on re-grant).\n const track = store.snapshot().media[kind];\n if (reading === \"granted\" && !track.enabled && track.unavailable?.reason === \"denied\") {\n store.setLocalTrack(kind, {\n enabled: false,\n unavailable: { ...track.unavailable, reason: \"failed\", remedy: \"Access is allowed now. Press Try again.\" },\n });\n }\n }\n\n async function snapshotDevices(): Promise<Record<\"audioinput\" | \"videoinput\", Map<string, string>>> {\n const list = platform === null ? [] : await platform.listDevices();\n const out = { audioinput: new Map<string, string>(), videoinput: new Map<string, string>() };\n for (const device of list) {\n if (device.kind === \"audioinput\" || device.kind === \"videoinput\") out[device.kind].set(device.deviceId, device.label);\n }\n return out;\n }\n\n function readInUse(): void {\n const driver = options.driver();\n if (driver === null) return;\n const active = driver.activeDevices();\n inUse = { audioinput: active.audioinput, videoinput: active.videoinput };\n }\n\n async function handleDevicesChanged(): Promise<void> {\n if (platform === null) return;\n const before = known;\n const now = await snapshotDevices();\n known = now;\n const driver = options.driver();\n if (driver === null || before === null) {\n readInUse();\n return;\n }\n for (const kind of [\"microphone\", \"camera\"] as const) {\n const deviceKind = INPUT_KIND[kind];\n const previous = inUse[deviceKind];\n const track = store.snapshot().media[kind];\n // An alias (\"default\") is the device the system points it at: it is gone when it left the list or now names a different device.\n const aliasLabel = previous !== null && isAlias(previous) ? before[deviceKind].get(previous) : undefined;\n const aliasGone =\n previous !== null && aliasLabel !== undefined && aliasLabel !== \"\" && (!now[deviceKind].has(previous) || now[deviceKind].get(previous) !== aliasLabel);\n const gone = aliasGone || (previous !== null && !isAlias(previous) && before[deviceKind].has(previous) && !now[deviceKind].has(previous));\n if (gone) {\n const from = before[deviceKind].get(previous) ?? \"\";\n const remaining = [...now[deviceKind].entries()].filter(([id]) => !isAlias(id));\n if (remaining.length === 0) {\n // Nothing of this kind is left: the device is unusable, not \"off\".\n store.setLocalTrack(kind, {\n enabled: false,\n unavailable: {\n code: \"device-not-found\",\n message: `meet.${kind}: the ${kind} was disconnected and no other is connected`,\n remedy: `Connect a ${kind === \"microphone\" ? \"microphone\" : \"camera\"}, then press Try again.`,\n reason: \"none\",\n },\n });\n setRegion({ notice: { id: ++noticeId, kind, outcome: \"lost\", from, to: null } });\n continue;\n }\n if (kind === \"camera\") {\n // LiveKit mutes a lost camera (#2035): the supervisor moves it, keeping on/off as it was.\n const [nextId, nextLabel] = remaining[0]!;\n try {\n await driver.switchDevice(\"videoinput\", nextId);\n if (track.enabled || track.unavailable !== null) {\n await driver.setCameraEnabled(true);\n store.setLocalTrack(\"camera\", { enabled: true, unavailable: null });\n }\n setRegion({ notice: { id: ++noticeId, kind, outcome: \"switched\", from, fromId: previous!, to: nextLabel } });\n } catch (error) {\n const refused = await unavailable(error, \"camera\", \"meet.camera(fallback)\");\n store.setLocalTrack(\"camera\", { enabled: false, unavailable: refused });\n setRegion({ notice: { id: ++noticeId, kind, outcome: \"lost\", from, to: null } });\n }\n } else {\n // LiveKit restarts the microphone on the default device when it can; when the default went with it,\n // the supervisor moves it to what is left, keeping on/off as it was (device-unplugged-midcall).\n const active = driver.activeDevices().audioinput;\n let to = (active !== null && !aliasGone ? now.audioinput.get(active) : undefined) ?? now.audioinput.get(\"default\");\n if (to === undefined || aliasGone) {\n const [nextId, nextLabel] = remaining[0]!;\n try {\n await driver.switchDevice(\"audioinput\", nextId);\n if (track.enabled || track.unavailable !== null) {\n await driver.setMicrophoneEnabled(true);\n store.setLocalTrack(\"microphone\", { enabled: true, unavailable: null });\n }\n to = nextLabel;\n } catch (error) {\n const refused = await unavailable(error, \"microphone\", \"meet.microphone(fallback)\");\n store.setLocalTrack(\"microphone\", { enabled: false, unavailable: refused });\n setRegion({ notice: { id: ++noticeId, kind, outcome: \"lost\", from, to: null } });\n continue;\n }\n }\n setRegion({ notice: { id: ++noticeId, kind, outcome: \"switched\", from, fromId: previous!, to } });\n }\n continue;\n }\n // A NEW device appeared: offer it, never switch silently.\n const added = [...now[deviceKind].entries()].find(([id]) => !isAlias(id) && !before[deviceKind].has(id));\n if (added !== undefined && added[0] !== driver.activeDevices()[deviceKind]) {\n setRegion({ offer: { kind, deviceId: added[0], label: added[1] } });\n }\n }\n readInUse();\n }\n\n async function unavailable(error: unknown, kind: MediaKind, operation: string): Promise<MediaUnavailable> {\n const normalized = normalizeDeviceError(error, operation);\n const permission = platform === null ? \"unknown\" : await platform.queryPermission(kind);\n if (permission !== \"unknown\") setPermission(kind, permission);\n return unavailableFrom(normalized, classifyDeviceFailure(normalized, permission));\n }\n\n const api: MediaSupervisor = {\n start() {\n if (started || platform === null) return;\n started = true;\n for (const kind of [\"microphone\", \"camera\"] as const) {\n void platform.queryPermission(kind).then((reading) => setPermission(kind, reading));\n unsubscribers.push(platform.watchPermission(kind, (reading) => setPermission(kind, reading)));\n }\n unsubscribers.push(\n platform.onVisible(() => {\n // Safari re-suspends audio after a background stint (LiveKit #1751): try again on\n // return. If the browser still refuses, the Enable sound offer simply stays.\n const driver = options.driver();\n if (driver?.startAudio === undefined || region().audioOutput !== \"blocked\") return;\n driver.startAudio().catch(() => undefined);\n }),\n );\n },\n stop() {\n for (const off of unsubscribers) off();\n unsubscribers = [];\n started = false;\n },\n unavailable,\n async preview(kind, outcome) {\n // Reading the permission store before the room exists is what lets the pre-join say\n // \"blocked\" (not \"didn't start\") — start() is idempotent.\n api.start();\n let next: DevicePreview;\n if (typeof outcome === \"string\") next = { state: outcome, unavailable: null };\n else next = { state: \"unavailable\", unavailable: await unavailable(outcome.error, kind, `meet.${kind}Preview`) };\n const current = region().preview;\n const was = current[kind];\n if (was.state === next.state && was.unavailable?.reason === next.unavailable?.reason && was.unavailable?.message === next.unavailable?.message) return;\n setRegion({ preview: { ...region().preview, [kind]: next } });\n },\n onAudioPlayback(canPlay) {\n setRegion({ audioOutput: canPlay ? \"ok\" : \"blocked\" });\n },\n onDevicesChanged() {\n void handleDevicesChanged().catch((error: unknown) => {\n options.report(\"warn\", `meet: device change could not be handled: ${String(error)}`, \"Pick the device in Settings.\");\n });\n },\n onDeviceError(error, deviceKind) {\n if (deviceKind === null) return;\n const kind: MediaKind = deviceKind === \"audioinput\" ? \"microphone\" : \"camera\";\n void unavailable(error, kind, `meet.${kind}`).then((refused) => {\n const track = store.snapshot().media[kind];\n if (track.enabled) return; // the SDK recovered on its own\n store.setLocalTrack(kind, { enabled: false, unavailable: refused });\n });\n },\n onConnected() {\n void snapshotDevices().then((now) => {\n known = now;\n readInUse();\n });\n },\n onDisconnected() {\n known = null;\n inUse = { audioinput: null, videoinput: null };\n setRegion({ audioOutput: \"ok\", notice: null, offer: null });\n },\n async enableAudio() {\n const driver = options.driver();\n if (driver?.startAudio === undefined) return;\n try {\n await driver.startAudio();\n setRegion({ audioOutput: \"ok\" });\n } catch (error) {\n options.report(\"warn\", `meet: sound could not be enabled: ${String(error)}`, \"Click Enable sound again, or check the tab is not muted.\");\n }\n },\n async acceptOffer() {\n const offer = region().offer;\n const driver = options.driver();\n setRegion({ offer: null });\n if (offer === null || driver === null) return;\n try {\n await driver.switchDevice(INPUT_KIND[offer.kind], offer.deviceId);\n readInUse();\n } catch (error) {\n const refused = await unavailable(error, offer.kind, \"meet.switchDevice\");\n options.report(\"warn\", refused.message, refused.remedy);\n }\n },\n dismissOffer() {\n setRegion({ offer: null });\n },\n dismissNotice() {\n setRegion({ notice: null });\n },\n async undoNotice() {\n const notice = region().notice;\n setRegion({ notice: null });\n const driver = options.driver();\n if (notice === null || notice.outcome !== \"switched\" || notice.fromId === undefined || driver === null) return;\n const list = await snapshotDevices();\n if (!list[INPUT_KIND[notice.kind]].has(notice.fromId)) return;\n try {\n await driver.switchDevice(INPUT_KIND[notice.kind], notice.fromId);\n readInUse();\n } catch (error) {\n const refused = await unavailable(error, notice.kind, \"meet.undoDeviceSwitch\");\n store.setLocalTrack(notice.kind, { enabled: false, unavailable: refused });\n }\n },\n };\n return api;\n}\n\n","/**\n * THE ROOM ENGINE — the object a host mounts once and never reasons about.\n *\n * It owns every sequence a calling product gets wrong: ask for a token, connect,\n * project the roster, carry the in-room control plane, keep the phase honest\n * through a reconnect, and tear everything down exactly once. A consumer's job\n * is `engine.join(...)` and rendering a snapshot.\n *\n * THREE rules are structural here, not documented-and-hoped:\n *\n * 1. **The phase never lies.** `connecting` becomes `connected` only when the\n * driver says so, and any failure becomes `failed` WITH the remedy attached\n * to the snapshot. There is no path that leaves a spinner running forever —\n * the single most common complaint about every video product.\n * 2. **Role comes from the TOKEN, never from the client.** A participant's role\n * is read out of the server-minted metadata. A client that could decide it\n * was host could mute anyone; here the claim is unforgeable because the\n * server signed it.\n * 3. **Teardown is idempotent and total.** Leaving twice, or unmounting during\n * a connect, disconnects once and leaves no listener behind. The half-torn\n * room is what produces \"my camera light stays on after the call\".\n */\n\nimport {\n IDLE_TRACK,\n type LocalTrackState,\n} from \"./attendance\";\nimport { detectCapabilities } from \"./capabilities\";\nimport { createMediaSupervisor, type MediaPlatform, webMediaPlatform } from \"./media-supervisor\";\nimport {\n classifyScreenShareError,\n MeetError,\n normalizeDeviceError,\n normalizeMeetError,\n normalizeScreenShareError,\n SCREEN_SHARE_BLOCKED_REMEDY,\n} from \"./errors\";\n\n/** A system/policy block: the error's own remedy when it names one (policy), else the system one. */\nfunction SCREEN_SHARE_BLOCKED_REMEDY_FOR(remedy: string): string {\n return /administrator|policy/i.test(remedy) ? remedy : SCREEN_SHARE_BLOCKED_REMEDY;\n}\nimport { applyRecordingState, applyRoomEvent, isRoomEventTopic, ROOM_EVENTS } from \"./room-events\";\nimport { displayNameFor, personOf } from \"./names\";\nimport type { TabClaim } from \"./tab-claim\";\nimport { noteTakerIdentity } from \"./note-taker\";\nimport { captionSegment } from \"./transcript\";\nimport {\n DATA_TOPICS,\n type DriverParticipant,\n type MeetRoomDriver,\n type RoomDriverFactory,\n type RoomDriverHandlers,\n type VideoEffect,\n} from \"./room-driver\";\nimport { type MediaEffects, type MeetStore, NO_SHARE, UNKNOWN_LOCAL_NETWORK, type LocalNetwork } from \"./store\";\nimport {\n ALIVE_TOPIC,\n createLivenessTracker,\n createSelfAssessor,\n createVideoAutoOff,\n decodeBeat,\n encodeBeat,\n heartbeatIntervalMs,\n pathOf,\n worstQuality,\n} from \"./network-health\";\nimport { resolveExit, type ExitServerWord } from \"./phase\";\nimport {\n createConnectionSupervisor,\n createWebNetworkPort,\n DEFAULT_CONNECTION_POLICY,\n isUnannouncedDeparture,\n mergeGhosts,\n type ConnectionPolicy,\n type Ghost,\n type NetworkPort,\n type ResumeStorage,\n} from \"./connection-supervisor\";\nimport type { RoomToken, SecondDeviceAnswer, TokenClient } from \"./tokens\";\nimport {\n asParticipantIdentity,\n isHostRole,\n type CallMode,\n type ConnectionQuality,\n type MeetIdentity,\n type MeetingRecord,\n type MeetParticipant,\n type ParticipantIdentity,\n type ParticipantRole,\n type ParticipantStanding,\n type RoomName,\n type StageLayout,\n asUserId,\n type UserId,\n} from \"./types\";\n\nexport interface JoinRequest {\n readonly roomName: RoomName;\n readonly meetingId?: string | undefined;\n readonly slug?: string | undefined;\n /** `audio` joins with the camera off. Same room, same code path (D3). */\n readonly mode: CallMode;\n /**\n * Whether to ATTEMPT the microphone on join. Defaults to true. `false` is a\n * deliberate listen-only join — the pre-join screen's \"Join with microphone\"\n * toggle. Either way the join itself never depends on the outcome; a refused\n * device lands in `snapshot.media.microphone.unavailable`, not in a throw.\n */\n readonly microphone?: boolean | undefined;\n /** Meet wave 5: a breakout room of this meeting; absent/null = the main room. */\n readonly breakoutRoomId?: string | null | undefined;\n /** HR-360 wave 2 (MD-16): ask to join as a silent observer. The server decides. */\n readonly observe?: boolean | undefined;\n /**\n * S7: the person's answer when they are already in this meeting on another tab or device\n * (`snapshot.secondDevice`): `switch_here` moves the call here, `join_too` adds this tab as a\n * companion (no microphone, speaker off).\n */\n readonly secondDevice?: SecondDeviceAnswer | undefined;\n}\n\nexport interface RoomEngineOptions {\n store: MeetStore;\n tokens: TokenClient;\n identity: MeetIdentity;\n driverFactory: RoomDriverFactory;\n /** S7: this page's tab claim (`core/tab-claim.ts`); absent = one connection per person. */\n tabClaim?: TabClaim | undefined;\n /** Canonical public metadata read; room packets alone never authorize teardown. */\n resolveMeeting?: ((slug: string) => Promise<MeetingRecord>) | undefined;\n onDiagnostic?: ((event: { level: \"info\" | \"warn\" | \"error\"; message: string; remedy?: string }) => void) | undefined;\n /** Fired when the local participant leaves for any reason, including kicked. */\n onLeft?: ((reason: string | null) => void) | undefined;\n now?: (() => number) | undefined;\n /** Remembered noise suppression / background choice (Meet wave 2). */\n effectsStorage?: EffectsStorage | undefined;\n /**\n * The device the person PICKED for each kind (the pre-join pickers, the\n * settings panel). Applied before any track is created — through 0.7.x the\n * pickers stored a choice nothing ever read, so every join used the browser\n * default whatever the screen said.\n */\n resolveDevice?: ((kind: \"audioinput\" | \"videoinput\" | \"audiooutput\") => string | null) | undefined;\n /**\n * §3.4 EXIT-REASON RESOLUTION: on every LiveKit disconnect the engine asks the server's state\n * read what happened to THIS connection (`GET /v1/meet/state?connection=`). Resolves null when\n * the read failed (offline / unreachable), undefined when this room has no state read (a call).\n */\n readExit?: ((connection: string, pass?: string | null) => Promise<ExitServerWord | null | undefined>) | undefined;\n /** Was the page frozen when the connection dropped (Page Lifecycle)? → `inactive`. */\n wasFrozen?: (() => boolean) | undefined;\n /**\n * S6b — the connection supervisor's ports. `network` = does the device have a network\n * (default: `navigator.onLine`); `probe` = do our API and the media server answer (default:\n * assume yes); `resume` = per-tab storage for the reload-after-offline marker (default: none).\n */\n /**\n * S6c — the media supervisor's platform port (permissions, device list, visibility). Default:\n * the browser's (`webMediaPlatform()`); `null` turns the supervisor's browser reads off.\n */\n mediaPlatform?: MediaPlatform | null | undefined;\n connection?: {\n readonly network?: NetworkPort | undefined;\n readonly probe?: ((serverUrl: string | null) => Promise<boolean>) | undefined;\n readonly resume?: ResumeStorage | undefined;\n } | undefined;\n}\n\nexport interface RoomEngine {\n join(request: JoinRequest): Promise<void>;\n /**\n * 🚨 THE HOST SAID YES — COME IN.\n *\n * A lobby token is minted with `canPublish: false` and `canSubscribe: false`\n * (that is what makes the lobby real rather than a screen over a live\n * microphone), so admission is not a UI state change: the cached token has to\n * be thrown away and a NEW one minted with the grants the server now gives.\n * Without this the waiting room's own sentence — \"you will join automatically\n * once you are admitted\" — is a lie, and the guest waits forever.\n */\n rejoin(): Promise<void>;\n /**\n * S6b: rejoin BY ITSELF from `disconnected` (the connection supervisor calls this once the\n * network and our servers answer). A fresh mint into the same identity: the server restores\n * role, hand and admission from the person row.\n */\n autoRejoin(): Promise<void>;\n /**\n * S6b: this tab was in (or dropped from) the call before the page reloaded — Chrome's offline\n * page reloading, a refresh. Returns true when it went straight back to joining (from\n * `resolving`), false when there is nothing fresh to resume (the caller resolves as usual).\n */\n resume(roomName: RoomName): boolean;\n /**\n * Meet wave 5: go to a breakout room (`null` = back to the main room). The server\n * mints the pass (`/token` with `breakout_room_id`) and refuses a room the plan does\n * not allow; the move is a reconnect that never reads as leaving the meeting.\n */\n moveToBreakout(roomId: string | null): Promise<void>;\n leave(): Promise<void>;\n /** The person's own \"back to the device check\" press from `left` / `disconnected`. */\n backToPrejoin(): void;\n /**\n * S7: BEFORE Join — is this person already live in `roomName` on another tab of THIS browser\n * (`TabClaim.presentElsewhere`)? When yes the pre-join offers Switch here / Join here too up\n * front (the same choice the server would otherwise ask after the press). Returns whether it did.\n * A guest has no stable person key before the pass, so the server's own answer covers them.\n */\n checkElsewhere(roomName: RoomName): Promise<boolean>;\n /** Clear a refused-share notice (the `share` region) the person dismissed. */\n dismissShareNotice(): void;\n setMicrophoneEnabled(enabled: boolean): Promise<void>;\n setCameraEnabled(enabled: boolean): Promise<void>;\n setScreenShareEnabled(enabled: boolean): Promise<void>;\n react(emoji: string): Promise<void>;\n /**\n * A PRIVATE message to one person (Meet wave 2). Data plane only, addressed\n * to that identity, NEVER saved — see `core/chat.ts` for the saved,\n * everyone-chat, which goes through the server.\n */\n sendPrivateChat(to: ParticipantIdentity, text: string): Promise<void>;\n /**\n * Change this participant's own media processing — noise suppression and the\n * camera background — on the live tracks, and remember it in this browser.\n * A refused effect is a diagnostic and a revert, never a lost camera.\n */\n setEffects(next: Partial<MediaEffects>): Promise<void>;\n /** Whether this browser can run a background effect at all (absent, never dead). */\n supportsVideoEffects(): boolean;\n /** Whether this browser can share a screen at all — the control is ABSENT where it cannot. */\n supportsScreenShare(): boolean;\n /** S6c: \"Enable sound\" — resume remote audio the autoplay policy holds. Call inside the click. */\n enableAudio(): Promise<void>;\n /** S6c: switch to the device the supervisor offered (a new one was plugged in). */\n /** The pre-join preview's outcome for one kind (the media supervisor classifies a refusal). */\n previewDevice(kind: \"microphone\" | \"camera\", outcome: \"prompting\" | \"on\" | \"off\" | { readonly error: unknown }): Promise<void>;\n acceptDeviceOffer(): Promise<void>;\n dismissDeviceOffer(): void;\n dismissDeviceNotice(): void;\n undoDeviceNotice(): Promise<void>;\n /** Choose the speaker (audio output) for this meeting. */\n switchDevice(kind: \"audioinput\" | \"videoinput\" | \"audiooutput\", deviceId: string): Promise<void>;\n pin(identity: ParticipantIdentity | null): void;\n setLayout(layout: StageLayout): void;\n attach(\n identity: ParticipantIdentity,\n kind: \"camera\" | \"microphone\" | \"screen\",\n element: HTMLMediaElement,\n ): () => void;\n token(): RoomToken | null;\n dispose(): Promise<void>;\n}\n\n/** Where this browser keeps a participant's media choices. */\nexport interface EffectsStorage {\n load(): MediaEffects;\n save(effects: MediaEffects): void;\n}\n\n/** What the server puts in the token's participant metadata. */\ninterface ParticipantMetadata {\n readonly user_id?: string;\n readonly role?: string;\n readonly can_record?: boolean;\n readonly is_agent?: boolean;\n readonly avatar_url?: string;\n}\n\n/**\n * 🚨 THE SERVER'S WORD, ON TWO CARRIERS (MRI-A11, feedback `7418ef78`).\n *\n * aidream mints the role into the token's participant METADATA (a JSON blob)\n * AND as a LiveKit participant ATTRIBUTE. Both come out of one decision in\n * `services/meet/service.py`; neither can be written by a client. The attribute\n * is preferred because LiveKit delivers the attribute map on every roster\n * update, while a roster event can carry an empty metadata string — which is\n * precisely how, through 2026-09-08, EVERY participant projected as\n * \"participant\" and no host ever saw a host control in a browser.\n *\n * These strings are pinned on the server side too: `test_meet_service.py` reads\n * them out of THIS file and fails if the two sides drift.\n */\nexport const PARTICIPANT_ROLE_ATTRIBUTE = \"matrx.participant.role\";\n/** S8: the server's raise time of a hand (epoch ms), \"\" when down. Pinned with aidream's `room_state.ATTRIBUTE_HAND`. */\nexport const PARTICIPANT_HAND_ATTRIBUTE = \"matrx.hand\";\n\nfunction handFrom(value: string | undefined): { handRaised: boolean; handRaisedAt: number | null } {\n const at = value === undefined || value.length === 0 ? Number.NaN : Number(value);\n return Number.isFinite(at) ? { handRaised: true, handRaisedAt: at } : { handRaised: false, handRaisedAt: null };\n}\nexport const PARTICIPANT_CAN_RECORD_ATTRIBUTE = \"matrx.participant.can_record\";\n/** S7: `main` or `companion` (a \"join here too\" connection) — `config.py::ATTRIBUTE_CONNECTION_KIND`. */\nexport const CONNECTION_KIND_ATTRIBUTE = \"matrx.participant.connection_kind\";\n/** Standing travels beside the role (CORE-DESIGN §2.1). Pinned by aidream's tests too. */\nexport const PARTICIPANT_STANDING_ATTRIBUTE = \"matrx.participant.standing\";\n\nconst KNOWN_STANDINGS: readonly ParticipantStanding[] = [\"member\", \"invited\", \"outsider\", \"guest\"];\n\nfunction parseStanding(value: unknown): ParticipantStanding | null {\n return KNOWN_STANDINGS.includes(value as ParticipantStanding) ? (value as ParticipantStanding) : null;\n}\n\nconst KNOWN_ROLES: readonly ParticipantRole[] = [\"host\", \"cohost\", \"presenter\", \"participant\", \"viewer\"];\n\n/**\n * An unrecognized role is `participant` — never `host`. A word this package\n * does not know is a server that is ahead of it, and the safe reading of an\n * unknown claim is the one that grants nothing.\n */\nfunction parseRole(value: string | undefined): ParticipantRole {\n return KNOWN_ROLES.includes(value as ParticipantRole) ? (value as ParticipantRole) : \"participant\";\n}\n\n/**\n * Metadata is SERVER-MINTED and therefore trusted; it is still parsed\n * defensively, because a malformed blob must degrade a tile to \"participant\"\n * rather than throw inside a roster event and freeze the whole grid.\n */\nexport function projectParticipant(\n driver: DriverParticipant,\n joinedAt: string,\n): MeetParticipant {\n let metadata: ParticipantMetadata = {};\n if (driver.metadata !== null && driver.metadata.length > 0) {\n try {\n const parsed: unknown = JSON.parse(driver.metadata);\n if (typeof parsed === \"object\" && parsed !== null) {\n metadata = parsed as ParticipantMetadata;\n }\n } catch {\n metadata = {};\n }\n }\n const userId = typeof metadata.user_id === \"string\" ? asUserId(metadata.user_id) : null;\n return {\n // S7: a tile is a PERSON; LiveKit names the tab's connection (`<person>~<tab>`).\n identity: personOf(driver.identity),\n userId,\n // 🚨 A RAW LIVEKIT IDENTITY IS NEVER A NAME (MRI-A18). Through 0.4.12 this\n // line read `driver.name.length > 0 ? driver.name : driver.identity`, and\n // that single fallback put `guest:v-spk-1` on the participant strip, the\n // People panel, the meeting header and every Pin/Mute aria-label, in BOTH\n // lanes — inside the very structure `core/names.ts` is taught from. A\n // nameless tile is still a bug report nobody can action, so the fallback\n // stays; it is now the name book's HONEST placeholder (\"Unnamed guest\", or\n // the agent's product name) instead of the token. `store.setParticipants`\n // then upgrades this to the best name anything has ever learned.\n displayName: displayNameFor({}, driver.identity, driver.name),\n avatarUrl: typeof metadata.avatar_url === \"string\" ? metadata.avatar_url : null,\n // ATTRIBUTE FIRST, metadata second — see the constants above. Both are the\n // server's; the attribute is simply the one that always arrives.\n role: parseRole(driver.attributes[PARTICIPANT_ROLE_ATTRIBUTE] ?? metadata.role),\n standing: parseStanding(\n driver.attributes[PARTICIPANT_STANDING_ATTRIBUTE] ??\n (metadata as { standing?: unknown }).standing,\n ),\n canRecord:\n driver.attributes[PARTICIPANT_CAN_RECORD_ATTRIBUTE] === \"true\" ||\n metadata.can_record === true,\n isLocal: driver.isLocal,\n isAgent: metadata.is_agent === true,\n attributes: driver.attributes,\n micEnabled: driver.micEnabled,\n cameraEnabled: driver.cameraEnabled,\n screenSharing: driver.screenSharing,\n speaking: false,\n // S8: a hand is the SERVER's (`hand_raised_at` on the person row, `/moderate raise_hand`),\n // replicated as the `matrx.hand` attribute: epoch ms of the raise, \"\" when down. It is\n // therefore delivered to late joiners and survives refresh and reconnect.\n ...handFrom(driver.attributes[PARTICIPANT_HAND_ATTRIBUTE]),\n quality: \"unknown\",\n joinedAt,\n };\n}\n\ninterface DataEnvelope {\n readonly id?: string;\n readonly at?: string;\n /** Epoch ms a hand went up — the queue's order. */\n readonly raisedAt?: number;\n /** A private chat message's addressee. */\n readonly to?: string;\n readonly raised?: boolean;\n readonly emoji?: string;\n readonly text?: string;\n readonly name?: string;\n}\n\nexport function createRoomEngine(options: RoomEngineOptions): RoomEngine {\n const { store, tokens, identity } = options;\n const now = options.now ?? (() => Date.now());\n let driver: MeetRoomDriver | null = null;\n let current: RoomToken | null = null;\n let joining: Promise<void> | null = null;\n // The request this engine last joined with, so an admitted guest can be let\n // in without the surface having to remember how it got here.\n let lastRequest: JoinRequest | null = null;\n let disposed = false;\n // True while a breakout move tears down one room to connect the next: that\n // disconnect is not the person leaving the meeting.\n let moving = false;\n const confirmingEnds = new WeakSet<MeetRoomDriver>();\n /** The media server of the last mint — what the probe asks after the room is gone. */\n let lastServerUrl: string | null = null;\n /** The meeting's connection knobs, from the last mint (S6b). */\n let connectionPolicy: ConnectionPolicy = DEFAULT_CONNECTION_POLICY;\n /** Remote people who dropped without leaving, held as `reconnecting` tiles (S6b). */\n let ghosts = new Map<string, Ghost>();\n let ghostTimer: ReturnType<typeof setTimeout> | null = null;\n /** The last LIVE roster the driver reported, so an expiring ghost can re-project it. */\n let liveRoster: readonly DriverParticipant[] = [];\n /** S7: person key → every LiveKit connection they hold, and the one their tile shows. */\n let personConnections = new Map<string, string[]>();\n let mainConnection = new Map<string, string>();\n function connectionsOf(who: string): string[] {\n return personConnections.get(personOf(who)) ?? [who];\n }\n /** Departures LiveKit announced since the last roster: identity → reason name. */\n const departures = new Map<string, string | null>();\n\n /* P1 connection-network (`network-health.ts`): this tab's own path, and everyone's heartbeat. */\n const liveness = createLivenessTracker(now);\n const assessor = createSelfAssessor();\n const autoOff = createVideoAutoOff(() => connectionPolicy.videoAutoOffAfterPoorSeconds * 1000, now);\n /** LiveKit's own quality per PERSON — combined with the self-reported reading, never replaced. */\n const lkQuality = new Map<string, ConnectionQuality>();\n /** Connections that beat before and went silent: shown `reconnecting` to everyone here. */\n let silent = new Set<string>();\n let healthTimer: ReturnType<typeof setInterval> | null = null;\n let lastBeatAt = 0;\n let wasUnstable = false;\n\n function patchLocalNetwork(next: Partial<LocalNetwork>): void {\n const before = store.snapshot().localNetwork;\n const merged = { ...before, ...next };\n if (merged.path !== before.path || merged.quality !== before.quality || merged.videoPaused !== before.videoPaused) {\n store.patch({ localNetwork: merged });\n }\n }\n\n /** What a person's tile says about their connection: the worse of LiveKit's and their own word. */\n function effectiveQuality(person: string, connection: string | undefined, isLocal: boolean): ConnectionQuality {\n const fromServer = lkQuality.get(person) ?? \"unknown\";\n if (isLocal) return worstQuality(fromServer, assessor.current());\n return worstQuality(fromServer, connection === undefined ? null : liveness.reported(connection)?.q);\n }\n\n function refreshQualities(): void {\n const snap = store.snapshot();\n for (const p of snap.participants) {\n if (p.reconnecting === true) continue;\n const next = effectiveQuality(p.identity, mainConnection.get(p.identity), p.isLocal);\n if (next !== \"unknown\" && next !== p.quality) store.setQuality(p.identity, next);\n }\n }\n\n function stopHealth(): void {\n if (healthTimer !== null) clearInterval(healthTimer);\n healthTimer = null;\n liveness.clear();\n assessor.reset();\n autoOff.reset();\n silent = new Set();\n lkQuality.clear();\n lastBeatAt = 0;\n wasUnstable = false;\n if (store.snapshot().localNetwork !== UNKNOWN_LOCAL_NETWORK) store.patch({ localNetwork: UNKNOWN_LOCAL_NETWORK });\n }\n\n /** One tick a second while in the call: beat, and judge who went silent. */\n function healthTick(): void {\n const snap = store.snapshot();\n const target = driver;\n if (disposed || target === null || snap.phase !== \"in_call\") return;\n if (snap.connection !== \"stable\") {\n // THIS tab is the one cut off: it hears nobody, which says nothing about them.\n wasUnstable = true;\n return;\n }\n if (wasUnstable) {\n wasUnstable = false;\n liveness.restartClock();\n }\n const size = liveRoster.length;\n const at = now();\n if (at - lastBeatAt >= heartbeatIntervalMs(size)) {\n lastBeatAt = at;\n const beat = encodeBeat({ q: assessor.current(), vp: snap.localNetwork.videoPaused });\n void target.publishData(ALIVE_TOPIC, beat).catch(() => undefined);\n }\n const remotes = liveRoster.filter((p) => !p.isLocal).map((p) => p.identity);\n const next = liveness.stale(remotes, size);\n if (next.size !== silent.size || [...next].some((who) => !silent.has(who))) {\n silent = next;\n projectRoster(liveRoster, new Date(at).toISOString());\n }\n }\n\n function startHealth(): void {\n if (healthTimer !== null) return;\n healthTimer = setInterval(healthTick, 1000);\n }\n\n /** The request to come back with: the last join, with the camera/mic as they are NOW. */\n function requestAsNow(): JoinRequest | null {\n if (lastRequest === null) return null;\n const media = store.snapshot().media;\n if (store.snapshot().phase !== \"in_call\") return lastRequest;\n return { ...lastRequest, mode: media.camera.enabled ? \"video\" : \"audio\", microphone: media.microphone.enabled };\n }\n\n /** Drop the room this engine holds (no phase change — the caller says where the person went). */\n async function dropRoom(): Promise<void> {\n const target = driver;\n driver = null;\n if (current !== null) {\n tokens.invalidate(current.roomName);\n current = null;\n }\n store.setLocalTrack(\"microphone\", IDLE_TRACK);\n store.setLocalTrack(\"camera\", IDLE_TRACK);\n media.onDisconnected();\n store.patch({ share: NO_SHARE, connection: \"stable\" });\n clearGhosts();\n stopHealth();\n await target?.disconnect();\n }\n\n /** MY Leave: the room is dropped and the phase is `left` (§3.1). */\n async function leaveRoom(): Promise<void> {\n store.dispatch({ type: \"leave\" });\n await dropRoom();\n options.onLeft?.(null);\n }\n\n async function confirmMeetingEnd(): Promise<void> {\n const meeting = store.snapshot().meeting;\n const target = driver;\n if (meeting === null || target === null || confirmingEnds.has(target) || options.resolveMeeting === undefined) return;\n confirmingEnds.add(target);\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n const verified = await Promise.race([\n options.resolveMeeting(meeting.slug),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(\"Meeting end verification timed out\")), 5000);\n }),\n ]);\n if (disposed || driver !== target || store.snapshot().meeting?.id !== meeting.id) return;\n if (verified.id !== meeting.id || verified.roomName !== meeting.roomName || verified.endedAt === null) {\n report(\"warn\", \"Meet ignored an unconfirmed meeting-end notice\", \"The canonical meeting is still live or does not match this room.\");\n return;\n }\n // Keep newer roster/metadata updates; only the durable ending is needed.\n const latest = store.snapshot().meeting;\n if (latest === null) return;\n store.patch({ meeting: { ...latest, endedAt: verified.endedAt } });\n // time-limit-warning: the screen says WHY the meeting ended (the server recorded the trigger on this connection).\n const connection = current?.connectionIdentity ?? current?.identity ?? store.snapshot().localIdentity ?? null;\n let why: ExitServerWord | null | undefined;\n try {\n why = options.readExit !== undefined && connection !== null ? await options.readExit(connection, current?.meetingPass ?? null) : undefined;\n } catch {\n why = null;\n }\n store.dispatch({ type: \"ended\" }, { reason: why?.exitReason === \"time_limit\" ? \"time_limit\" : \"ended\" });\n await dropRoom();\n options.onLeft?.(\"ended\");\n } catch (error) {\n report(\"error\", `Meet could not confirm or finish meeting teardown: ${String(error)}`, \"Check the meeting link again; if media remains active, close this meeting tab.\");\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n confirmingEnds.delete(target);\n }\n }\n\n function report(level: \"info\" | \"warn\" | \"error\", message: string, remedy?: string): void {\n options.onDiagnostic?.(remedy === undefined ? { level, message } : { level, message, remedy });\n }\n\n /** Consecutive screen-share refusals no browser can tell from a closed picker. */\n let ambiguousShareDenials = 0;\n\n /**\n * THE JOIN FAILED. Only a join can fail the join: the machine accepts `join_failed` from\n * `joining` alone, so a caller anywhere else (an in-call action) cannot move the phase with it\n * — the failure is still recorded and reported, and the call goes on. A gate reason renders\n * `refused:<reason>` (`ended` → `ended`); no reason renders `disconnected` with Rejoin — never\n * pre-join (the old `failed` → pre-join fallback is gone).\n */\n function failJoin(error: unknown, operation: string): MeetError {\n const normalized = normalizeMeetError(error, operation);\n store.patch({\n failure: {\n message: normalized.message,\n remedy: normalized.remedy,\n reason: normalized.reason,\n retryAt: normalized.retryAfterSeconds === null ? null : now() + normalized.retryAfterSeconds * 1000,\n stage: \"join\",\n },\n });\n store.dispatch({ type: \"join_failed\", reason: normalized.reason ?? null });\n report(\n normalized.code === \"session-unavailable\" ? \"warn\" : \"error\",\n normalized.message,\n normalized.remedy,\n );\n return normalized;\n }\n\n /**\n * A move between rooms (breakout) left the person in NO room: that is an exit, resolved as\n * `disconnected` with Rejoin (§3.4) — the failure is kept for the screen's remedy.\n */\n async function lostRoom(error: unknown, operation: string): Promise<MeetError> {\n const normalized = normalizeMeetError(error, operation);\n store.patch({ failure: { message: normalized.message, remedy: normalized.remedy, reason: normalized.reason } });\n await dropRoom();\n store.dispatch({ type: \"exit\", to: \"disconnected\" });\n report(\"error\", normalized.message, normalized.remedy);\n return normalized;\n }\n\n // S6c — MS, the media supervisor. Its regions never move the phase.\n const media = createMediaSupervisor({\n store,\n platform: options.mediaPlatform === undefined ? webMediaPlatform() : options.mediaPlatform,\n driver: () => driver,\n report,\n });\n\n const handlers: RoomDriverHandlers = {\n audioPlayback(canPlay) {\n media.onAudioPlayback(canPlay);\n },\n devicesChanged() {\n media.onDevicesChanged();\n },\n mediaDeviceError(error, kind) {\n media.onDeviceError(error, kind);\n },\n participants(list) {\n const joinedAt = new Date(now()).toISOString();\n projectRoster(list, joinedAt);\n },\n participantLeft(who, reason) {\n departures.set(who, reason);\n liveness.forget(who);\n silent.delete(who);\n },\n roomMetadata(metadata) {\n // S8 THE SERVER'S ROOM STATE (CORE-DESIGN §2.6 RS). Only aidream's\n // `room_state.publish_room_state` writes it, always whole, read back from the rows. A\n // malformed or empty string means \"nothing set\", never a throw inside a room event.\n let parsed: Record<string, unknown> = {};\n if (metadata.length > 0) {\n try {\n const value: unknown = JSON.parse(metadata);\n if (typeof value === \"object\" && value !== null) parsed = value as Record<string, unknown>;\n } catch {\n report(\n \"warn\",\n \"meet: the room's metadata is not JSON, so the room state was not read from it\",\n \"Only aidream writes room metadata; check services/meet/room_state.py.\",\n );\n }\n }\n const key = (name: string): ParticipantIdentity | null => {\n const value = parsed[name];\n return typeof value === \"string\" && value.length > 0 ? asParticipantIdentity(personOf(value)) : null;\n };\n const waiting = typeof parsed[\"waiting_count\"] === \"number\" ? (parsed[\"waiting_count\"] as number) : 0;\n const before = store.snapshot();\n const next = { spotlight: key(\"spotlight\"), actingHost: key(\"acting_host\"), presenter: key(\"presenter\"), waitingCount: waiting };\n if (\n before.spotlight !== next.spotlight ||\n before.actingHost !== next.actingHost ||\n before.presenter !== next.presenter ||\n before.waitingCount !== next.waitingCount\n ) {\n store.patch(next);\n }\n if (parsed[\"recording\"] !== undefined && parsed[\"recording\"] !== null) {\n applyRecordingState(store, { meeting_id: parsed[\"meeting_id\"], recording: parsed[\"recording\"] });\n }\n },\n activeSpeakers(identities) {\n store.setSpeaking([...new Set(identities.map((who) => personOf(who)))]);\n },\n quality(who, value) {\n const person = personOf(who);\n lkQuality.set(person, value);\n const local = store.snapshot().participants.find((p) => p.identity === person)?.isLocal === true;\n store.setQuality(person, value === \"lost\" ? value : effectiveQuality(person, who, local));\n },\n networkReading(reading) {\n const snap = store.snapshot();\n if (snap.phase !== \"in_call\" || snap.connection !== \"stable\") return;\n startHealth();\n const quality = assessor.push(reading);\n const path = pathOf(reading);\n patchLocalNetwork({ quality, ...(path !== \"unknown\" ? { path } : {}) });\n refreshQualities();\n // `net-video-auto-off`: a path poor for the knob's time pauses the camera to keep the audio.\n const cameraOn = snap.media.camera.enabled;\n if (autoOff.step(quality, cameraOn) === \"pause\" && driver !== null) {\n const target = driver;\n report(\"info\", \"meet: the camera was paused because the connection is poor\", \"The audio is kept; Try video again turns it back on.\");\n void enableTrack(target, \"camera\", false).then((state) => {\n if (!state.enabled) patchLocalNetwork({ videoPaused: true });\n });\n }\n },\n connectionState(state) {\n // `reconnecting` is a VISIBLE state, not a silent one — and it is the `connection`\n // REGION, never a phase (CORE-DESIGN §3.1/§3.2): the person is still in the call. Which\n // region value (signal blip, full reconnect, offline, servers unreachable) is the\n // connection supervisor's call (S6b).\n supervisor.driverState(state);\n if (state === \"connected\" && store.snapshot().phase === \"joining\") store.dispatch({ type: \"connected\" });\n },\n transcription(event) {\n // A CAPTION IS A HINT (D16). It is stored as one, next to the durable\n // rows, and the reconciliation in `transcript.ts` decides which of the two\n // a line ends up being. Nothing else in this package reads captions.\n const snapshot = store.snapshot();\n const meeting = snapshot.meeting;\n if (meeting === null) {\n // A caption with no meeting record has nothing durable to reconcile\n // against later. An ad-hoc call room is exactly that case; dropping it\n // is correct, and it is not silent — it is stated here.\n return;\n }\n // `lk.transcribed_participant_identity` is supplied by the publisher and\n // only says who they CLAIM to be transcribing. The stream's publisher is\n // the SFU-reported identity; captions are admitted only from this\n // meeting's deterministic note-taker. This remains display-only (D16),\n // but a room participant cannot write a fake line into the caption rail.\n // This check deliberately does not wait for a roster event: the first\n // note-taker utterance may arrive before its roster projection.\n if (event.publisherIdentity !== noteTakerIdentity(meeting.id)) {\n report(\n \"warn\",\n \"meet: ignored a caption from a participant that is not this meeting's note-taker\",\n \"Captions are accepted only from the server-dispatched note-taker; the durable transcript remains authoritative.\",\n );\n return;\n }\n const speakerIdentity = personOf(event.speakerIdentity);\n const speaker = snapshot.participants.find(\n (participant) => participant.identity === speakerIdentity,\n );\n store.ingestCaption(\n captionSegment({\n captionId: event.segmentId,\n meetingId: meeting.id,\n identity: speakerIdentity,\n // 🚨 ONE ANSWER TO \"WHO SAID THAT\" (MRI-A16). This line used to fall\n // back to `event.speakerIdentity` — the raw LiveKit token — which is\n // how a guest's caption rail read `guest:c8-spk-1` in the same second\n // the host's read \"Ada Lovelace\". The book keeps the last known name\n // for somebody who has left, and says \"Unnamed guest\" when nothing\n // anywhere has ever known one. It never prints a token.\n speaker: displayNameFor(snapshot.names, speakerIdentity, speaker?.displayName),\n text: event.text,\n final: event.final,\n startedAt: event.at,\n }),\n );\n },\n data(topic, payload, from) {\n // 🚨 THE SERVER'S OWN EVENTS COME FIRST, and they are the only live feed a\n // session-less guest has (MRI-A12). They are handled before the `mx.*`\n // control plane because they are a different KIND of message: published by\n // the platform with a `roomAdmin` grant no participant token can claim,\n // carrying durable rows rather than a sender's intent.\n if (isRoomEventTopic(topic)) {\n // `matrx.meet.*` is a server namespace, not an ACL. Any participant\n // may publish to a topic, including one with an agent-looking name;\n // only RoomService.SendData reaches the SDK without a participant.\n if (from !== null) {\n report(\n \"warn\",\n `meet: ignored participant-published server event on ${topic}`,\n \"Only aidream may publish matrx.meet.* events. The durable meeting record remains unchanged.\",\n );\n return;\n }\n let body: unknown;\n try {\n body = JSON.parse(payload);\n } catch {\n report(\n \"warn\",\n `meet: dropped an unparseable room event on ${topic}`,\n \"This topic is published by aidream only. Non-JSON here means a client is \" +\n \"publishing under the server's namespace, or the payload was truncated.\",\n );\n return;\n }\n if (topic === ROOM_EVENTS.ended && options.resolveMeeting !== undefined) {\n const meeting = store.snapshot().meeting;\n if (typeof body === \"object\" && body !== null && \"meeting_id\" in body && body.meeting_id === meeting?.id) {\n void confirmMeetingEnd();\n } else {\n report(\"warn\", \"Meet ignored an ending for another or missing meeting\", \"Only a matching canonical meeting can end this transport.\");\n }\n return;\n }\n const outcome = applyRoomEvent({ store, topic, payload: body, now });\n if (!outcome.handled) {\n // NEVER SILENT. A dropped room event is a note, a decision or an\n // ending that a guest will simply never see, and the reason has to be\n // reachable from the surface rather than from a debugger.\n report(\n \"warn\",\n `meet: ignored a room event on ${topic} — ${outcome.ignored ?? \"no reason given\"}`,\n \"The durable row still exists behind the meeting link. Signed-in members \" +\n \"receive it over Postgres Changes; a guest sees it on their next join.\",\n );\n }\n return;\n }\n let envelope: DataEnvelope = {};\n try {\n const parsed: unknown = JSON.parse(payload);\n if (typeof parsed === \"object\" && parsed !== null) envelope = parsed as DataEnvelope;\n } catch {\n report(\n \"warn\",\n `meet: dropped an unparseable data message on ${topic}`,\n \"A sender is publishing non-JSON on a reserved mx.* topic. Use publishData through \" +\n \"the engine so the envelope is always well formed.\",\n );\n return;\n }\n const sender = personOf(from ?? \"\");\n if (from !== null) {\n const beat = topic === ALIVE_TOPIC ? decodeBeat(payload) : null;\n const before = liveness.reported(from);\n liveness.heard(from, beat);\n if (topic === ALIVE_TOPIC) {\n startHealth();\n if (beat !== null && (before?.q !== beat.q || before?.vp !== beat.vp || silent.has(from))) {\n silent.delete(from);\n projectRoster(liveRoster, new Date(now()).toISOString());\n refreshQualities();\n } else if (silent.delete(from)) {\n projectRoster(liveRoster, new Date(now()).toISOString());\n }\n return;\n }\n }\n switch (topic) {\n case DATA_TOPICS.reaction:\n if (typeof envelope.emoji === \"string\") {\n store.addReaction({\n id: envelope.id ?? `${sender}-${now()}`,\n identity: sender,\n emoji: envelope.emoji,\n at: now(),\n });\n }\n return;\n case DATA_TOPICS.chat:\n // ONLY PRIVATE MESSAGES ride the data plane since 0.8: a message to\n // everyone is SAVED through the server and arrives as the server's\n // `matrx.meet.chat`. A participant's un-addressed `mx.chat` packet is\n // an older client's ephemeral chat — shown, never mistaken for saved.\n if (typeof envelope.text === \"string\") {\n const local = store.snapshot().localIdentity;\n store.addChat({\n id: envelope.id ?? `${sender}-${now()}`,\n identity: sender,\n displayName: envelope.name ?? sender,\n text: envelope.text,\n at: envelope.at ?? new Date(now()).toISOString(),\n ...(typeof envelope.to === \"string\" && local !== null ? { to: local } : {}),\n });\n }\n return;\n default:\n // Unknown topics are IGNORED, never thrown on — that is what lets a\n // newer client ship a new in-room feature without breaking old ones.\n return;\n }\n },\n disconnected(reason) {\n // A breakout move, or a disconnect THIS engine asked for (Leave, end, dispose), is not\n // an exit to resolve: the caller already said where the person went.\n if (moving || driver === null) return;\n void resolveExitAndApply(reason);\n },\n };\n\n /**\n * §3.4 — LiveKit dropped this tab. Ask the server what happened to THIS connection, then land\n * on the phase the resolution names. Never pre-join; `disconnected` always offers Rejoin.\n */\n async function resolveExitAndApply(hint: string | null): Promise<void> {\n const connection = current?.connectionIdentity ?? current?.identity ?? store.snapshot().localIdentity ?? null;\n // The pass this connection held — read BEFORE the room is dropped (which forgets the token):\n // S7's \"switched to another tab\" is only the server's word, never LiveKit's.\n const heldPass = current?.meetingPass ?? null;\n // Come back as you were: the camera and microphone as they are at the drop (S6b), for the\n // auto-rejoin, a manual Rejoin and a reload alike — `dropRoom` resets the local tracks.\n lastRequest = requestAsNow();\n // THE SERVER ENDED THIS SESSION (removed, room closed, token revoked), so the cached token\n // is dead: the next Join must mint again and read the server's own decision. Reusing it\n // showed \"invalid token: revoked\" under a camera-refusal remedy (production 2026-09-27).\n await dropRoom();\n let server: ExitServerWord | null | undefined;\n if (options.readExit === undefined || connection === null || store.snapshot().meeting === null) {\n server = undefined;\n } else {\n try {\n server = await options.readExit(connection, heldPass);\n } catch {\n server = null;\n }\n }\n if (disposed) return;\n const resolved = resolveExit({ server, hint, frozen: options.wasFrozen?.() ?? false });\n // The state read failed: the supervisor knows WHICH failure — no network, or our servers.\n store.patch({ connection: server === null ? supervisor.exitConnection() : resolved.connection });\n if (resolved.to === \"waiting:knocking\") {\n store.patch({\n waiting: {\n admission: \"knocking\",\n admittersPresent: null,\n reknockAllowed: true,\n knockExpiresAt: null,\n pollSeconds: store.snapshot().waiting?.pollSeconds ?? 3,\n liveVersion: 0,\n },\n });\n }\n store.dispatch({ type: \"exit\", to: resolved.to }, { reason: resolved.reason, notice: resolved.notice });\n report(\n resolved.to === \"disconnected\" ? \"warn\" : \"info\",\n `meet: the room connection ended (${hint ?? \"no LiveKit reason\"}) → ${resolved.to}`,\n resolved.to === \"disconnected\" ? \"Press Rejoin; the meeting is still going.\" : \"The screen says what happened.\",\n );\n options.onLeft?.(hint);\n }\n\n async function connect(request: JoinRequest): Promise<void> {\n store.patch({ failure: null, waiting: null, roomName: request.roomName, share: NO_SHARE, connection: \"stable\" });\n const tabId = options.tabClaim !== undefined ? await options.tabClaim.tabId : null;\n const token = await tokens.get({\n roomName: request.roomName,\n identity,\n ...(request.meetingId !== undefined ? { meetingId: request.meetingId } : {}),\n ...(request.slug !== undefined ? { slug: request.slug } : {}),\n ...(request.breakoutRoomId != null ? { breakoutRoomId: request.breakoutRoomId } : {}),\n ...(request.observe === true ? { observe: true } : {}),\n ...(tabId !== null ? { tabId } : {}),\n ...(request.secondDevice !== undefined ? { secondDevice: request.secondDevice } : {}),\n });\n if (token.secondDeviceChoice != null) {\n // S7: ALREADY IN THIS MEETING on another tab or device. Nothing was minted; the pre-join\n // offers the meeting's choice and the person's press joins again with the answer.\n store.patch({ secondDevice: { rule: token.secondDeviceChoice }, localIdentity: token.identity });\n store.dispatch({ type: \"second_device\" });\n report(\"info\", `meet: already in this meeting elsewhere — offering the choice (${token.secondDeviceChoice})`);\n return;\n }\n if (store.snapshot().secondDevice !== null) store.patch({ secondDevice: null });\n // A switch happens once: a later rejoin of THIS tab must not displace a tab opened since.\n if (request.secondDevice === \"switch_here\" && lastRequest !== null) {\n const { secondDevice: _answered, ...rest } = lastRequest;\n lastRequest = rest;\n }\n options.tabClaim?.inRoom(String(request.roomName), String(token.identity), token.secondDeviceRule ?? null);\n current = token;\n lastServerUrl = token.serverUrl;\n connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;\n store.patch({ captionsStartTimeoutSeconds: connectionPolicy.captionsStartTimeoutSeconds });\n if (token.leavePolicy !== undefined) store.patch({ leavePolicy: token.leavePolicy });\n store.patch({ canReclaim: token.canReclaim === true });\n // 🚨 THE GRANTS LAND WITH THE TOKEN, before any roster exists (MRI-A11).\n // Waiting for the LiveKit roster to learn that this person is the host means\n // the host's own controls flicker in — or, when a roster event arrives with\n // empty metadata, never appear at all. That was the shipped defect.\n store.patch({\n localIdentity: token.identity,\n breakoutRoomId: token.breakoutRoomId ?? null,\n grants: {\n role: token.role,\n standing: token.standing,\n canRecord: token.canRecord,\n canPublish: token.canPublish,\n canSubscribe: token.canSubscribe,\n observersVisibleTo: token.observersVisibleTo ?? \"everyone\",\n },\n });\n\n // THE LOBBY IS A PHASE, NOT AN ERROR (D6). The server said \"wait\"; the UI\n // shows the waiting room and the room channel delivers the host's decision.\n //\n // 🚨 S4: A TOKEN THAT CANNOT SUBSCRIBE NEVER BECOMES AN IN-CALL SCREEN. A waiting guest\n // was once shown an empty room (\"Just you\") while the host still listed them as waiting:\n // a media connection that can hear nobody IS the waiting room, whatever flag came with\n // it. The wait itself is then read from the server (`GET /v1/meet/state`), never from a\n // broadcast (CORE-DESIGN §2.6).\n if (token.lobby || !token.canSubscribe) {\n const admission = token.admissionState !== undefined && token.admissionState !== \"admitted\"\n ? token.admissionState\n : \"knocking\";\n store.patch({\n waiting: {\n admission,\n admittersPresent: null,\n reknockAllowed: true,\n knockExpiresAt: null,\n pollSeconds: token.pollSeconds ?? 3,\n liveVersion: 0,\n },\n });\n store.dispatch({ type: \"admission\", admission });\n return;\n }\n\n store.patch({ waiting: null });\n const created = await options.driverFactory(handlers);\n if (disposed) {\n // Unmounted mid-connect. Disconnect the room we just built rather than\n // leaking it — this is the \"camera light stays on\" bug.\n await created.disconnect();\n return;\n }\n driver = created;\n // S7: a companion plays no remote audio — set before the connect so no packet is ever heard.\n if (token.connectionKind === \"companion\") created.setRemoteAudioPlayback?.(false);\n await created.connect(token.serverUrl, token.token);\n\n // 🚨 IN THE ROOM. Attendance is settled HERE, before a single device is\n // touched. Everything below is best-effort publishing; nothing below can\n // unwind the join. See `attendance.ts` for the defect this closes.\n store.patch({ failure: null, connection: \"stable\" });\n store.dispatch({ type: \"connected\" });\n store.setLocalTrack(\"microphone\", IDLE_TRACK);\n store.setLocalTrack(\"camera\", IDLE_TRACK);\n media.start();\n\n // THE REMEMBERED CHOICES GO ON BEFORE ANY DEVICE DOES, so the first frame\n // anyone sees is already blurred and the first word already cleaned.\n const effects = options.effectsStorage?.load() ?? store.snapshot().effects;\n store.patch({ effects });\n // Only what DIFFERS from the browser default is applied on the way in: a\n // default join touches nothing it does not have to.\n if (!effects.noiseSuppression || effects.background !== \"none\") {\n try {\n await applyEffects(created, effects);\n } catch {\n // Reported inside; the join never depends on an effect.\n }\n }\n for (const kind of [\"audioinput\", \"videoinput\", \"audiooutput\"] as const) {\n const chosen = options.resolveDevice?.(kind) ?? null;\n if (chosen === null || chosen === \"default\") continue;\n try {\n await created.switchDevice(kind, chosen);\n } catch (error) {\n const normalized = normalizeDeviceError(error, `meet.switchDevice(${kind})`);\n report(\"warn\", normalized.message, normalized.remedy);\n }\n }\n\n if (request.mode === \"video\") {\n await enableTrack(created, \"camera\", true);\n }\n // S7: a companion (\"join here too\") never opens the microphone — the person's other device\n // is the one that speaks.\n if (request.microphone !== false && current?.connectionKind !== \"companion\") {\n await enableTrack(created, \"microphone\", true);\n }\n // What is in use now is the baseline a later unplug is measured against.\n media.onConnected();\n }\n\n /**\n * THE ONE PATH that turns a local device on or off — used by the join and by\n * the in-room controls alike, so \"enable it later\" cannot behave differently\n * from \"enable it on the way in\". A refusal is recorded as state with the\n * browser's own reason and a remedy, reported as a diagnostic, and returned;\n * it is never thrown, because there is no caller for whom losing the room is\n * the right answer to a busy microphone.\n */\n async function enableTrack(\n target: MeetRoomDriver,\n kind: \"microphone\" | \"camera\",\n enabled: boolean,\n ): Promise<LocalTrackState> {\n try {\n if (kind === \"microphone\") await target.setMicrophoneEnabled(enabled);\n else await target.setCameraEnabled(enabled);\n const state: LocalTrackState = { enabled, unavailable: null };\n store.setLocalTrack(kind, state);\n return state;\n } catch (error) {\n // S6c: the refusal is classified against the permission store (Block vs the operating\n // system vs a missing or busy device) — the error name alone cannot tell them apart.\n const refused = await media.unavailable(error, kind, `meet.${kind}`);\n const state: LocalTrackState = { enabled: false, unavailable: refused };\n store.setLocalTrack(kind, state);\n report(\"warn\", refused.message, refused.remedy);\n return state;\n }\n }\n\n async function publish(\n topic: string,\n body: Record<string, unknown>,\n to?: readonly string[],\n ): Promise<void> {\n if (driver === null) return;\n // S7: a private message goes to a PERSON — every connection they hold.\n const addresses = to === undefined ? undefined : to.flatMap((who) => connectionsOf(who));\n await driver.publishData(topic, JSON.stringify(body), addresses);\n }\n\n async function applyEffects(target: MeetRoomDriver, effects: MediaEffects): Promise<void> {\n try {\n await target.setAudioProcessing?.({\n noiseSuppression: effects.noiseSuppression,\n echoCancellation: true,\n autoGainControl: true,\n });\n } catch (error) {\n report(\"warn\", `meet: noise suppression could not be applied: ${String(error)}`, \"The microphone keeps working without it.\");\n }\n if (target.setVideoEffect === undefined) return;\n const effect: VideoEffect =\n effects.background === \"blur\"\n ? { kind: \"blur\" }\n : effects.background === \"image\" && effects.backgroundImage !== null\n ? { kind: \"image\", imageUrl: effects.backgroundImage }\n : { kind: \"none\" };\n if (effect.kind !== \"none\" && target.supportsVideoEffects?.() === false) return;\n try {\n await target.setVideoEffect(effect);\n } catch (error) {\n const normalized = normalizeMeetError(error, \"meet.setVideoEffect\");\n report(\"warn\", normalized.message, normalized.remedy);\n throw normalized;\n }\n }\n\n /**\n * THE ROSTER, WITH OTHERS' DROPS HELD (S6b, `net-others-see-drop`). A participant LiveKit\n * dropped without a deliberate reason stays as a `reconnecting` tile for\n * `reconnect_grace_seconds`; they disappear at once when they come back (same identity — one\n * tile, never a copy) or when the grace runs out.\n */\n function projectRoster(list: readonly DriverParticipant[], joinedAt: string): void {\n const at = now();\n const previous = new Map(liveRoster.map((p) => [p.identity, p]));\n const present = new Set(list.map((p) => p.identity));\n const graceMs = connectionPolicy.reconnectGraceSeconds * 1000;\n for (const [who, reason] of departures) {\n const last = previous.get(who);\n if (last !== undefined && !last.isLocal && !present.has(who) && graceMs > 0 && isUnannouncedDeparture(reason)) {\n ghosts.set(who, { participant: last, until: at + graceMs });\n }\n }\n departures.clear();\n liveRoster = list;\n // `share-stopped-by-browser`: the browser's own \"Stop sharing\" ends the track and LiveKit\n // unpublishes it — the share region follows the roster, never a stale \"presenting\".\n const localNow = list.find((p) => p.isLocal);\n if (localNow !== undefined && !localNow.screenSharing && store.snapshot().share.state === \"presenting\") {\n store.patch({ share: NO_SHARE });\n }\n const merged = mergeGhosts(list, ghosts, at);\n ghosts = merged.kept;\n // S7: ONE TILE PER PERSON. A person on two tabs/devices is two LiveKit connections; the tile\n // shows the one to look at — this tab first, then a live main connection, then any.\n const people = new Map<string, { entry: (typeof merged.roster)[number]; rank: number }>();\n const connections = new Map<string, string[]>();\n for (const entry of merged.roster) {\n const person = personOf(entry.identity);\n connections.set(person, [...(connections.get(person) ?? []), entry.identity]);\n const rank =\n (entry.isLocal ? 8 : 0) +\n (entry.reconnecting === true ? 0 : 4) +\n (entry.attributes[CONNECTION_KIND_ATTRIBUTE] === \"companion\" ? 0 : 2) +\n (entry.cameraEnabled || entry.micEnabled ? 1 : 0);\n const held = people.get(person);\n if (held === undefined || rank > held.rank) people.set(person, { entry, rank });\n }\n personConnections = connections;\n mainConnection = new Map([...people].map(([person, { entry }]) => [person, entry.identity]));\n store.setParticipants(\n [...people.values()].map(({ entry }) => {\n const projected = projectParticipant(entry, joinedAt);\n if (entry.reconnecting === true || (!entry.isLocal && silent.has(entry.identity))) {\n return { ...projected, reconnecting: true, quality: \"lost\" as const };\n }\n const videoPaused = entry.isLocal\n ? store.snapshot().localNetwork.videoPaused\n : liveness.reported(entry.identity)?.vp === true;\n const quality = effectiveQuality(projected.identity, entry.identity, entry.isLocal);\n return {\n ...projected,\n ...(quality !== \"unknown\" && quality !== \"lost\" ? { quality } : {}),\n ...(videoPaused ? { videoPaused: true } : {}),\n };\n }),\n );\n scheduleGhostExpiry();\n }\n\n function scheduleGhostExpiry(): void {\n if (ghostTimer !== null) clearTimeout(ghostTimer);\n ghostTimer = null;\n if (ghosts.size === 0 || disposed) return;\n const next = Math.min(...[...ghosts.values()].map((g) => g.until));\n ghostTimer = setTimeout(() => {\n ghostTimer = null;\n if (disposed || driver === null) return;\n projectRoster(liveRoster, new Date(now()).toISOString());\n }, Math.max(0, next - now()) + 50);\n }\n\n function clearGhosts(): void {\n ghosts = new Map();\n departures.clear();\n liveRoster = [];\n if (ghostTimer !== null) clearTimeout(ghostTimer);\n ghostTimer = null;\n }\n\n const supervisor = createConnectionSupervisor({\n store,\n network: options.connection?.network ?? createWebNetworkPort(),\n probe: () => options.connection?.probe?.(current?.serverUrl ?? lastServerUrl) ?? Promise.resolve(true),\n policy: () => connectionPolicy,\n autoRejoin: () => engine.autoRejoin(),\n currentRequest: requestAsNow,\n resume: options.connection?.resume,\n now,\n report: (level, message) => report(level, message),\n });\n\n const engine: RoomEngine = {\n join(request) {\n // SINGLE FLIGHT: a strict-mode double effect and a user's second click\n // share one join. Two joins for one identity make LiveKit drop the first.\n if (joining !== null) return joining;\n // THE PERSON'S PRESS (§3.1): prejoin / left / disconnected / superseded → joining. An\n // answered call or an admission already moved the phase to `joining`.\n if (store.snapshot().phase !== \"joining\" && !store.dispatch({ type: \"join\", click: true })) {\n report(\n \"warn\",\n `meet.join: cannot join from \"${store.snapshot().phase}\"`,\n \"Join is offered from the pre-join, left, disconnected and replaced screens only.\",\n );\n return Promise.resolve();\n }\n lastRequest = request;\n joining = connect(request)\n .catch((error: unknown) => {\n throw failJoin(error, \"meet.join\");\n })\n .finally(() => {\n joining = null;\n });\n return joining;\n },\n async rejoin() {\n const request = lastRequest;\n if (request === null) {\n // Nothing to rejoin is not an error and never a silent no-op: it means\n // this engine never joined, which a caller can only reach by wiring the\n // admission signal to a room nobody is waiting in.\n report(\n \"warn\",\n \"meet.rejoin: this room engine has never joined, so there is nothing to rejoin\",\n \"Call join() first. An admission verdict for a participant who never knocked is \" +\n \"ignored rather than minting a token nobody asked for.\",\n );\n return;\n }\n if (joining !== null) return joining;\n // ADMITTED from the waiting room, or the person's own Rejoin (disconnected / left /\n // replaced) — the two ways in that are not the first Join (§3.1).\n const phase = store.snapshot().phase;\n // `join` covers the person's presses (Rejoin, Ask to join again); `admitted` the server's yes.\n const moved =\n phase === \"joining\" ||\n store.dispatch({ type: \"join\", click: true }) ||\n store.dispatch({ type: \"admitted\" });\n if (!moved) {\n report(\"warn\", `meet.rejoin: cannot rejoin from \"${phase}\"`, \"Rejoin is offered after leaving, a lost connection, or admission.\");\n return;\n }\n // THE LOBBY TOKEN IS THROWN AWAY. It grants neither publish nor\n // subscribe; reusing it would put the admitted guest in the room as a\n // participant who can neither speak nor hear.\n tokens.invalidate(request.roomName);\n current = null;\n joining = connect(request)\n .catch((error: unknown) => {\n throw failJoin(error, \"meet.rejoin\");\n })\n .finally(() => {\n joining = null;\n });\n return joining;\n },\n async autoRejoin() {\n const request = lastRequest;\n if (request === null || joining !== null) return joining ?? undefined;\n // Not a click: only `disconnected` → `joining` (§3.1). Anything else (the person already\n // pressed Rejoin, left, or the meeting ended) means there is nothing to do.\n if (!store.dispatch({ type: \"auto_rejoin\" })) return;\n tokens.invalidate(request.roomName);\n current = null;\n joining = connect(request)\n .catch((error: unknown) => {\n throw failJoin(error, \"meet.autoRejoin\");\n })\n .finally(() => {\n joining = null;\n });\n return joining;\n },\n resume(roomName) {\n const marker = supervisor.resumable(roomName);\n if (marker === null || joining !== null) return false;\n if (!store.dispatch({ type: \"resumed\" })) return false;\n lastRequest = { ...marker.request, roomName };\n report(\"info\", \"meet: this tab was in the call before the page reloaded — rejoining\");\n tokens.invalidate(roomName);\n current = null;\n joining = connect(lastRequest)\n .catch((error: unknown) => {\n throw failJoin(error, \"meet.resume\");\n })\n .finally(() => {\n joining = null;\n });\n joining.catch(() => undefined);\n return true;\n },\n async moveToBreakout(roomId) {\n const request = lastRequest;\n if (request === null || driver === null) {\n report(\n \"warn\",\n \"meet.moveToBreakout: this engine is not in a meeting room\",\n \"Join the meeting first; a breakout is a room OF a meeting.\",\n );\n return;\n }\n if ((store.snapshot().breakoutRoomId ?? null) === roomId) return;\n if (joining !== null) await joining.catch(() => undefined);\n const previous = driver;\n const next: JoinRequest = {\n ...request,\n breakoutRoomId: roomId,\n // Arrive as you were: camera and microphone as they are now.\n mode: store.snapshot().media.camera.enabled ? \"video\" : \"audio\",\n microphone: store.snapshot().media.microphone.enabled,\n };\n moving = true;\n try {\n driver = null;\n tokens.invalidate(request.roomName);\n current = null;\n await previous.disconnect();\n lastRequest = next;\n joining = connect(next).finally(() => {\n joining = null;\n });\n await joining;\n } catch (error) {\n // THE WAY BACK IS ALWAYS OPEN: a refused breakout returns the person to the\n // main room rather than leaving them in no room at all.\n const normalized = normalizeMeetError(error, \"meet.moveToBreakout\");\n report(\"warn\", normalized.message, normalized.remedy);\n if (roomId !== null) {\n lastRequest = { ...next, breakoutRoomId: null };\n joining = connect(lastRequest).finally(() => {\n joining = null;\n });\n await joining.catch(async (again: unknown) => {\n throw await lostRoom(again, \"meet.moveToBreakout\");\n });\n } else {\n throw await lostRoom(error, \"meet.moveToBreakout\");\n }\n throw normalized;\n } finally {\n moving = false;\n }\n },\n leave: leaveRoom,\n async checkElsewhere(roomName) {\n const claim = options.tabClaim;\n const userId = identity.userId;\n if (claim === undefined || userId === null) return false;\n const before = store.snapshot();\n if (before.phase !== \"prejoin\" || before.secondDevice !== null) return false;\n const person = `user:${String(userId)}`;\n const found =\n claim.presenceElsewhere !== undefined\n ? await claim.presenceElsewhere(String(roomName), person)\n : (await claim.presentElsewhere(String(roomName), person))\n ? { rule: null }\n : null;\n // `allow_both` (Zoom): nobody is asked — Join adds this tab, as the server would.\n if (found === null || found.rule === \"allow_both\") return false;\n // The person may have pressed Join (or left) while the other tabs answered.\n const now = store.snapshot();\n if (now.phase !== \"prejoin\" || now.secondDevice !== null) return false;\n // The live tab's word for the meeting's `second_device` rule (Meet's choice when unknown).\n store.patch({ secondDevice: { rule: found.rule ?? \"ask_switch_or_join\" } });\n report(\"info\", \"meet: this browser already has you in this meeting — offering the choice before Join\");\n return true;\n },\n backToPrejoin() {\n // The person's own press from `left` / `disconnected` (§3.1) — the only way back.\n store.dispatch({ type: \"back_to_prejoin\", click: true });\n },\n // Turning a device on mid-meeting is the SAME operation as turning it on\n // during the join, so it runs the same function. A failure here used to\n // stamp the room `failed` — a busy microphone would blank a live meeting.\n async setMicrophoneEnabled(enabled) {\n if (driver === null) return;\n await enableTrack(driver, \"microphone\", enabled);\n },\n async setCameraEnabled(enabled) {\n if (driver === null) return;\n const state = await enableTrack(driver, \"camera\", enabled);\n // `net-video-auto-off` \"Try video again\": the person's own choice wins; no re-pause until\n // the path has recovered once.\n if (enabled && state.enabled && store.snapshot().localNetwork.videoPaused) {\n autoOff.retried();\n patchLocalNetwork({ videoPaused: false });\n }\n if (!enabled && store.snapshot().localNetwork.videoPaused) patchLocalNetwork({ videoPaused: false });\n },\n async setScreenShareEnabled(enabled) {\n if (driver === null) return;\n // 🚨 AN IN-CALL ACTION NEVER CHANGES THE CALL PHASE (CORE-DESIGN §3.3 SA). Every outcome\n // lands in the `share` REGION; there is no phase event for it at all.\n // A probe that itself throws is not \"unsupported\": try the share, and let ITS outcome say.\n let unsupported = false;\n try {\n unsupported = enabled && driver.supportsScreenShare?.() === false;\n } catch {\n unsupported = false;\n }\n if (unsupported) {\n // This browser cannot share (no getDisplayMedia): say so, keep the call.\n store.patch({ share: { state: \"refused\", reason: \"unsupported\", remedy: null, at: now() } });\n report(\"warn\", \"meet: this browser cannot share a screen\", \"Share from a desktop browser (Chrome, Edge, Firefox or Safari).\");\n return;\n }\n if (enabled) store.patch({ share: { state: \"picking\", reason: null, remedy: null, at: now() } });\n try {\n await driver.setScreenShareEnabled(enabled);\n ambiguousShareDenials = 0;\n const audio = enabled ? driver?.screenShareHasAudio?.() : undefined;\n store.patch({\n share: { state: enabled ? \"presenting\" : \"none\", reason: null, remedy: null, at: now(), ...(audio !== undefined ? { audio } : {}) },\n });\n } catch (error) {\n const kind = classifyScreenShareError(error);\n if (kind === \"ambiguous\") {\n // Closing the picker is a choice, and some browsers cannot tell it from a\n // block: stay silent once, explain on the second consecutive refusal.\n ambiguousShareDenials += 1;\n if (ambiguousShareDenials < 2) {\n store.patch({ share: NO_SHARE });\n return;\n }\n }\n ambiguousShareDenials = 0;\n const normalized = normalizeScreenShareError(error, \"meet.setScreenShareEnabled\");\n report(\"warn\", normalized.message, normalized.remedy);\n store.patch({\n share: {\n state: \"refused\",\n reason: kind === \"other\" ? \"failed\" : \"blocked\",\n remedy: kind === \"other\" ? normalized.remedy : SCREEN_SHARE_BLOCKED_REMEDY_FOR(normalized.remedy),\n at: now(),\n },\n });\n }\n },\n dismissShareNotice() {\n const state = store.snapshot().share.state;\n if (state === \"refused\" || state === \"ended\") store.patch({ share: NO_SHARE });\n },\n async react(emoji) {\n const local = store.snapshot().localIdentity;\n const id = `${local ?? \"local\"}-${now()}`;\n if (local !== null) {\n store.addReaction({ id, identity: local, emoji, at: now() });\n }\n await publish(DATA_TOPICS.reaction, { id, emoji });\n },\n async sendPrivateChat(to, text) {\n const trimmed = text.trim();\n if (trimmed.length === 0) return;\n const local = store.snapshot().localIdentity;\n const id = `${local ?? \"local\"}-${now()}`;\n const at = new Date(now()).toISOString();\n if (local !== null) {\n store.addChat({ id, identity: local, displayName: identity.displayName, text: trimmed, at, to });\n }\n await publish(\n DATA_TOPICS.chat,\n { id, text: trimmed, at, name: identity.displayName, to },\n [to],\n );\n },\n async setEffects(next) {\n const previous = store.snapshot().effects;\n const merged: MediaEffects = { ...previous, ...next };\n store.patch({ effects: merged });\n options.effectsStorage?.save(merged);\n if (driver === null) return;\n try {\n await applyEffects(driver, merged);\n } catch {\n // The camera keeps running without the effect; the choice reverts so\n // the control never claims an effect that is not on screen.\n const reverted: MediaEffects = { ...merged, background: previous.background, backgroundImage: previous.backgroundImage };\n store.patch({ effects: reverted });\n options.effectsStorage?.save(reverted);\n }\n },\n supportsVideoEffects() {\n return driver?.supportsVideoEffects?.() ?? false;\n },\n supportsScreenShare() {\n // Before a room exists the browser itself is asked (a phone never shows Share screen).\n return driver?.supportsScreenShare?.() ?? detectCapabilities().canShareScreen;\n },\n async switchDevice(kind, deviceId) {\n if (driver === null) return;\n await driver.switchDevice(kind, deviceId);\n },\n enableAudio: () => media.enableAudio(),\n previewDevice: (kind, outcome) => media.preview(kind, outcome),\n acceptDeviceOffer: () => media.acceptOffer(),\n dismissDeviceOffer: () => media.dismissOffer(),\n dismissDeviceNotice: () => media.dismissNotice(),\n undoDeviceNotice: () => media.undoNotice(),\n pin(who) {\n store.patch({ pinned: who, layout: who === null ? \"grid\" : \"speaker\" });\n },\n setLayout(layout) {\n store.patch({ layout });\n },\n attach(who, kind, element) {\n if (driver === null) return () => undefined;\n // S7: the tile is the person; its media is their main connection's.\n return driver.attach(mainConnection.get(personOf(who)) ?? who, kind, element);\n },\n token: () => current,\n async dispose() {\n disposed = true;\n supervisor.dispose();\n media.stop();\n clearGhosts();\n if (driver !== null) {\n const target = driver;\n driver = null;\n await target.disconnect();\n }\n store.reset();\n },\n };\n return engine;\n}\n\n/** Exported for the host-power check in UI code — one definition, one rank. */\nexport function canModerate(participant: MeetParticipant | undefined): boolean {\n return participant !== undefined && isHostRole(participant.role);\n}\n\nexport function hostUserIds(participants: readonly MeetParticipant[]): readonly UserId[] {\n return participants\n .filter((participant) => participant.role === \"host\" && participant.userId !== null)\n .map((participant) => participant.userId as UserId);\n}\n","/**\n * A PARTICIPANT'S OWN MEDIA CHOICES, REMEMBERED (Meet wave 2).\n *\n * Noise suppression and the camera background are personal and per device —\n * Zoom and Meet both remember them — so they live in this browser, never on a\n * server. Blocked storage means the defaults (suppression on, no background),\n * never a failed join.\n */\n\nimport type { EffectsStorage } from \"./room\";\nimport { DEFAULT_EFFECTS, type MediaEffects } from \"./store\";\n\nfunction coerce(value: unknown): MediaEffects {\n if (typeof value !== \"object\" || value === null) return DEFAULT_EFFECTS;\n const row = value as Record<string, unknown>;\n const background =\n row[\"background\"] === \"blur\" || row[\"background\"] === \"image\" ? row[\"background\"] : \"none\";\n const backgroundImage = typeof row[\"backgroundImage\"] === \"string\" ? row[\"backgroundImage\"] : null;\n return {\n noiseSuppression: row[\"noiseSuppression\"] !== false,\n background: background === \"image\" && backgroundImage === null ? \"none\" : background,\n backgroundImage,\n };\n}\n\nexport function createWebEffectsStorage(key = \"mx.meet.effects\"): EffectsStorage {\n let memory: MediaEffects = DEFAULT_EFFECTS;\n const storage = (globalThis as { localStorage?: Storage }).localStorage;\n return {\n load() {\n if (storage === undefined) return memory;\n try {\n const raw = storage.getItem(key);\n return raw === null ? memory : coerce(JSON.parse(raw));\n } catch {\n return memory;\n }\n },\n save(effects) {\n memory = effects;\n try {\n storage?.setItem(key, JSON.stringify(effects));\n } catch {\n // Storage blocked: the choice lasts for this visit.\n }\n },\n };\n}\n","/**\n * THE IN-ROOM SERVER CALLS (Meet wave 2) — host moderation and the SAVED chat.\n *\n * 🚨 **Why these are server calls and not data packets.** Through 0.7.x a host's\n * \"Mute\" and \"Remove\" were published on the room's data plane (`mx.host`) and\n * nothing anywhere listened: the buttons did nothing. A client cannot be the\n * authority on host power, and a guest cannot write the database. So both go to\n * aidream (`POST /v1/meet/moderate`, `POST|GET /v1/meet/chat`), which decides the\n * actor's role from the meeting itself, performs the act through LiveKit's server\n * API, writes what is durable, and tells the room on its own `matrx.meet.*` topic.\n *\n * A guest proves who they are with the meeting pass this server signed for them, in\n * `x-meet-pass` — a header, never a URL.\n */\n\nimport { refusalFromResponse } from \"./ai\";\nimport type { MeetApi } from \"./api\";\nimport { MeetError } from \"./errors\";\nimport { collabFromState, withItem } from \"./collab\";\nimport { chatFromRow } from \"./room-events\";\nimport type { MeetStore, RoomChatMessage } from \"./store\";\nimport { MEET_ROUTES, MEETING_PASS_HEADER } from \"./tokens\";\nimport type { ParticipantIdentity } from \"./types\";\n\n/** Every host power the server performs. One vocabulary with `services/meet/moderation.py`. */\nexport type ModerationAction =\n | \"mute\"\n | \"stop_video\"\n | \"stop_screen\"\n | \"mute_all\"\n | \"ask_unmute\"\n | \"ask_all_unmute\"\n | \"spotlight\"\n | \"sync_presenter\"\n | \"raise_hand\"\n | \"lower_hand\"\n | \"lower_all_hands\"\n | \"make_cohost\"\n | \"make_presenter\"\n | \"revoke_presenter\"\n | \"set_av_lock\"\n | \"revoke_cohost\"\n | \"transfer_host\"\n | \"reclaim_host\"\n | \"rename\"\n | \"remove\"\n | \"report\";\n\nexport const MODERATION_ACTIONS: readonly ModerationAction[] = [\n \"mute\",\n \"stop_video\",\n \"stop_screen\",\n \"mute_all\",\n \"ask_unmute\",\n \"ask_all_unmute\",\n \"spotlight\",\n \"sync_presenter\",\n \"raise_hand\",\n \"lower_hand\",\n \"lower_all_hands\",\n \"make_cohost\",\n \"make_presenter\",\n \"revoke_presenter\",\n \"set_av_lock\",\n \"revoke_cohost\",\n \"transfer_host\",\n \"reclaim_host\",\n \"rename\",\n \"remove\",\n \"report\",\n];\n\nexport interface ModerationRequest {\n readonly action: ModerationAction;\n readonly identity?: ParticipantIdentity | null | undefined;\n readonly name?: string | undefined;\n readonly value?: string | undefined;\n readonly reason?: string | undefined;\n}\n\nexport interface ModerationResult {\n readonly action: ModerationAction;\n readonly affected: readonly string[];\n /** The server's sentence for the actor. */\n readonly message: string;\n}\n\nexport interface InRoomClient {\n moderate(request: ModerationRequest): Promise<ModerationResult>;\n /** Send a message to EVERYONE. Saved; shows at once as pending, then as saved. */\n sendChat(text: string): Promise<void>;\n /** Read the saved chat once per join, so a late joiner sees what was said. */\n loadChat(): Promise<void>;\n /**\n * Meet wave 5: read the polls, questions, whiteboard and breakout plan this viewer\n * may see, once per join. A refusal is written to `collab.error`, never thrown away.\n */\n loadCollab(): Promise<void>;\n /**\n * Meet wave 5: one poll / Q&A / whiteboard / breakout action. The server's answer is\n * folded into the store at once (the room packet that follows is the same item).\n * Returns the server's sentence; throws a `MeetError` with the server's words.\n */\n collab(request: CollabRequest): Promise<CollabResult>;\n /**\n * Meet wave 5: translate finished caption lines into `language` for this viewer.\n * Results land in `collab.captionTranslations`; a refusal in `captionTranslationError`.\n */\n translateCaptions(language: string, lines: readonly { id: string; text: string }[]): Promise<void>;\n}\n\n/** Every room-item action the server performs. One vocabulary with `services/meet/collab.py`. */\nexport type CollabAction =\n | \"poll_create\"\n | \"poll_update\"\n | \"poll_launch\"\n | \"poll_close\"\n | \"poll_share\"\n | \"poll_vote\"\n | \"poll_delete\"\n | \"question_ask\"\n | \"question_upvote\"\n | \"question_answer\"\n | \"question_reopen\"\n | \"question_dismiss\"\n | \"question_delete\"\n | \"stroke_add\"\n | \"stroke_erase\"\n | \"whiteboard_clear\"\n | \"breakout_open\"\n | \"breakout_update\"\n | \"breakout_broadcast\"\n | \"breakout_move\"\n | \"breakout_join\"\n | \"breakout_close\"\n | \"breakout_expire\"\n /** HR-360 wave 2 (MD-15): move everyone's app panel to one section. */\n | \"focus_set\";\n\nexport interface CollabRequest {\n readonly action: CollabAction;\n readonly itemId?: string | undefined;\n readonly question?: string | undefined;\n readonly options?: readonly string[] | undefined;\n readonly multiple?: boolean | undefined;\n readonly anonymous?: boolean | undefined;\n readonly optionIds?: readonly string[] | undefined;\n readonly text?: string | undefined;\n readonly stroke?:\n | {\n readonly points: readonly (readonly [number, number])[];\n readonly color: string;\n readonly width: number;\n readonly tool: \"pen\" | \"highlighter\";\n }\n | undefined;\n readonly rooms?: readonly { readonly name: string; readonly identities: readonly string[] }[] | undefined;\n readonly allowChoose?: boolean | undefined;\n readonly minutes?: number | undefined;\n readonly identity?: string | undefined;\n /** `null` = the main room (breakout_join / breakout_move). */\n readonly roomId?: string | null | undefined;\n /** focus_set: the app panel's key and the section everyone looks at. */\n readonly panel?: string | undefined;\n readonly section?: string | undefined;\n}\n\nexport interface CollabResult {\n readonly action: CollabAction;\n readonly message: string;\n readonly removed: readonly string[];\n /** The id of the item the server returned (a created poll, a new question), or null. */\n readonly itemId: string | null;\n}\n\nexport interface InRoomClientOptions {\n api: MeetApi;\n store: MeetStore;\n /** The room pass for THIS meeting, or null (a signed-in member needs none). */\n resolveRoomToken: () => string | null;\n now?: (() => number) | undefined;\n}\n\nexport function createInRoomClient(options: InRoomClientOptions): InRoomClient {\n const { api, store } = options;\n const now = options.now ?? (() => Date.now());\n\n function meetingId(operation: string): string {\n const meeting = store.snapshot().meeting;\n if (meeting === null) {\n throw new MeetError(\n \"not-found\",\n `${operation}: this room has no meeting record`,\n \"Moderation and saved chat belong to a meeting. An ad-hoc call has neither.\",\n );\n }\n return meeting.id;\n }\n\n function headers(): Record<string, string> | undefined {\n const token = options.resolveRoomToken();\n return token === null ? undefined : { [MEETING_PASS_HEADER]: token };\n }\n\n function refusal(response: Response, operation: string, remedy: string): Promise<MeetError> {\n return refusalFromResponse(response, operation, remedy);\n }\n\n // S8 PRESENTER IS TRUTH FROM THE MEDIA. Who presents is the run's (replicated room state) and\n // the SERVER decides it from the screen-share track it sees in the room (track webhooks, and\n // this nudge) — a client never claims it. The nudge carries no claim: \"my share may have\n // changed, look\". It is derived from the local screen-share track, once per change, because\n // that track also ends when the browser's own \"Stop sharing\" bar (or the shared window\n // closing) kills it — a path no button of ours sees.\n let toldSharing = false;\n let tellChain: Promise<void> = Promise.resolve();\n store.subscribe(() => {\n const local = store.snapshot().participants.find((p) => p.isLocal);\n const sharing = local?.screenSharing === true;\n if (sharing === toldSharing || local === undefined) return;\n toldSharing = sharing;\n tellChain = tellChain\n .then(() => client.moderate({ action: \"sync_presenter\" }))\n .then(\n () => undefined,\n (error: unknown) => {\n // Not retried on every store change (that would be a storm). The share itself is\n // media and keeps working; the track webhook still tells the server.\n console.warn(`[meet] the server did not take the screen-share change: ${String(error)}`);\n },\n );\n });\n\n const client: InRoomClient = {\n async moderate(request) {\n const operation = `meet.moderate(${request.action})`;\n const hdrs = headers();\n const response = await api.request({\n path: MEET_ROUTES.moderate,\n method: \"POST\",\n operation,\n body: {\n meeting_id: meetingId(operation),\n action: request.action,\n ...(request.identity != null ? { identity: request.identity } : {}),\n ...(request.name !== undefined ? { name: request.name } : {}),\n ...(request.value !== undefined ? { value: request.value } : {}),\n ...(request.reason !== undefined ? { reason: request.reason } : {}),\n },\n ...(hdrs !== undefined ? { headers: hdrs } : {}),\n });\n if (!response.ok) {\n throw await refusal(\n response,\n operation,\n \"Nothing was changed. The server decides host powers from the meeting itself.\",\n );\n }\n const payload = (await response.json()) as Record<string, unknown>;\n return {\n action: request.action,\n affected: Array.isArray(payload[\"affected\"])\n ? payload[\"affected\"].filter((value): value is string => typeof value === \"string\")\n : [],\n message: typeof payload[\"message\"] === \"string\" ? payload[\"message\"] : \"Done.\",\n };\n },\n\n async sendChat(text) {\n const body = text.trim();\n if (body.length === 0) return;\n const operation = \"meet.sendChat\";\n const id = meetingId(operation);\n const snapshot = store.snapshot();\n const me = snapshot.participants.find((p) => p.identity === snapshot.localIdentity);\n // PENDING AT ONCE: a message that appears only after a round trip feels\n // broken. It is marked pending (not `saved`) until the server's row lands.\n const localId = `pending-${now()}`;\n const pending: RoomChatMessage = {\n id: localId,\n identity: snapshot.localIdentity ?? (\"local\" as ParticipantIdentity),\n displayName: me?.displayName ?? \"You\",\n text: body,\n at: new Date(now()).toISOString(),\n saved: false,\n };\n store.addChat(pending);\n const hdrs = headers();\n let response: Response;\n try {\n response = await api.request({\n path: MEET_ROUTES.chat,\n method: \"POST\",\n operation,\n body: { meeting_id: id, body },\n ...(hdrs !== undefined ? { headers: hdrs } : {}),\n });\n } catch (error) {\n store.settleChat(localId, { failed: \"Not sent — the server could not be reached.\" });\n throw error;\n }\n if (!response.ok) {\n const error = await refusal(response, operation, \"Your message was not sent.\");\n store.settleChat(localId, { failed: error.message });\n throw error;\n }\n const saved = chatFromRow((await response.json()) as Record<string, unknown>);\n if (saved === null) {\n store.settleChat(localId, { failed: \"The server's answer could not be read.\" });\n return;\n }\n store.settleChat(localId, saved);\n },\n\n async loadChat() {\n const operation = \"meet.loadChat\";\n const id = meetingId(operation);\n const hdrs = headers();\n const response = await api.request({\n path: `${MEET_ROUTES.chat}?meeting_id=${encodeURIComponent(id)}`,\n method: \"GET\",\n operation,\n ...(hdrs !== undefined ? { headers: hdrs } : {}),\n });\n if (!response.ok) {\n throw await refusal(\n response,\n operation,\n \"Messages sent before you joined could not be read. New messages still arrive.\",\n );\n }\n const payload = (await response.json()) as { messages?: unknown };\n const rows = Array.isArray(payload.messages) ? payload.messages : [];\n const messages = rows\n .map((row) =>\n typeof row === \"object\" && row !== null ? chatFromRow(row as Record<string, unknown>) : null,\n )\n .filter((message): message is RoomChatMessage => message !== null);\n store.loadChat(messages);\n },\n\n async loadCollab() {\n const operation = \"meet.loadCollab\";\n const id = meetingId(operation);\n const hdrs = headers();\n const response = await api.request({\n path: `${MEET_ROUTES.collab}?meeting_id=${encodeURIComponent(id)}`,\n method: \"GET\",\n operation,\n ...(hdrs !== undefined ? { headers: hdrs } : {}),\n });\n if (!response.ok) {\n const error = await refusal(\n response,\n operation,\n \"Polls, questions and the whiteboard could not be read. New ones still arrive.\",\n );\n store.patch({ collab: { ...store.snapshot().collab, loaded: true, error: error.message } });\n throw error;\n }\n const body = (await response.json()) as Record<string, unknown>;\n store.patch({ collab: collabFromState(body, store.snapshot().collab) });\n },\n\n async collab(request) {\n const operation = `meet.collab(${request.action})`;\n const hdrs = headers();\n const response = await api.request({\n path: MEET_ROUTES.collab,\n method: \"POST\",\n operation,\n body: {\n meeting_id: meetingId(operation),\n action: request.action,\n ...(request.itemId !== undefined ? { item_id: request.itemId } : {}),\n ...(request.question !== undefined ? { question: request.question } : {}),\n ...(request.options !== undefined ? { options: request.options } : {}),\n ...(request.multiple !== undefined ? { multiple: request.multiple } : {}),\n ...(request.anonymous !== undefined ? { anonymous: request.anonymous } : {}),\n ...(request.optionIds !== undefined ? { option_ids: request.optionIds } : {}),\n ...(request.text !== undefined ? { text: request.text } : {}),\n ...(request.stroke !== undefined ? { stroke: request.stroke } : {}),\n ...(request.rooms !== undefined ? { rooms: request.rooms } : {}),\n ...(request.allowChoose !== undefined ? { allow_choose: request.allowChoose } : {}),\n ...(request.minutes !== undefined ? { minutes: request.minutes } : {}),\n ...(request.identity !== undefined ? { identity: request.identity } : {}),\n ...(request.roomId !== undefined ? { room_id: request.roomId } : {}),\n ...(request.panel !== undefined ? { panel: request.panel } : {}),\n ...(request.section !== undefined ? { section: request.section } : {}),\n },\n ...(hdrs !== undefined ? { headers: hdrs } : {}),\n });\n if (!response.ok) {\n throw await refusal(response, operation, \"Nothing was changed.\");\n }\n const payload = (await response.json()) as Record<string, unknown>;\n const removed = Array.isArray(payload[\"removed\"])\n ? payload[\"removed\"].filter((v): v is string => typeof v === \"string\")\n : [];\n let next = store.snapshot().collab;\n const item = payload[\"item\"];\n if (typeof item === \"object\" && item !== null) next = withItem(next, item as Record<string, unknown>);\n if (removed.length > 0) {\n const gone = new Set(removed);\n next = {\n ...next,\n polls: next.polls.filter((p) => !gone.has(p.id)),\n questions: next.questions.filter((q) => !gone.has(q.id)),\n strokes: next.strokes.filter((st) => !gone.has(st.id)),\n };\n }\n store.patch({ collab: next });\n const itemId =\n typeof item === \"object\" && item !== null && typeof (item as Record<string, unknown>)[\"id\"] === \"string\"\n ? ((item as Record<string, unknown>)[\"id\"] as string)\n : null;\n return {\n action: request.action,\n message: typeof payload[\"message\"] === \"string\" ? payload[\"message\"] : \"Done.\",\n removed,\n itemId,\n };\n },\n\n async translateCaptions(language, lines) {\n if (lines.length === 0) return;\n const operation = \"meet.translateCaptions\";\n const hdrs = headers();\n const response = await api.request({\n path: MEET_ROUTES.captionsTranslate,\n method: \"POST\",\n operation,\n body: { meeting_id: meetingId(operation), target_language: language, lines },\n ...(hdrs !== undefined ? { headers: hdrs } : {}),\n });\n if (!response.ok) {\n const error = await refusal(response, operation, \"Captions stay in the language they were spoken.\");\n store.patch({\n collab: { ...store.snapshot().collab, captionTranslationError: error.message },\n });\n throw error;\n }\n const payload = (await response.json()) as { translations?: Record<string, unknown> };\n const current = store.snapshot().collab;\n // A reply for a language the viewer has since left is not shown.\n if (current.captionLanguage !== language) return;\n const merged: Record<string, string> = { ...current.captionTranslations };\n for (const [id, value] of Object.entries(payload.translations ?? {})) {\n if (typeof value === \"string\") merged[id] = value;\n }\n store.patch({\n collab: { ...current, captionTranslations: merged, captionTranslationError: null },\n });\n },\n };\n return client;\n}\n","/**\n * THE MEETING RECORD — what a meeting IS once the room is gone (MRI-D2).\n *\n * 🚨 THE DEFECT THIS EXISTS TO KILL. Through 2026-09-08 a meeting produced a\n * wrap-up summary, decisions, owner-assigned action items, five windows of live\n * notes, thirty-two transcript segments and a recording — and then had NO\n * SURFACE AT ALL. `/meet/<slug>` fell back to `<PreJoin>` on an ended meeting,\n * so the durable link that R3 promises would outlive the meeting resolved to a\n * screen offering to join a room that no longer exists. Everything the campaign\n * built after the room closed was reachable only from SQL.\n *\n * Three rules hold this file together:\n *\n * 1. **Every read is INDEPENDENT and every refusal is NAMED.** The transcript,\n * the notes, the attendance list and the recording live in four RLS-guarded\n * tables, and a guest holding the link can read NONE of them (only\n * `meet_meeting_by_slug` is granted to `anon`). One `Promise.all` would turn\n * a permission boundary into a blank page. Known guest restrictions never issue private reads; each authenticated read that is refused\n * becomes a `MeetingRecordRestriction` with a sentence — the reader is told\n * what exists and why they cannot see it, which is the difference between a\n * record they can ask for and a product that looks broken.\n * 2. **A partial record is still a record.** A failed transcript read never\n * hides the summary somebody CAN read.\n * 3. **Nothing here computes meaning.** Grouping, ordering and formatting only.\n * The summary, decisions and action items are durable rows written by the\n * wrap-up mandate (D5/D18); this module never re-derives them from the\n * transcript, because two sources for one fact is how they start to disagree.\n */\n\nimport { formatDurationMs } from \"@ai-matrx/kit/format\";\nimport type { MeetApi } from \"./api\";\nimport { MeetError } from \"./errors\";\nimport { displayNameFor, learnNames, type NameBook } from \"./names\";\nimport type { MeetRepository } from \"./repository\";\nimport { MEET_ROUTES, MEETING_PASS_HEADER, type RoomToken } from \"./tokens\";\nimport type {\n MeetingAttendee,\n MeetingId,\n MeetingNote,\n MeetIdentity,\n MeetingRecord,\n OrganizationId,\n ParticipantIdentity,\n RecordingRecord,\n TranscriptSegment,\n} from \"./types\";\n\n/** One thing the reader could not see, and the honest reason. Never a blank. */\nexport interface MeetingRecordRestriction {\n /** Which part of the record: `transcript`, `notes`, `attendance`, `recording`. */\n readonly part: \"transcript\" | \"notes\" | \"attendance\" | \"recording\";\n readonly message: string;\n readonly remedy: string;\n}\n\n/** One visible access state, made from restrictions that share one remedy. */\nexport interface MeetingRecordRestrictionNotice {\n readonly parts: readonly MeetingRecordRestriction[\"part\"][];\n readonly message: string;\n readonly remedy: string;\n}\n\n/** A run of consecutive lines by one speaker — how a person reads a transcript. */\nexport interface TranscriptBlock {\n readonly id: string;\n /** Who spoke. Blocks break on THIS, never on the rendered name (MRI-A16). */\n readonly identity: ParticipantIdentity;\n readonly speaker: string;\n /** ISO of the first line in the block. */\n readonly startedAt: string;\n readonly lines: readonly TranscriptSegment[];\n}\n\nexport interface MeetingRecordBundle {\n readonly meeting: MeetingRecord;\n readonly attendees: readonly MeetingAttendee[];\n /** The wrap-up summary: `kind='note'` with `metadata.role='summary'` (D5). */\n readonly summary: MeetingNote | null;\n /** D20: the AUTHORITATIVE set — window rows the wrap-up superseded are out. */\n readonly decisions: readonly MeetingNote[];\n readonly actionItems: readonly MeetingNote[];\n /** The live-notes windows, in window order (D3). */\n readonly liveNotes: readonly MeetingNote[];\n readonly transcript: readonly TranscriptSegment[];\n /** The most recent recording attempt, whatever state it reached. */\n readonly recording: RecordingRecord | null;\n /** What this reader could NOT see. Rendered; never swallowed. */\n readonly restrictions: readonly MeetingRecordRestriction[];\n /**\n * Identity → name, learned from everything this read COULD see: the durable\n * attendance rows and the transcript's own `speaker` column. It is what stops\n * a raw LiveKit token reaching the screen when one of those reads was refused\n * (MRI-A16).\n */\n readonly names: NameBook;\n}\n\n/**\n * The current wrap-up summary. Repairs preserve the prior durable row for\n * history, then mark it `superseded_by`; every current-facing projection must\n * therefore make the same distinction as decisions and action items.\n */\nexport function activeWrapUpSummary(notes: readonly MeetingNote[]): MeetingNote | null {\n return notes.find((note) => note.role === \"summary\" && note.supersededBy === null) ?? null;\n}\n\n/**\n * The sentence a reader gets when the database refuses them a part of the\n * record. It is deliberately about TENANCY, not about them: `meet_notes`,\n * `meet_transcript_segments`, `meet_participants` and `meet_recordings` have no\n * `anon` policy at all, so a link-follower with no session reaches exactly the\n * meeting row and nothing else. That is the correct posture and it must be\n * SAID, not rendered as emptiness.\n */\nconst RESTRICTION_REMEDY =\n \"Open it from the meeting link in the browser you attended with, or ask the host to share it.\";\n\nconst PART_LABEL: Readonly<Record<MeetingRecordRestriction[\"part\"], string>> = {\n transcript: \"The transcript of this meeting is not readable from this account.\",\n notes: \"The notes, decisions and action items are not readable from this account.\",\n attendance: \"Who attended is not readable from this account.\",\n recording: \"The recording is not readable from this account.\",\n};\n\n/** A refusal is DATA. A transport wobble is not, and is re-thrown as itself. */\nfunction restrictionFor(\n part: MeetingRecordRestriction[\"part\"],\n error: unknown,\n): MeetingRecordRestriction {\n const code = (error as { code?: string }).code;\n const denied =\n code === \"forbidden\" || code === \"session-unavailable\" || code === \"not-found\";\n return {\n part,\n message: denied\n ? PART_LABEL[part]\n : `${PART_LABEL[part]} (${(error as Error).message ?? \"the read failed\"})`,\n remedy: denied\n ? RESTRICTION_REMEDY\n : (error as { remedy?: string }).remedy ??\n \"This is a transient read failure, not a permission decision. Reload the page.\",\n };\n}\n\n/**\n * The four durable reads behind a record, as the database returned them, plus\n * what the reader could not see. `assembleMeetingRecord` turns them into the\n * bundle; a cache that keeps them can apply a realtime row to one part and\n * assemble again, without reading the other three (0.7.79).\n */\nexport interface MeetingRecordParts {\n readonly attendees: readonly MeetingAttendee[];\n /** Every note row (all kinds, superseded included), oldest first. */\n readonly notes: readonly MeetingNote[];\n readonly transcript: readonly TranscriptSegment[];\n /** Newest first; archived recordings are not here. */\n readonly recordings: readonly RecordingRecord[];\n readonly restrictions: readonly MeetingRecordRestriction[];\n}\n\n/**\n * Load the record parts the reader may request; state all restrictions.\n *\n * `meeting` is passed in when the caller already resolved it (the slug route\n * does, through the `anon`-granted RPC) — re-reading it through the table would\n * fail for exactly the reader who most needs the page to work.\n */\nexport async function loadMeetingRecord(\n repository: MeetRepository,\n meetingId: MeetingId,\n meeting: MeetingRecord,\n reader: Pick<MeetIdentity, \"userId\"> = { userId: null },\n): Promise<MeetingRecordBundle> {\n return assembleMeetingRecord(meeting, await loadMeetingRecordParts(repository, meetingId, reader));\n}\n\n/** The four reads, each with its own verdict (see `loadMeetingRecord`). */\nexport async function loadMeetingRecordParts(\n repository: MeetRepository,\n meetingId: MeetingId,\n reader: Pick<MeetIdentity, \"userId\"> = { userId: null },\n): Promise<MeetingRecordParts> {\n const restrictions: MeetingRecordRestriction[] = [];\n\n async function attempt<T>(\n part: MeetingRecordRestriction[\"part\"],\n run: () => Promise<T>,\n empty: T,\n ): Promise<T> {\n // No anonymous policy exists for these four tables. State the known\n // restriction without probing private data or generating expected 401s.\n if (reader.userId === null) {\n restrictions.push(restrictionFor(part, { code: \"session-unavailable\" }));\n return empty;\n }\n try {\n return await run();\n } catch (error) {\n restrictions.push(restrictionFor(part, error));\n return empty;\n }\n }\n\n // Four independent reads, four independent verdicts. Deliberately NOT\n // `Promise.all` over a single try/catch — see rule 1 in the header.\n const [attendees, notes, transcript, recordings] = await Promise.all([\n attempt<readonly MeetingAttendee[]>(\"attendance\", () => repository.attendees(meetingId), []),\n attempt<readonly MeetingNote[]>(\"notes\", () => repository.notes(meetingId), []),\n attempt<readonly TranscriptSegment[]>(\n \"transcript\",\n () => repository.transcript(meetingId),\n [],\n ),\n attempt<readonly RecordingRecord[]>(\n \"recording\",\n () => repository.recordings(meetingId),\n [],\n ),\n ]);\n return { attendees, notes, transcript, recordings, restrictions };\n}\n\n/** The bundle a reader sees, from the parts. Grouping and ordering only (rule 3). */\nexport function assembleMeetingRecord(\n meeting: MeetingRecord,\n parts: MeetingRecordParts,\n): MeetingRecordBundle {\n const { attendees, notes, transcript, recordings, restrictions } = parts;\n return {\n meeting,\n attendees,\n summary: activeWrapUpSummary(notes),\n // 🚨 D20: THE WRAP-UP IS THE AUTHORITATIVE POST-MEETING SET. A window row the\n // wrap-up superseded is still in `notes` and still in the timeline below —\n // it is simply not listed a second time as a decision that was made.\n decisions: notes.filter((note) => note.kind === \"decision\" && note.supersededBy === null),\n actionItems: notes.filter(\n (note) => note.kind === \"action-item\" && note.supersededBy === null,\n ),\n liveNotes: notes\n .filter((note) => note.kind === \"note\" && note.role === null)\n // Window order, then creation order for the notes of one window.\n .slice()\n .sort((left, right) => {\n const a = left.window ?? Number.MAX_SAFE_INTEGER;\n const b = right.window ?? Number.MAX_SAFE_INTEGER;\n return a === b ? left.createdAt.localeCompare(right.createdAt) : a - b;\n }),\n transcript,\n // `recordings` is newest first; the record shows the latest attempt,\n // including a failed one — a recording that failed is a fact the reader\n // needs, not a row to hide.\n recording: recordings[0] ?? null,\n restrictions,\n names: learnNames(learnNames({}, attendees), transcript.map((line) => ({\n identity: line.identity,\n displayName: line.speaker,\n }))),\n };\n}\n\n/**\n * 🚨 **A READ THAT WAS REFUSED IS NOT AN EMPTY RESULT (MRI-A16).**\n *\n * On production 2026-09-09 a guest's record view said *\"No wrap-up was written\n * for this meeting\"* directly underneath a panel that had just told them, in\n * the same screen, that the notes are not readable from their account — and\n * *\"Nothing was recorded as a decision\"* beside a decision that exists\n * (MRI-C8). The page asserted an absence it had no way to know, which is worse\n * than saying nothing: a person who reads \"no wrap-up was written\" stops\n * looking for one.\n *\n * So no surface writes its own empty sentence any more. It asks here, and gets\n * either its absence sentence — earned, because the read SUCCEEDED and returned\n * nothing — or the refusal, said plainly.\n */\nexport const REFUSED_ABSENCE = \"This record is not shared with guests.\";\n\n/** Was this part of the record refused to this reader? */\nexport function wasRefused(\n bundle: MeetingRecordBundle,\n part: MeetingRecordRestriction[\"part\"],\n): boolean {\n return bundle.restrictions.some((restriction) => restriction.part === part);\n}\n\n/**\n * The sentence to show where a part of the record has nothing in it. NEVER an\n * absence claim over a read that was refused.\n */\nexport function absenceSentence(\n bundle: MeetingRecordBundle,\n part: MeetingRecordRestriction[\"part\"],\n absence: string,\n): string {\n return wasRefused(bundle, part) ? REFUSED_ABSENCE : absence;\n}\n\n/**\n * 🚨 **\"NO WRAP-UP WAS WRITTEN\" IS A CLAIM, AND FOR ~33 SECONDS IT WAS FALSE.**\n *\n * On production 2026-09-09 the HOST's record view said *\"No wrap-up was written\n * for this meeting\"* between 04:46:42 — the flip from the room to the record —\n * and 04:47:15, when the wrap-up landed. The read was permitted and genuinely\n * empty, so this is not the refusal class above; it is the OTHER way a screen\n * lies, by reading \"not here yet\" as \"never happened\". A person who reads *\"no\n * wrap-up was written\"* closes the tab.\n *\n * The meeting row already knows the difference, and it is not a guess: the\n * wrap-up is a durable job, `enqueue_wrap_up` stamps\n * `metadata.note_taker.intelligence.wrap_up` when it queues it, and the graph\n * action stamps `state: \"failed\"` when the pass refuses TERMINALLY. So:\n *\n * - a summary exists → `written`;\n * - the notes read was refused → `refused` (never an absence claim);\n * - the job is marked failed → `failed`, in the server's own words;\n * - the job is queued or running → `in-progress`, with a refresh affordance;\n * - nothing ever queued one → `absent`, which is the only EARNED absence.\n *\n * There is no timeout that flips `in-progress` into `absent`. A wrap-up that is\n * genuinely stuck says it is still being written — which is TRUE — rather than\n * inventing a verdict this browser has no way to reach.\n */\nexport type WrapUpStatus =\n | { readonly state: \"written\" }\n | { readonly state: \"in-progress\"; readonly message: string; readonly canRefresh: true }\n | { readonly state: \"failed\"; readonly message: string }\n /** Nobody spoke: a normal outcome, said plainly (not an alert). */\n | { readonly state: \"empty\"; readonly message: string }\n | { readonly state: \"refused\"; readonly message: string }\n | { readonly state: \"absent\"; readonly message: string };\n\n/** What `metadata.note_taker` holds, as far as this file cares. */\nfunction noteTakerMetadata(meeting: MeetingRecord): Record<string, unknown> {\n const raw = (meeting.metadata as Record<string, unknown>)[\"note_taker\"];\n return typeof raw === \"object\" && raw !== null && !Array.isArray(raw)\n ? (raw as Record<string, unknown>)\n : {};\n}\n\nconst WRAP_UP_IN_PROGRESS =\n \"The wrap-up is being written… The summary, the decisions and the action items are \" +\n \"produced from the whole transcript once the meeting ends, which takes a moment longer \" +\n \"than the meeting does.\";\n\nconst WRAP_UP_ABSENT =\n \"No wrap-up was written for this meeting. A summary is produced from the transcript \" +\n \"when the meeting ends and the note-taker was in the room.\";\n\n/** Where the wrap-up actually is. Never \"absent\" over a job that is running. */\nexport function wrapUpStatus(bundle: MeetingRecordBundle): WrapUpStatus {\n if (bundle.summary !== null) return { state: \"written\" };\n if (wasRefused(bundle, \"notes\")) return { state: \"refused\", message: REFUSED_ABSENCE };\n\n const noteTaker = noteTakerMetadata(bundle.meeting);\n const intelligenceRaw = noteTaker[\"intelligence\"];\n const intelligence: Record<string, unknown> =\n typeof intelligenceRaw === \"object\" && intelligenceRaw !== null && !Array.isArray(intelligenceRaw)\n ? (intelligenceRaw as Record<string, unknown>)\n : {};\n const wrapUpRaw = intelligence[\"wrap_up\"];\n const wrapUp: Record<string, unknown> =\n typeof wrapUpRaw === \"object\" && wrapUpRaw !== null && !Array.isArray(wrapUpRaw)\n ? (wrapUpRaw as Record<string, unknown>)\n : {};\n\n if (wrapUp[\"state\"] === \"empty\") {\n const reason = wrapUp[\"reason\"];\n return {\n state: \"empty\",\n message:\n typeof reason === \"string\" && reason.trim().length > 0\n ? reason.trim()\n : \"No one spoke in this meeting, so there is no summary.\",\n };\n }\n\n if (wrapUp[\"state\"] === \"failed\") {\n const reason = wrapUp[\"reason\"];\n return {\n state: \"failed\",\n // THE SERVER'S OWN SENTENCE, verbatim. It already names the remedy, and a\n // house sentence over it would hide the one thing worth reading.\n message:\n typeof reason === \"string\" && reason.trim().length > 0\n ? reason.trim()\n : \"The wrap-up pass failed and no summary was written. It is not retried \" +\n \"automatically; a host can end the meeting again to re-queue it.\",\n };\n }\n\n const queued =\n typeof wrapUp[\"run_id\"] === \"string\" ||\n typeof intelligence[\"wrap_up_run_id\"] === \"string\" ||\n wrapUp[\"state\"] === \"queued\" ||\n wrapUp[\"state\"] === \"running\";\n // A meeting the note-taker actually attended will get a wrap-up even if this\n // browser loaded the record before the enqueue stamp landed — that race is\n // the whole defect, so `ended + the note-taker was there` is enough.\n const attended =\n noteTaker[\"state\"] === \"ended\" ||\n noteTaker[\"state\"] === \"transcribing\" ||\n noteTaker[\"state\"] === \"handoff_pending\";\n\n if (hasEnded(bundle.meeting) && (queued || attended)) {\n return { state: \"in-progress\", message: WRAP_UP_IN_PROGRESS, canRefresh: true };\n }\n return { state: \"absent\", message: WRAP_UP_ABSENT };\n}\n\n/**\n * GROUP THE TRANSCRIPT THE WAY A PERSON READS IT: consecutive lines by one\n * speaker become one block with one timestamp. Reading thirty-two rows each\n * re-stamped with a name and a clock is how a transcript becomes unreadable.\n */\nexport function groupTranscript(\n segments: readonly TranscriptSegment[],\n names: NameBook = {},\n): readonly TranscriptBlock[] {\n const blocks: TranscriptBlock[] = [];\n for (const segment of segments) {\n const last = blocks[blocks.length - 1];\n // 🚨 GROUPED BY IDENTITY, not by the rendered name (MRI-A16). Two people\n // whose names nobody ever learned both render \"Unnamed guest\", and merging\n // their lines into one block would put words in the wrong mouth — the exact\n // failure the honest placeholder exists to avoid.\n if (last !== undefined && last.identity === segment.identity) {\n blocks[blocks.length - 1] = { ...last, lines: [...last.lines, segment] };\n continue;\n }\n blocks.push({\n id: segment.id,\n identity: segment.identity,\n // One resolution, shared with the live rail — so the record and the room\n // can never disagree about who said something.\n speaker: displayNameFor(names, segment.identity, segment.speaker),\n startedAt: segment.startedAt,\n lines: [segment],\n });\n }\n return blocks;\n}\n\n/**\n * WHAT THE RECORDING IS DOING, in a sentence — including the two states people\n * hit most and understand least. `processing` is not \"broken\" and `failed` is\n * never silence: both say what happened and what to do.\n */\nexport function recordingNotice(recording: RecordingRecord | null): string | null {\n if (recording === null) return null;\n switch (recording.state) {\n case \"available\":\n if (recording.retention?.phase !== \"warning\") return null;\n return `This recording expires on ${retentionDueDate(recording.retention.dueAt)}. The transcript and notes remain available.`;\n case \"expired\":\n return \"This recording has expired. The transcript and notes remain available.\";\n case \"processing\":\n case \"stopping\":\n return \"The recording is still being processed. It appears here on its own once the file lands — nothing is lost while it is being written.\";\n case \"recording\":\n case \"starting\":\n return \"This recording is still being written. It becomes playable once the meeting's egress finishes.\";\n case \"failed\":\n return recording.failureReason !== null && recording.failureReason.length > 0\n ? `The recording failed: ${recording.failureReason}`\n : \"The recording failed and no file was produced. The transcript below was written separately and is unaffected.\";\n default:\n return null;\n }\n}\n\nfunction retentionDueDate(value: string): string {\n const dueAt = new Date(value);\n return Number.isNaN(dueAt.getTime()) ? \"its scheduled retention date\" : dueAt.toLocaleString();\n}\n\n/** Only an `available` recording has a file to play. Nothing else pretends to. */\nexport function playableRecordingFileId(recording: RecordingRecord | null): string | null {\n if (recording === null) return null;\n return recording.state === \"available\" ? recording.fileId : null;\n}\n\n/**\n * A MEETING IS OVER WHEN THE SERVER SAYS SO. `ended_at` is stamped by\n * `POST /v1/meet/end` or by the auto-end evaluation (MRI-A6) — never by a\n * client noticing the room is empty.\n */\nexport function hasEnded(meeting: MeetingRecord | null): boolean {\n return meeting !== null && meeting.endedAt !== null;\n}\n\n/**\n * \"Thursday, 8 September 2026, 4:21 PM · 19 minutes\". One shape everywhere.\n *\n * THE SPAN IS `formatDurationMs`'s (2026-09-12). This body used to divide by\n * `60_000` and pluralise \"minute\" itself — a duration formatter living in a\n * PACKAGE, found the day `aidream/apps/shared` became a scan root. `@ai-matrx/\n * meet` already depends on `@ai-matrx/kit`, and `style: \"long\"` is precisely\n * this voice (\"19 minutes\", \"1 minute\"). Three things improve by adopting it:\n * a 40-second meeting read \"0 minutes\" and now reads \"40 seconds\" (the\n * confident zero the package refuses), a 19-minute-35-second meeting read\n * \"20 minutes\" and now reads \"19 minutes\" (prose asserts at-least, so it\n * floors), and a 90-minute meeting reads \"1 hour\" instead of \"90 minutes\".\n */\nexport function meetingWhen(meeting: MeetingRecord, locale?: string): string {\n const start = meeting.startedAt ?? meeting.scheduledFor;\n if (start === null) return \"Time not recorded\";\n const startedAt = new Date(start);\n if (Number.isNaN(startedAt.getTime())) return \"Time not recorded\";\n const when = startedAt.toLocaleString(locale, {\n weekday: \"long\",\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n if (meeting.endedAt === null || meeting.startedAt === null) return when;\n const endedAt = new Date(meeting.endedAt);\n if (Number.isNaN(endedAt.getTime())) return when;\n const elapsedMs = endedAt.getTime() - startedAt.getTime();\n return `${when} · ${formatDurationMs(elapsedMs, { style: \"long\" })}`;\n}\n\n/* ═══════════════════════════════════════════════════════════════════════════\n * MRF-1 — WHAT A GUEST MAY READ, AND HOW THEY READ IT\n *\n * 🚨 THE DEFECT THIS CLOSES. Everything above this line is correct and was\n * still the wrong product: a guest who attended a meeting, was admitted by the\n * host, spoke, and was transcribed got FOUR refusal sentences where the record\n * should have been. `anon` has no grant on the four org-scoped tables and\n * widening it is forbidden (MRI-A10) — a meeting link is not organization\n * membership — so the honest refusals were the only thing this file could say.\n *\n * Arman ruled on 2026-09-12 that guest access to the record is a CONFIGURATION\n * and never a question: a system default, an organization override, and a\n * per-meeting setting a host chooses. The server resolves it and serves the\n * shared subset at `GET /api/v1/meet/record`, authorized by the room token this\n * device already holds. So this half does three things and no more:\n *\n * 1. reads that route and turns its answer into the SAME `MeetingRecordBundle`\n * the member lane produces — so one screen renders both readers and they can\n * never drift into two products;\n * 2. states every part the level does NOT share as a `MeetingRecordRestriction`\n * with the server's own sentence, which is what keeps \"not shared\" and\n * \"nothing happened\" different things on screen;\n * 3. gives a host the control that sets it.\n *\n * What it does NOT do is decide anything. The level, the refusals and the\n * remedies are the server's words; a client that computed its own would be a\n * second authority on who may read a transcript.\n * ═══════════════════════════════════════════════════════════════════════════ */\n\n\n/** How much of a finished meeting a GUEST may read. Pinned with the server. */\nexport type GuestRecordAccess = \"none\" | \"summary\" | \"full\";\n\n/** Widest last. The ORDER is the comparison, exactly as it is on the server. */\nexport const GUEST_RECORD_ACCESS_LEVELS: readonly GuestRecordAccess[] = [\n \"none\",\n \"summary\",\n \"full\",\n];\n\n/**\n * What a HOST may choose for one meeting. `default` is not a fourth level — it\n * is the absence of a per-meeting choice, and it is the only way to un-choose.\n */\nexport type GuestRecordAccessSetting = \"default\" | GuestRecordAccess;\n\nexport const GUEST_RECORD_ACCESS_CHOICES: readonly GuestRecordAccessSetting[] = [\n \"default\",\n \"summary\",\n \"full\",\n \"none\",\n];\n\n/** `meet_meetings.metadata.guest_record_access`. No DDL; a setting on a row. */\nexport const GUEST_RECORD_ACCESS_METADATA_KEY = \"guest_record_access\";\n\n/** What a host chose for THIS meeting, read from the row they already have. */\nexport function guestRecordAccessSetting(\n meeting: MeetingRecord | null,\n): GuestRecordAccessSetting {\n const raw = (meeting?.metadata as Record<string, unknown> | undefined)?.[\n GUEST_RECORD_ACCESS_METADATA_KEY\n ];\n return GUEST_RECORD_ACCESS_LEVELS.includes(raw as GuestRecordAccess)\n ? (raw as GuestRecordAccess)\n : \"default\";\n}\n\n/** The sentence on the host's control. Plain English, never a token. */\nexport function guestRecordAccessLabel(setting: GuestRecordAccessSetting): string {\n switch (setting) {\n case \"default\":\n return \"Use my organization's setting\";\n case \"summary\":\n return \"Summary, decisions and action items\";\n case \"full\":\n return \"Everything, including the transcript\";\n case \"none\":\n return \"Nothing — keep the record internal\";\n }\n}\n\n/** What each choice actually means for the people who were in the room. */\nexport function guestRecordAccessExplanation(setting: GuestRecordAccessSetting): string {\n switch (setting) {\n case \"default\":\n return \"Guests who attended read whatever your organization shares by default. An administrator sets that once, for every meeting nobody thinks about.\";\n case \"summary\":\n return \"Guests who attended read the wrap-up summary, the decisions, the action items, the notes taken as it happened, and who was here. They do not get the word-for-word transcript or the recording.\";\n case \"full\":\n return \"Guests who attended read everything this meeting produced, including the word-for-word transcript of what each person said.\";\n case \"none\":\n return \"Guests who attended are told plainly that this meeting's record is kept inside your organization. They see nothing of it.\";\n }\n}\n\n/** What came back from the server, so a surface never has to infer a level. */\nexport interface GuestRecordAccessResolution {\n readonly level: GuestRecordAccess;\n /** `meeting` when a host chose for this one; `organization` otherwise. */\n readonly source: \"meeting\" | \"organization\";\n}\n\n/** What the parts of the record the level does not share are called, and why. */\nconst NOT_SHARED: Readonly<Record<MeetingRecordRestriction[\"part\"], string>> = {\n transcript:\n \"The word-for-word transcript of this meeting is not shared with guests.\",\n notes: \"The notes, decisions and action items are not shared with guests.\",\n attendance: \"Who attended is not shared with guests.\",\n recording: \"The recording of this meeting is not shared with guests.\",\n};\n\nconst NOT_SHARED_REMEDY =\n \"The organization that held this meeting decided how much of its record guests can read. \" +\n \"If you need more, ask whoever invited you — they can send it to you, or change the \" +\n \"setting for this meeting.\";\n\nconst RESTRICTION_PART_NAME: Readonly<Record<MeetingRecordRestriction[\"part\"], string>> = {\n attendance: \"attendance\",\n notes: \"notes and decisions\",\n transcript: \"word-for-word transcript\",\n recording: \"recording\",\n};\n\nfunction joinPartNames(parts: readonly MeetingRecordRestriction[\"part\"][]): string {\n const names = parts.map((part) => RESTRICTION_PART_NAME[part]);\n if (names.length < 2) return names[0] ?? \"this part of the record\";\n if (names.length === 2) return `${names[0]} and ${names[1]}`;\n return `${names.slice(0, -1).join(\", \")}, and ${names[names.length - 1]}`;\n}\n\n/**\n * Repeated policy refusals are one access state, not four alarming cards.\n *\n * The individual rows remain durable data for absence handling in their own\n * sections. This projection groups only rows with the exact same remedy, so a\n * distinct transport or integrity failure remains visible on its own.\n */\nexport function summarizeMeetingRecordRestrictions(\n restrictions: readonly MeetingRecordRestriction[],\n): readonly MeetingRecordRestrictionNotice[] {\n const grouped = new Map<string, MeetingRecordRestriction[]>();\n for (const restriction of restrictions) {\n const rows = grouped.get(restriction.remedy) ?? [];\n rows.push(restriction);\n grouped.set(restriction.remedy, rows);\n }\n return [...grouped.values()].map((rows) => {\n if (rows.length === 1) {\n const row = rows[0]!;\n return { parts: [row.part], message: row.message, remedy: row.remedy };\n }\n return {\n parts: rows.map((row) => row.part),\n message: `Some parts of this meeting record are not available to this reader: ${joinPartNames(rows.map((row) => row.part))}.`,\n remedy: rows[0]!.remedy,\n };\n });\n}\n\n/**\n * Read the record the server shares with THIS guest.\n *\n * `roomToken` is the pass this device kept from the meeting\n * (`TokenClient.remembered`). It is presented in a header, never appended to\n * the URL. A guest with no pass, an expired pass, or a level of `none` gets the\n * server's refusal — as restrictions on a real bundle, so the screen says what\n * it cannot show instead of collapsing into an error page.\n */\nexport async function loadGuestMeetingRecord(args: {\n api: MeetApi;\n repository: MeetRepository;\n meetingId: MeetingId;\n meeting: MeetingRecord;\n roomToken: RoomToken | null;\n /**\n * A SIGNED-IN reader the database refused (from another organization, or a\n * guest who later created an account). Their session stands in for the\n * pass: the server reads their `user:<id>` admission or the attendance they\n * claimed, and shares what guests may read (2026-10-01).\n */\n session?: boolean;\n /**\n * THE SESSION-AWARE RECORD (CORE-DESIGN §2.3): one run of a series. Omitted = the server's\n * default (a one-off meeting's whole record; a series' latest ended run).\n */\n sessionId?: string | null;\n}): Promise<MeetingRecordBundle> {\n const { api, repository, meetingId, meeting, roomToken } = args;\n const session = args.session === true;\n\n if (roomToken?.meetingPass == null && !session) {\n // NOT A GUESS AND NOT A FETCH. This browser has no pass for this meeting,\n // which the server would answer with the same sentence — asking it first\n // would only add a round trip and a 403 in the console.\n return refusedBundle(\n meeting,\n \"This meeting's record is only readable by the people who were in it.\",\n \"Open the meeting from the link you were invited with, in the same browser you used \" +\n \"to attend — that is what identifies you to this meeting. Signing in with an \" +\n \"account in the organization that held the meeting also works.\",\n );\n }\n\n let response: Response;\n try {\n response = await api.request({\n path:\n `${MEET_ROUTES.record}?meeting_id=${encodeURIComponent(meetingId)}` +\n (args.sessionId ? `&session_id=${encodeURIComponent(args.sessionId)}` : \"\"),\n method: \"GET\",\n // A GUEST HAS NO SESSION AND IS STILL A FIRST-CLASS READER (D6).\n requireAuth: session,\n operation: \"meet.record\",\n headers:\n roomToken?.meetingPass != null ? { [MEETING_PASS_HEADER]: roomToken.meetingPass } : {},\n });\n } catch (error) {\n // A transport wobble is not a permission decision and must not read as one.\n return refusedBundle(\n meeting,\n `This meeting's record could not be read (${(error as Error).message}).`,\n (error as { remedy?: string }).remedy ??\n \"This is a connection failure, not a decision about you. Reload the page.\",\n );\n }\n\n if (!response.ok) {\n const refusal = await readRefusal(response);\n return refusedBundle(meeting, refusal.message, refusal.remedy);\n }\n\n const body = (await response.json()) as Record<string, unknown>;\n const payload = (\n typeof body[\"data\"] === \"object\" && body[\"data\"] !== null ? body[\"data\"] : body\n ) as GuestRecordPayload;\n\n const rows = (key: keyof GuestRecordPayload): Record<string, unknown>[] =>\n Array.isArray(payload[key]) ? (payload[key] as Record<string, unknown>[]) : [];\n\n const attendees = rows(\"attendees\").map((row) => repository.projectAttendee(row));\n const notes = rows(\"notes\").map((row) => repository.projectNote(row));\n const transcript = rows(\"transcript\").map((row) => repository.projectSegment(row));\n const recordings = rows(\"recordings\").map((row) => repository.projectRecording(row));\n\n // 🚨 THE RESTRICTIONS COME FROM `shared`, NOT FROM AN EMPTY ARRAY. A level of\n // `summary` and a meeting where nobody spoke both produce zero transcript\n // rows, and telling the reader they are the same thing is the MRI-A16 defect.\n const shared = (payload.shared ?? {}) as Record<string, boolean>;\n const restrictions: MeetingRecordRestriction[] = [];\n for (const part of [\"attendance\", \"notes\", \"transcript\", \"recording\"] as const) {\n if (shared[part] === false) {\n restrictions.push({ part, message: NOT_SHARED[part], remedy: NOT_SHARED_REMEDY });\n }\n }\n\n return {\n meeting,\n attendees,\n summary: activeWrapUpSummary(notes),\n decisions: notes.filter((note) => note.kind === \"decision\" && note.supersededBy === null),\n actionItems: notes.filter(\n (note) => note.kind === \"action-item\" && note.supersededBy === null,\n ),\n liveNotes: notes\n .filter((note) => note.kind === \"note\" && note.role === null)\n .slice()\n .sort((left, right) => {\n const a = left.window ?? Number.MAX_SAFE_INTEGER;\n const b = right.window ?? Number.MAX_SAFE_INTEGER;\n return a === b ? left.createdAt.localeCompare(right.createdAt) : a - b;\n }),\n transcript,\n recording: recordings[0] ?? null,\n restrictions,\n names: learnNames(\n learnNames({}, attendees),\n transcript.map((line) => ({ identity: line.identity, displayName: line.speaker })),\n ),\n };\n}\n\n/** The row shapes the route returns — the same snake_case the tables use. */\ninterface GuestRecordPayload {\n readonly access?: GuestRecordAccess;\n readonly access_source?: \"meeting\" | \"organization\";\n readonly shared?: Record<string, boolean>;\n readonly attendees?: unknown;\n readonly notes?: unknown;\n readonly transcript?: unknown;\n readonly recordings?: unknown;\n readonly recording_notice?: string;\n}\n\n/**\n * THE SERVER'S OWN WORDS, verbatim. Every meet refusal carries\n * `{message, remedy}`, and a house sentence written over it would hide the one\n * thing worth reading — including the difference between \"your pass expired,\n * open the link again\" and \"this record is not shared with guests\".\n */\nasync function readRefusal(\n response: Response,\n): Promise<{ message: string; remedy: string }> {\n try {\n const body = (await response.json()) as Record<string, unknown>;\n const detail = (\n typeof body[\"detail\"] === \"object\" && body[\"detail\"] !== null ? body[\"detail\"] : body\n ) as Record<string, unknown>;\n const message = detail[\"message\"];\n const remedy = detail[\"remedy\"];\n if (typeof message === \"string\" && message.length > 0) {\n return {\n message,\n remedy:\n typeof remedy === \"string\" && remedy.length > 0\n ? remedy\n : \"Ask whoever hosted the meeting for what you need.\",\n };\n }\n } catch {\n /* falls through to the status sentence below */\n }\n return {\n message: `This meeting's record could not be read (the server answered ${response.status}).`,\n remedy:\n \"This is not a decision about you — it is a failure to reach the record. Reload the \" +\n \"page; if it repeats, tell whoever hosted the meeting.\",\n };\n}\n\n/** A real bundle whose every part is refused, with ONE reason said once. */\nfunction refusedBundle(\n meeting: MeetingRecord,\n message: string,\n remedy: string,\n): MeetingRecordBundle {\n return {\n meeting,\n attendees: [],\n summary: null,\n decisions: [],\n actionItems: [],\n liveNotes: [],\n transcript: [],\n recording: null,\n // Every empty collection below is a refusal, never evidence of absence.\n // The record renderer asks its own part before it writes an empty-state\n // sentence, so omitting attendance here made a denied roster read as\n // “Nobody attended.” `summarizeMeetingRecordRestrictions` still presents\n // this shared server reason once at the top of the record.\n restrictions: ([\"attendance\", \"notes\", \"transcript\", \"recording\"] as const).map(\n (part) => ({ part, message, remedy }),\n ),\n names: {},\n };\n}\n\n/**\n * A HOST chooses what guests may read of THIS meeting.\n *\n * Host power is the SERVER's decision and it re-checks it; this function exists\n * so the control has one call to make, and it returns what the choice RESOLVES\n * to so the screen never has to guess what \"my organization's setting\" means\n * today.\n */\nexport async function setGuestRecordAccess(args: {\n api: MeetApi;\n meetingId: MeetingId;\n organizationId: OrganizationId;\n access: GuestRecordAccessSetting;\n}): Promise<{\n readonly setting: GuestRecordAccessSetting;\n readonly effective: GuestRecordAccessResolution;\n}> {\n const response = await args.api.request({\n path: MEET_ROUTES.recordAccess,\n method: \"POST\",\n requireAuth: true,\n operation: \"meet.record.access\",\n body: {\n meeting_id: args.meetingId,\n organization_id: args.organizationId,\n access: args.access,\n },\n });\n if (!response.ok) {\n const refusal = await readRefusal(response);\n throw new MeetError(\"forbidden\", refusal.message, refusal.remedy);\n }\n const body = (await response.json()) as Record<string, unknown>;\n const payload = (\n typeof body[\"data\"] === \"object\" && body[\"data\"] !== null ? body[\"data\"] : body\n ) as {\n setting?: GuestRecordAccessSetting;\n effective?: GuestRecordAccessResolution;\n };\n return {\n setting: payload.setting ?? args.access,\n effective: payload.effective ?? { level: \"summary\", source: \"organization\" },\n };\n}\n\n/** What a guest is told when the level shares nothing. Rendered, never hidden. */\nexport const GUEST_RECORD_NOT_SHARED = NOT_SHARED;\n","/**\n * ONE MEETING RECORD PER READER PER TAB — read once, kept current by one\n * ref-counted channel (0.7.79).\n *\n * 🚨 THE DEFECT THIS CLOSES. `useMeetingRecord` held the bundle in its own\n * component state and read all four parts (`meet_participants`, `meet_notes`,\n * `meet_transcript_segments`, `meet_recordings`) in a mount effect. A host that\n * hides a view without unmounting its data — a board tile asleep under React\n * `<Activity>`, which tears every effect down and runs it again on wake — read\n * the whole record again on every wake, on a Remove + Undo, and once more for\n * every second view of the same meeting (the record tab, the meeting's agent\n * surface, five \"meeting notes\" tiles). The view owned the data.\n *\n * THE SHAPE. One entry per (reader, meeting). The first view reads; every later\n * view — the same view after a wake or a remount included — renders the entry\n * and reads nothing. A read already in flight is joined. A forced read (Retry,\n * a reconnect) supersedes it, and an answer that lands after a newer read was\n * asked is DROPPED (the late-answer guard), so an old answer can never paint\n * over a newer one.\n *\n * LIVE WHILE HELD. A member's entry (a direct read, kept as its four parts) is\n * patched row by row from the database's change feed for the four tables, then\n * assembled again — one row, never four reads. The channel opens on the first\n * holder and closes `MEETING_RECORD_GRACE_MS` after the last one lets go, so a\n * wake or an Undo inside the grace costs nothing. Past the grace nothing\n * watched the meeting, so the entry is marked stale and the next view reads it\n * once. A guest's record arrives through the server as a finished bundle; it\n * has no change feed and is kept as it is.\n *\n * Framework-free on purpose: `react/hooks.ts` is one reader of it.\n */\n\nimport { assembleMeetingRecord, type MeetingRecordBundle, type MeetingRecordParts } from \"./record\";\nimport type { MeetingRecord } from \"./types\";\n\n/** How long a meeting's channel and kept record outlive the last view of it. */\nexport const MEETING_RECORD_GRACE_MS = 60_000;\n\nexport interface MeetingRecordFailure {\n readonly message: string;\n readonly remedy: string;\n}\n\nexport interface MeetingRecordState {\n readonly loading: boolean;\n readonly record: MeetingRecordBundle | null;\n /** A failure that stopped the WHOLE record loading (never kept as an answer). */\n readonly failure: MeetingRecordFailure | null;\n}\n\n/** What a read produced: the four parts (patchable), or a finished bundle (a guest's). */\nexport type MeetingRecordAnswer =\n | { readonly kind: \"parts\"; readonly meeting: MeetingRecord; readonly parts: MeetingRecordParts }\n | { readonly kind: \"bundle\"; readonly bundle: MeetingRecordBundle };\n\n/** One changed row, already projected by the repository. `null` + `removedId` = gone. */\nexport type MeetingRecordChange =\n | { readonly part: \"transcript\"; readonly row: MeetingRecordParts[\"transcript\"][number] | null; readonly removedId?: string | undefined }\n | { readonly part: \"notes\"; readonly row: MeetingRecordParts[\"notes\"][number] | null; readonly removedId?: string | undefined }\n | { readonly part: \"attendance\"; readonly row: MeetingRecordParts[\"attendees\"][number] | null; readonly removedId?: string | undefined }\n | { readonly part: \"recording\"; readonly row: MeetingRecordParts[\"recordings\"][number] | null; readonly removedId?: string | undefined };\n\nexport interface MeetingRecordCache {\n /** The kept state for `key` (stable until it changes). */\n state(key: string | null): MeetingRecordState;\n subscribe(listener: () => void): () => void;\n /**\n * Read unless the entry is kept and current (or a read is in flight — joined).\n * `force` reads anyway and supersedes a read in flight. Never rejects: a\n * failure is in the state.\n */\n ensure(key: string, read: () => Promise<MeetingRecordAnswer>, options?: { force?: boolean }): Promise<void>;\n /** The meeting row moved (a rename, it ended): assemble again — no read. */\n setMeeting(key: string, meeting: MeetingRecord): void;\n /** Apply one changed row from the change feed — no read. */\n apply(key: string, change: MeetingRecordChange): void;\n /**\n * Hold the live half: `open` on the first holder, its close a grace after the\n * last. `owner` is what the channel runs on (the realtime manager): a new\n * owner means the old one's channels are gone, so it opens again.\n */\n hold(key: string, open: () => () => void, owner?: unknown): () => void;\n /** Tests only. */\n reset(): void;\n}\n\ninterface Entry {\n state: MeetingRecordState;\n meeting: MeetingRecord | null;\n parts: MeetingRecordParts | null;\n fresh: boolean;\n seq: number;\n inflight: Promise<void> | null;\n holders: number;\n close: (() => void) | null;\n owner: unknown;\n grace: ReturnType<typeof setTimeout> | null;\n}\n\nconst INITIAL: MeetingRecordState = { loading: true, record: null, failure: null };\n\nfunction upsert<T extends { readonly id: string }>(\n rows: readonly T[],\n row: T | null,\n removedId: string | undefined,\n order: (a: T, b: T) => number,\n): readonly T[] {\n const id = row?.id ?? removedId;\n if (id === undefined) return rows;\n const rest = rows.filter((existing) => existing.id !== id);\n return row === null ? rest : [...rest, row].sort(order);\n}\n\nconst byId = (a: { id: string }, b: { id: string }) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);\nconst ascending = (a: string | null, b: string | null) =>\n a === b ? 0 : a === null ? 1 : b === null ? -1 : a < b ? -1 : 1;\n\nexport function createMeetingRecordCache(\n options: { graceMs?: number } = {},\n): MeetingRecordCache {\n const graceMs = options.graceMs ?? MEETING_RECORD_GRACE_MS;\n const entries = new Map<string, Entry>();\n const listeners = new Set<() => void>();\n const notify = () => {\n for (const listener of listeners) listener();\n };\n const entryOf = (key: string): Entry => {\n let entry = entries.get(key);\n if (!entry) {\n entry = {\n state: INITIAL,\n meeting: null,\n parts: null,\n fresh: false,\n seq: 0,\n inflight: null,\n holders: 0,\n close: null,\n owner: null,\n grace: null,\n };\n entries.set(key, entry);\n }\n return entry;\n };\n const assemble = (entry: Entry) => {\n if (entry.meeting === null || entry.parts === null) return;\n entry.state = { loading: false, failure: null, record: assembleMeetingRecord(entry.meeting, entry.parts) };\n notify();\n };\n\n return {\n state: (key) => (key === null ? INITIAL : (entries.get(key)?.state ?? INITIAL)),\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n ensure(key, read, { force = false } = {}) {\n const entry = entryOf(key);\n if (!force) {\n if (entry.inflight !== null) return entry.inflight;\n if (entry.fresh) return Promise.resolve();\n }\n const seq = ++entry.seq;\n if (entry.state.failure !== null) {\n // Retrying after a failure says it is loading again; a kept record stays on screen.\n entry.state = { ...entry.state, loading: true, failure: null };\n notify();\n }\n const request = read().then(\n (answer) => {\n if (seq !== entry.seq) return; // a newer read was asked: this answer is old\n entry.fresh = true;\n if (answer.kind === \"parts\") {\n entry.meeting = answer.meeting;\n entry.parts = answer.parts;\n assemble(entry);\n } else {\n entry.meeting = answer.bundle.meeting;\n entry.parts = null;\n entry.state = { loading: false, failure: null, record: answer.bundle };\n notify();\n }\n },\n (thrown: unknown) => {\n if (seq !== entry.seq) return;\n // A failure is never kept as the answer: the next view (or Retry) asks again.\n entry.fresh = false;\n entry.state = {\n loading: false,\n record: entry.state.record,\n failure: {\n message: (thrown as Error)?.message ?? String(thrown),\n remedy:\n (thrown as { remedy?: string })?.remedy ??\n \"The meeting record could not be read. Reload the page; nothing was changed.\",\n },\n };\n notify();\n },\n );\n const tracked = request.finally(() => {\n if (entry.inflight === tracked) entry.inflight = null;\n });\n entry.inflight = tracked;\n return tracked;\n },\n setMeeting(key, meeting) {\n const entry = entries.get(key);\n if (!entry || entry.meeting === meeting) return;\n if (entry.parts !== null) {\n entry.meeting = meeting;\n assemble(entry);\n } else if (entry.state.record !== null) {\n entry.meeting = meeting;\n entry.state = { ...entry.state, record: { ...entry.state.record, meeting } };\n notify();\n }\n },\n apply(key, change) {\n const entry = entries.get(key);\n const parts = entry?.parts;\n if (!entry || !parts) return;\n // A part this reader was refused stays refused: a row it cannot read is not news to it.\n const refused = parts.restrictions.some((r) => r.part === change.part);\n if (refused) return;\n switch (change.part) {\n case \"transcript\":\n entry.parts = {\n ...parts,\n transcript: upsert(parts.transcript, change.row, change.removedId, (a, b) =>\n ascending(a.startedAt, b.startedAt) || byId(a, b),\n ),\n };\n break;\n case \"notes\":\n entry.parts = {\n ...parts,\n notes: upsert(parts.notes, change.row, change.removedId, (a, b) =>\n ascending(a.createdAt, b.createdAt) || byId(a, b),\n ),\n };\n break;\n case \"attendance\":\n entry.parts = {\n ...parts,\n attendees: upsert(parts.attendees, change.row, change.removedId, (a, b) =>\n ascending(a.joinedAt, b.joinedAt) || byId(a, b),\n ),\n };\n break;\n case \"recording\":\n entry.parts = {\n ...parts,\n // Newest first, as the read orders them.\n recordings: upsert(parts.recordings, change.row, change.removedId, (a, b) =>\n ascending(b.startedAt, a.startedAt) || byId(a, b),\n ),\n };\n break;\n }\n assemble(entry);\n },\n hold(key, open, owner = null) {\n const entry = entryOf(key);\n if (entry.grace !== null) {\n clearTimeout(entry.grace);\n entry.grace = null;\n }\n if (entry.close !== null && entry.owner !== owner) {\n entry.close();\n entry.close = null;\n }\n if (entry.close === null) {\n entry.close = open();\n entry.owner = owner;\n }\n entry.holders += 1;\n let released = false;\n return () => {\n if (released) return;\n released = true;\n entry.holders -= 1;\n if (entry.holders > 0) return;\n entry.grace = setTimeout(() => {\n entry.grace = null;\n if (entry.holders > 0) return;\n entry.close?.();\n entry.close = null;\n // Nothing watched the meeting from here on: the next view reads it once.\n entry.fresh = false;\n }, graceMs);\n };\n },\n reset() {\n for (const entry of entries.values()) {\n if (entry.grace !== null) clearTimeout(entry.grace);\n entry.close?.();\n }\n entries.clear();\n notify();\n },\n };\n}\n","/**\n * The reaction ids every skin offers (CORE-DESIGN §5.3). The id IS the emoji\n * the wire carries; a skin may draw its own art for each, never a different set.\n */\nexport const MEET_REACTIONS: readonly string[] = [\"💖\", \"👍\", \"🎉\", \"👏\", \"😂\", \"😮\", \"😢\", \"🤔\", \"👎\"];\n","/**\n * COLLAB AND CAPTION LOGIC, HEADLESS (CORE-DESIGN §5.3, step S1).\n *\n * What used to live inside the React hooks of the in-room activities: which\n * activities a person has, when the Activities door deserves a dot, the act /\n * move-to-breakout calls with their sentences, the caption-translation work\n * queue, and the one-shot panel and language requests on the store. Pure or\n * store-shaped, so every skin reads the same answers; the `/react` hooks are\n * thin selectors over these.\n */\n\nimport type { CollabPanel, CollabSnapshot } from \"./collab\";\nimport type { CollabRequest, CollabResult } from \"./in-room\";\nimport type { MeetSnapshot } from \"./store\";\n\n/** The activities this person has, in the order Meet lists them. */\nexport function availableActivities(collab: CollabSnapshot | null, isHost: boolean): CollabPanel[] {\n const settings = collab?.settings ?? null;\n if (settings === null) return [];\n const out: CollabPanel[] = [];\n if (settings.whiteboardEnabled) out.push(\"whiteboard\");\n // A participant sees Polls once a poll exists for them; a host always (to create one).\n if (settings.pollsEnabled && (isHost || (collab?.polls.length ?? 0) > 0)) out.push(\"polls\");\n if (settings.qaEnabled) out.push(\"qa\");\n // Breakouts: hosts run them; a participant needs the panel only when rooms are open.\n if (settings.breakoutsEnabled && (isHost || collab?.breakout?.state === \"open\")) out.push(\"breakouts\");\n return out;\n}\n\n/** Something worth a dot on the Activities button: a live poll unanswered, a new question for a host. */\nexport function activityAttention(collab: CollabSnapshot | null, isHost: boolean): boolean {\n if (collab === null) return false;\n if (collab.polls.some((poll) => poll.state === \"live\" && poll.myVote.length === 0 && !isHost)) return true;\n if (isHost && collab.questions.some((q) => q.state === \"open\")) return true;\n return false;\n}\n\nexport interface CollabRuntime {\n readonly inRoom: { collab(request: CollabRequest): Promise<CollabResult>; loadCollab?(): Promise<void> };\n readonly engine: { moveToBreakout(roomId: string | null): Promise<unknown> };\n}\n\n/** Perform one act; resolves with the server's sentence, or its refusal (never throws). */\nexport async function performCollab(\n runtime: CollabRuntime | null,\n request: CollabRequest,\n): Promise<{ ok: boolean; message: string; itemId?: string | null }> {\n if (runtime === null) return { ok: false, message: \"This meeting is not connected.\" };\n try {\n const result = await runtime.inRoom.collab(request);\n return { ok: true, message: result.message, itemId: result.itemId };\n } catch (error) {\n return { ok: false, message: (error as { message?: string }).message ?? \"That did not work.\" };\n }\n}\n\n/** Go to a breakout room (`null` = the main room), with the sentence to show. */\nexport async function moveToRoom(\n runtime: CollabRuntime | null,\n roomId: string | null,\n): Promise<{ ok: boolean; message: string }> {\n if (runtime === null) return { ok: false, message: \"This meeting is not connected.\" };\n try {\n await runtime.engine.moveToBreakout(roomId);\n // A room's packets only reach people already in it, so a person arriving mid-countdown (or\n // returning to the main room) re-reads the plan: they must see the closing countdown too.\n await runtime.inRoom.loadCollab?.().catch(() => undefined);\n return { ok: true, message: roomId === null ? \"Back in the main room.\" : \"Joined the breakout room.\" };\n } catch (error) {\n return { ok: false, message: (error as { message?: string }).message ?? \"That room could not be joined.\" };\n }\n}\n\nexport interface CollabStoreLike {\n snapshot(): MeetSnapshot;\n patch(next: Partial<MeetSnapshot>): void;\n}\n\n/** Finished caption lines that still need a translation into `language`. */\nexport function untranslatedLines(\n lines: readonly { id: string; text: string; final: boolean }[],\n translations: Readonly<Record<string, string>>,\n): { id: string; text: string }[] {\n return lines\n .filter((line) => line.final && translations[line.id] === undefined)\n .map(({ id, text }) => ({ id, text }));\n}\n\n/** The next batch to send: never a line already asked for in this language, at most 20. */\nexport function nextTranslationBatch(\n pending: readonly { id: string; text: string }[],\n language: string,\n asked: Set<string>,\n): { id: string; text: string }[] {\n const batch = pending.filter((line) => !asked.has(`${language}:${line.id}`)).slice(-20);\n for (const line of batch) asked.add(`${language}:${line.id}`);\n return batch;\n}\n\n/** The viewer chose a caption language (or `null` = as spoken): start over in it. */\nexport function setCaptionLanguage(store: CollabStoreLike, next: string | null): void {\n store.patch({\n collab: {\n ...store.snapshot().collab,\n captionLanguage: next,\n captionTranslations: {},\n captionTranslationError: null,\n },\n });\n}\n\n/** Take (and clear) the one-shot \"open this panel\" request, if any. */\nexport function takePanelRequest(store: CollabStoreLike): CollabPanel | null {\n const request = store.snapshot().collab.panelRequest;\n if (request === null) return null;\n store.patch({ collab: { ...store.snapshot().collab, panelRequest: null } });\n return request.panel;\n}\n\n/** Take (and clear) the one-shot \"move to this breakout room\" request, if any. */\nexport function takeMoveRequest(store: CollabStoreLike): { roomId: string | null } | null {\n const request = store.snapshot().collab.moveRequest;\n if (request === null) return null;\n store.patch({ collab: { ...store.snapshot().collab, moveRequest: null } });\n return { roomId: request.roomId };\n}\n","/**\n * THE OBSERVATION CONTRACT, DERIVED FROM TODAY'S STORE (CORE-DESIGN §7 step S0).\n *\n * The real-browser scenario harness reads a meeting through a small set of\n * `data-meet-*` attributes (common-docs `meet/duplicates/HARNESS-CONTRACT.md`,\n * owned by the harness lane). This module turns the store's `MeetSnapshot`\n * into exactly those values — pure, framework-free, rendered ONCE by the\n * package (`useCoreRootProps()` / `<MeetRoot>`), never per skin.\n *\n * S0 rule: TODAY'S VALUES ONLY. Every value here is what the person actually\n * sees on today's screens, including today's defects — a `failed` or `left`\n * room renders the pre-join screen, so its phase is `prejoin`, and the harness\n * records that as the defect it is. A value the store does not know (host\n * presence before joining, autoplay blocking) is OMITTED rather than guessed:\n * an absent attribute reads as \"unknown\", a guessed one would lie.\n */\n\nimport type { DevicePreview, LocalTrackState, PermissionReading } from \"./attendance\";\nimport { deviceNoticeCode, deviceUnavailableReason } from \"./media-supervisor\";\nimport type { ConnectionRegion, MeetSnapshot } from \"./store\";\nimport { isLivePhase, isWaitingPhase, reasonOf, type MeetPhase } from \"./phase\";\nimport { effectiveRole, effectiveStanding } from \"./store\";\nimport { consentPending, isLockedFor, mayAdmitNow } from \"./live-policy\";\nimport { isLinkFollower } from \"./types\";\nimport type {\n ConnectionQuality,\n MeetParticipant,\n MeetingRecord,\n ParticipantRole,\n ParticipantStanding,\n} from \"./types\";\n\n/**\n * `data-meet-role` is the HARNESS CONTRACT's vocabulary (host · cohost · participant · guest):\n * a link follower whose role is `participant` renders `guest` (CORE-DESIGN §2.1); `presenter` /\n * `viewer` render `participant` until the contract adopts them (§3.7 C3).\n */\nexport type ObservedRole = \"host\" | \"cohost\" | \"presenter\" | \"participant\" | \"guest\";\n\nexport function observedRole(role: ParticipantRole, standing: ParticipantStanding | null | undefined): ObservedRole {\n if (role === \"host\" || role === \"cohost\" || role === \"presenter\") return role;\n return isLinkFollower(standing) ? \"guest\" : \"participant\";\n}\n\n/** `data-meet-phase` — the core's phase IS the contract value (S6a: no translation layer). */\nexport type ObservedPhase = MeetPhase;\n\nexport type ObservedConnection = ConnectionRegion;\n\nexport interface MeetObservation {\n readonly phase: ObservedPhase;\n readonly reason: string | null;\n readonly role: ObservedRole | \"none\";\n readonly connection: ObservedConnection;\n /** null = this tab does not know (it is not in the room). */\n readonly hostPresent: boolean | null;\n readonly lobbyCount: number;\n readonly recording: boolean;\n /** null = today's store does not track autoplay blocking. */\n readonly audioBlocked: boolean | null;\n /** null outside `joining` / `in_call` — no device is in play there today. */\n readonly camera: string | null;\n readonly microphone: string | null;\n}\n\nexport interface ObserveInput {\n readonly snapshot: MeetSnapshot | null;\n /** The row the consumer resolved, when the store has none yet (the guest lane). */\n readonly meeting?: MeetingRecord | null | undefined;\n}\n\n/**\n * The store's device state → the contract's `camera` / `microphone` value (CORE-DESIGN §3.2).\n * The reason comes from the media supervisor (S6c), which reads the browser's permission store: a\n * kind the browser denies is `unavailable:denied` even when nothing tried to open it.\n */\nexport function observedDevice(\n track: LocalTrackState,\n permission: PermissionReading = \"unknown\",\n held: \"locked\" | \"muted_by_host\" | null = null,\n): string {\n if (held === \"locked\") return \"locked\";\n if (track.enabled) return \"on\";\n if (held === \"muted_by_host\") return \"muted_by_host\";\n const reason = deviceUnavailableReason(track, permission);\n return reason === null ? \"muted_by_me\" : `unavailable:${reason}`;\n}\n\n/**\n * The pre-join check → the contract's `camera` / `microphone` value (P1 `perm-*`, `device-none-found`,\n * `device-busy`). `prompting` = the browser is asking; null = nothing was tried and nothing is known.\n */\nexport function observedPreview(preview: DevicePreview, permission: PermissionReading = \"unknown\"): string | null {\n if (permission === \"denied\") return \"unavailable:denied\";\n switch (preview.state) {\n case \"on\":\n return \"on\";\n case \"off\":\n return \"muted_by_me\";\n case \"prompting\":\n return \"prompting\";\n case \"unavailable\":\n return `unavailable:${preview.unavailable?.reason ?? \"failed\"}`;\n default:\n return null;\n }\n}\n\n/** Does this snapshot's meeting row say it is over? */\nfunction endedRecord(snapshot: MeetSnapshot | null, given: MeetingRecord | null): boolean {\n const record = snapshot?.meeting ?? given;\n return record !== null && record.endedAt !== null;\n}\n\n/**\n * The phase the person is looking at: the store's phase, verbatim (CORE-DESIGN §3.1). The one\n * derivation left is the ended meeting: a row that says it is over renders `ended` for anyone not\n * in a live connection, whichever carrier delivered the ending first.\n */\nexport function observedPhase(input: ObserveInput): ObservedPhase {\n const { snapshot } = input;\n if (snapshot === null) return \"resolving\";\n const phase = snapshot.phase;\n if (endedRecord(snapshot, input.meeting ?? null) && !isLivePhase(phase) && !isCallPhase(phase)) return \"ended\";\n return phase;\n}\n\nfunction isCallPhase(phase: MeetPhase): boolean {\n return phase.startsWith(\"ringing_\") || phase.startsWith(\"call_\") || phase === \"answered_elsewhere\";\n}\n\nconst ADMITTER_ROLES: ReadonlySet<ParticipantRole> = new Set([\"host\", \"cohost\"]);\n\nexport function observe(input: ObserveInput): MeetObservation {\n const { snapshot } = input;\n const phase = observedPhase(input);\n const inCall = phase === \"in_call\";\n const actual: ParticipantRole | \"none\" = inCall && snapshot !== null ? effectiveRole(snapshot) : \"none\";\n const canAdmit = snapshot !== null && mayAdmitNow(actual, snapshot.participants, snapshot.livePolicy);\n const role: ObservedRole | \"none\" =\n actual === \"none\" || snapshot === null ? \"none\" : observedRole(actual, effectiveStanding(snapshot));\n const deviceInPlay = phase === \"joining\" || inCall;\n const region = snapshot?.mediaRegion;\n const atPrejoin = phase === \"prejoin\" && region !== undefined;\n return {\n phase,\n reason: phase === \"ended\" ? (snapshot?.reason ?? null) : (snapshot?.reason ?? reasonOf(phase)),\n role,\n // `net-blip`: in the call, a drop shorter than `reconnect_banner_delay_ms` is not something the\n // person sees — the contract says what is on screen, so it reads `stable` until the banner shows.\n connection: snapshot === null\n ? \"stable\"\n : inCall && snapshot.connection !== \"stable\" && !snapshot.connectionBanner\n ? \"stable\"\n : snapshot.connection,\n hostPresent: inCall && snapshot !== null\n ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role))\n : snapshot !== null && isWaitingPhase(snapshot.phase)\n ? (snapshot.waiting?.admittersPresent ?? null)\n : null,\n lobbyCount: canAdmit && snapshot !== null\n ? snapshot.lobby.filter((entry) => entry.state === \"knocking\").length\n : 0,\n recording: snapshot?.recording === \"recording\" || snapshot?.recording === \"stopping\",\n // S6c: the autoplay region is known only inside the room (no remote audio exists elsewhere).\n audioBlocked: inCall && snapshot !== null ? snapshot.mediaRegion.audioOutput === \"blocked\" : null,\n camera: deviceInPlay && snapshot !== null\n ? observedDevice(snapshot.media.camera, snapshot.mediaRegion.permission.camera, lockedFor(snapshot, \"camera\", inCall))\n : atPrejoin ? observedPreview(region.preview.camera, region.permission.camera) : null,\n microphone: deviceInPlay && snapshot !== null\n ? observedDevice(snapshot.media.microphone, snapshot.mediaRegion.permission.microphone, lockedFor(snapshot, \"microphone\", inCall))\n : atPrejoin ? observedPreview(region.preview.microphone, region.permission.microphone) : null,\n };\n}\n\n/** The organizer's lock (or a host's mute) as the person's own device state. */\nfunction lockedFor(snapshot: MeetSnapshot, kind: \"microphone\" | \"camera\", inCall: boolean): \"locked\" | \"muted_by_host\" | null {\n if (!inCall) return null;\n const role = effectiveRole(snapshot);\n if (isLockedFor(snapshot.livePolicy, role, kind)) return \"locked\";\n if (\n snapshot.livePolicy.recordingConsent === \"view_only_until_agree\" &&\n consentPending(snapshot.livePolicy, role, snapshot.recording === \"recording\", snapshot.recordingConsentGiven)\n ) {\n return \"locked\";\n }\n if (kind === \"microphone\" && snapshot.mutedByHost) return \"muted_by_host\";\n return null;\n}\n\n/** A phase the consumer renders itself (link resolution, the guest-name step, a dead link). */\nexport function staticObservation(phase: ObservedPhase, reason: string | null = null): MeetObservation {\n return {\n phase,\n reason,\n role: \"none\",\n connection: phase === \"disconnected\" ? \"server_unreachable\" : \"stable\",\n hostPresent: null,\n lobbyCount: 0,\n recording: false,\n audioBlocked: null,\n camera: null,\n microphone: null,\n };\n}\n\n/** The root element's attributes. Unknown values are omitted, never guessed. */\nexport function rootAttributes(o: MeetObservation): Record<string, string> {\n const attrs: Record<string, string> = {\n \"data-meet-root\": \"\",\n \"data-meet-phase\": o.phase,\n \"data-meet-role\": o.role,\n \"data-meet-connection\": o.connection,\n \"data-meet-lobby-count\": String(o.lobbyCount),\n \"data-meet-recording\": String(o.recording),\n };\n if (o.reason !== null) attrs[\"data-meet-reason\"] = o.reason;\n if (o.hostPresent !== null) attrs[\"data-meet-host-present\"] = String(o.hostPresent);\n if (o.audioBlocked !== null) attrs[\"data-meet-audio-blocked\"] = String(o.audioBlocked);\n if (o.camera !== null) attrs[\"data-meet-camera\"] = o.camera;\n if (o.microphone !== null) attrs[\"data-meet-microphone\"] = o.microphone;\n attrs[\"data-meet-regions\"] = JSON.stringify({\n connection: o.connection,\n camera: o.camera,\n mic: o.microphone,\n audio_output: o.audioBlocked === null ? null : o.audioBlocked ? \"blocked\" : \"playing\",\n recording: o.recording,\n });\n return attrs;\n}\n\n/** The contract's per-person connection vocabulary. `unknown` is not a reading. */\nexport function observedQuality(quality: ConnectionQuality): string | null {\n switch (quality) {\n case \"excellent\":\n case \"good\":\n return \"good\";\n case \"poor\":\n return \"poor\";\n case \"lost\":\n return \"lost\";\n default:\n return null;\n }\n}\n\n/** `[data-meet-participant]` attributes for one person's tile or roster row. */\nexport function participantAttributes(p: MeetParticipant): Record<string, string> {\n const attrs: Record<string, string> = {\n \"data-meet-participant\": \"\",\n \"data-meet-participant-identity\": p.identity,\n \"data-meet-participant-name\": p.displayName,\n \"data-meet-participant-role\": observedRole(p.role, p.standing),\n \"data-meet-participant-self\": String(p.isLocal),\n };\n // A person who dropped without leaving is `reconnecting` (S6b), whatever their last quality was.\n const quality = p.reconnecting === true ? \"reconnecting\" : observedQuality(p.quality);\n if (quality !== null) attrs[\"data-meet-participant-connection\"] = quality;\n return attrs;\n}\n\n/** The listen-only notice's contract code, from why the microphone is off. */\nexport function microphoneNoticeCode(track: LocalTrackState, permission: PermissionReading = \"unknown\"): \"mic-blocked\" | \"device-lost\" | null {\n const reason = deviceUnavailableReason(track, permission);\n if (reason === null) return null;\n return deviceNoticeCode(\"microphone\", reason) === \"mic-blocked\" ? \"mic-blocked\" : \"device-lost\";\n}\n","/**\n * MS — SPEAKING WHILE MUTED (CORE-DESIGN §11 `speaking-while-muted`). A pure detector fed the local\n * microphone level while the person muted themselves: sound that lasts `holdMs` (gaps shorter than\n * `gapMs` do not break it) fires ONCE, then stays quiet for `cooldownMs` so the nudge never nags.\n * The level source is the platform's (a browser analyser in the web skin); the rule lives here.\n */\n\nexport interface SpeakingWhileMutedOptions {\n /** Peak level 0..1 that counts as speech. */\n readonly threshold?: number;\n /** How long speech must last before the nudge. */\n readonly holdMs?: number;\n /** A pause shorter than this does not end the speech. */\n readonly gapMs?: number;\n /** After a nudge, no second one for this long. */\n readonly cooldownMs?: number;\n}\n\nexport interface SpeakingWhileMutedDetector {\n /** One level reading at time `at` (ms). True exactly when the nudge should show now. */\n feed(level: number, at: number): boolean;\n /** The person unmuted (or left): start over, cooldown included. */\n reset(): void;\n}\n\nexport function createSpeakingWhileMutedDetector(options: SpeakingWhileMutedOptions = {}): SpeakingWhileMutedDetector {\n const threshold = options.threshold ?? 0.06;\n const holdMs = options.holdMs ?? 600;\n const gapMs = options.gapMs ?? 400;\n const cooldownMs = options.cooldownMs ?? 20_000;\n let speechStart: number | null = null;\n let lastLoud: number | null = null;\n let firedAt: number | null = null;\n return {\n feed(level, at) {\n if (level >= threshold) {\n if (speechStart === null || lastLoud === null || at - lastLoud > gapMs) speechStart = at;\n lastLoud = at;\n } else if (lastLoud !== null && at - lastLoud > gapMs) {\n speechStart = null;\n lastLoud = null;\n }\n if (speechStart === null || at - speechStart < holdMs) return false;\n if (firedAt !== null && at - firedAt < cooldownMs) return false;\n firedAt = at;\n speechStart = null;\n lastLoud = null;\n return true;\n },\n reset() {\n speechStart = null;\n lastLoud = null;\n firedAt = null;\n },\n };\n}\n"],"mappings":"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","names":["text","row","refusal","text","refusal","text","byId","ms","pending","options","num","text","count","existing","text","current","count","count","byId","text","ms","readState","refusal","media","now","text","refusal","text","formatDurationMs","when","formatDurationMs","refusal","upsert","text"]}
1
+ {"version":3,"sources":["../src/core/ai.ts","../src/core/errors.ts","../src/core/connection-supervisor.ts","../src/core/leave.ts","../src/core/tokens.ts","../src/core/note-taker.ts","../src/core/transcript.ts","../src/core/channels.ts","../src/core/reasons.ts","../src/core/phase.ts","../src/core/types.ts","../src/core/calls.ts","../src/core/repository.ts","../src/core/app-panel.ts","../src/core/collab.ts","../src/core/names.ts","../src/core/recording.ts","../src/core/live-policy.ts","../src/core/attendance.ts","../src/core/store.ts","../src/core/room-events.ts","../src/core/devices.ts","../src/core/format.ts","../src/core/invitations.ts","../src/core/layout.ts","../src/core/capabilities.ts","../src/core/room-driver.ts","../src/core/network-health.ts","../src/core/livekit-driver.ts","../src/core/meetings.ts","../src/core/api.ts","../src/core/after.ts","../src/core/management.ts","../src/core/media-supervisor.ts","../src/core/room.ts","../src/core/effects.ts","../src/core/in-room.ts","../src/core/record.ts","../src/core/record-cache.ts","../src/core/reactions.ts","../src/core/collab-state.ts","../src/core/observation.ts","../src/core/speaking-detector.ts"],"sourcesContent":["/**\n * AI-NATIVE, ON BY DEFAULT — and SERVER-OWNED as of 0.3.0 (R4, D2, D16).\n *\n * 🚨 **THE PACKAGE NO LONGER KNOWS WHO ANSWERS.** Through 0.2.1 a host injected\n * `MeetAgents` — four database row ids — and this module ran the agent itself,\n * shipping the transcript up as `variables`. Both halves of that are gone:\n *\n * 1. **No agent id crosses this boundary, and none is injected.** Reasoning runs\n * behind Mandates whose Holder is resolved from the database at run time\n * (`meet.live_notes`, `meet.live_intelligence`, `meet.wrap_up`), so an\n * organization rebinds the job in the Mandate admin with no release of this\n * package and no change in the host. A host that had to supply an id was a\n * host that could supply the WRONG id, and a capability that silently\n * disappeared when nobody had filled a config line.\n * 2. **No transcript crosses this boundary either.** `POST /v1/meet/intelligence/ask`\n * takes `{meeting_id, question}` and NOTHING else; the answer is built\n * server-side from durable rows. Captions are hints and this client holds\n * only hints (D16) — sending them would be sending the model our guesses.\n *\n * What is left here is small and honest: ask a question and render the answer\n * as it streams, summon or dismiss the note-taker, end the meeting for\n * everyone, and report the server's refusal in the server's own words.\n *\n * 🚨 **THE USER-INPUT LAW still holds** and is now the server's to keep:\n * `question` is the one genuine human utterance in this module, and it is the\n * only free text this package sends anywhere near a model.\n */\n\nimport { readMatrxNdjsonStream } from \"@ai-matrx/agents\";\nimport type { MeetApi } from \"./api\";\nimport { MeetError, SERVER_FAILED_SENTENCE, isRawServerText } from \"./errors\";\nimport type { NoteTakerRefusal } from \"./note-taker\";\nimport type { MeetStore } from \"./store\";\nimport { MEET_ROUTES, MEETING_PASS_HEADER } from \"./tokens\";\nimport type { MeetingId, RoomName } from \"./types\";\n\nexport interface MeetAiOptions {\n /** The ONE org-aware aidream client (MRI-A16). Owns base url and headers. */\n api: MeetApi;\n store: MeetStore;\n /**\n * Resolves the current meeting's room pass for a guest Q&A request. The\n * provider owns this because it alone has both the active room and its\n * persisted guest pass. `null` leaves the header absent for signed-in record\n * Q&A, where the session remains the credential.\n */\n resolveRoomToken?: (() => string | null) | undefined;\n}\n\n/** What the answer was built from. The server measures it; we never guess it. */\nexport interface MeetAnswerCoverage {\n readonly segmentsUsed: number;\n readonly segmentsTotal: number;\n readonly truncated: boolean;\n readonly speakers: readonly string[];\n}\n\nexport interface MeetAiAnswer {\n readonly text: string;\n readonly coverage: MeetAnswerCoverage | null;\n}\n\nexport interface AskArgs {\n readonly meetingId: MeetingId;\n readonly question: string;\n readonly signal?: AbortSignal | undefined;\n /** Called with the answer so far, every time it grows. Rendering is live. */\n readonly onText?: ((text: string) => void) | undefined;\n /** The server's phase, verbatim, so a slow answer is never a bare spinner. */\n readonly onPhase?: ((phase: string) => void) | undefined;\n}\n\nexport interface NoteTakerDispatch {\n readonly dispatched: boolean;\n /** The server's sentence. Never empty on a refusal. */\n readonly detail: string;\n}\n\nexport interface MeetAi {\n /** In-meeting Q&A. The ONE human utterance this package sends (D16). */\n ask(args: AskArgs): Promise<MeetAiAnswer>;\n /** Ask aidream to dispatch the note-taker INTO the room. Host-only. */\n startNoteTaker(args: { meetingId: MeetingId; roomName: RoomName }): Promise<NoteTakerDispatch>;\n /**\n * Make sure the note-taker (the speech-to-text) is in the room because THIS viewer turned\n * captions on. Open to everyone in the meeting, guests included. Never throws: a refusal\n * is stored as `captionsRefusal` in the server's own words.\n */\n requestCaptions(args: { meetingId: MeetingId }): Promise<void>;\n /** Dismiss it. Nothing automatic brings a dismissed note-taker back (D9). */\n stopNoteTaker(args: { meetingId: MeetingId }): Promise<NoteTakerDispatch>;\n}\n\n/** The event vocabulary `/intelligence/ask` emits, pinned by the server's FEATURE.md. */\nexport const MEET_ANSWER_EVENTS = {\n chunk: \"chunk\",\n info: \"info\",\n phase: \"phase\",\n error: \"error\",\n end: \"end\",\n} as const;\n\n/** The `info` event carrying what the answer was built from. */\nexport const MEET_ANSWER_CONTEXT_EVENT = \"meet_intelligence_context\";\n\n/**\n * The coverage numbers, wherever the canonical emitter put them.\n *\n * 🚨 THE LIVE WIRE NESTS THEM UNDER `metadata`. `InfoPayload` is\n * `{code, system_message, user_message, metadata}`, so the numbers arrive at\n * `data.metadata.segments_used` — not at `data.segments_used`, which is what\n * this function read until it was walked against the real route on 2026-09-08\n * and quietly reported no coverage at all. Every shape is read now, and the\n * fixture in `ai.test.ts` is a VERBATIM line off the production stream.\n */\nfunction readCoverage(data: unknown): MeetAnswerCoverage | null {\n if (typeof data !== \"object\" || data === null) return null;\n const row = data as Record<string, unknown>;\n const candidates: Record<string, unknown>[] = [row];\n for (const key of [\"metadata\", \"data\"]) {\n const nested = row[key];\n if (typeof nested === \"object\" && nested !== null && !Array.isArray(nested)) {\n candidates.push(nested as Record<string, unknown>);\n }\n }\n const source =\n candidates.find(\n (entry) =>\n typeof entry[\"segments_used\"] === \"number\" ||\n typeof entry[\"segments_total\"] === \"number\",\n ) ?? null;\n if (source === null) return null;\n const used = source[\"segments_used\"];\n const total = source[\"segments_total\"];\n const speakers = source[\"speakers\"];\n return {\n segmentsUsed: typeof used === \"number\" ? used : 0,\n segmentsTotal: typeof total === \"number\" ? total : 0,\n truncated: source[\"transcript_truncated\"] === true,\n speakers: Array.isArray(speakers)\n ? speakers.filter((entry): entry is string => typeof entry === \"string\")\n : [],\n };\n}\n\nfunction readText(data: unknown): string {\n if (typeof data === \"string\") return data;\n if (typeof data !== \"object\" || data === null) return \"\";\n const row = data as Record<string, unknown>;\n // `phase` and `system_message` are on this list because the live emitter\n // sends `{\"phase\":\"generating\"}` and `{\"system_message\":\"Reading the meeting\n // transcript\"}`. Without them a phase rendered as \"[object Object]\" —\n // found by walking the real route, not by any test written from the docs.\n for (const key of [\"text\", \"content\", \"delta\", \"t\", \"phase\", \"system_message\"]) {\n const value = row[key];\n if (typeof value === \"string\") return value;\n }\n return \"\";\n}\n\nexport { SERVER_FAILED_SENTENCE, isRawServerText } from \"./errors\";\n\n/**\n * THE ONE REFUSAL → MeetError (0.7.8). A non-OK aidream response becomes an\n * error whose `message` is the server's sentence for a PERSON and whose\n * `remedy` is the server's remedy (or the caller's fallback) — never the\n * operation name, a status code alone, or an exception class. Every door that\n * calls aidream builds its refusal here, so no door can drift back.\n */\nexport async function refusalFromResponse(\n response: Response,\n operation: string,\n fallbackRemedy: string,\n): Promise<MeetError> {\n let body: unknown = null;\n try {\n body = await response.json();\n } catch {\n body = null;\n }\n const said = readServerRefusal(body);\n const code =\n response.status === 403 || response.status === 401\n ? \"forbidden\"\n : response.status === 404\n ? \"not-found\"\n : response.status === 409\n ? \"conflict\"\n : \"transport\";\n const error = new MeetError(\n code,\n said?.reason ?? SERVER_FAILED_SENTENCE,\n said !== null && said.remedy.length > 0 ? said.remedy : fallbackRemedy,\n );\n (error as { operation?: string; status?: number }).operation = operation;\n (error as { operation?: string; status?: number }).status = response.status;\n return error;\n}\n\ninterface ServerRefusal {\n readonly reason: string;\n readonly remedy: string;\n}\n\n/**\n * THE SERVER'S REFUSAL, READ RATHER THAN RE-WRITTEN.\n *\n * aidream answers a refused dispatch with a body carrying its own `reason` and\n * `remedy` — \"no worker is registered under matrx-note-taker\", and what to do\n * about it. FastAPI nests a raised `HTTPException` under `detail`, and a\n * validation failure produces a list. All three shapes are read here so the\n * screen shows the system's own words instead of \"something went wrong\".\n */\nexport function readServerRefusal(body: unknown): ServerRefusal | null {\n if (typeof body === \"string\") {\n return body.trim().length === 0 ? null : { reason: body.trim(), remedy: \"\" };\n }\n if (typeof body !== \"object\" || body === null) return null;\n const row = body as Record<string, unknown>;\n const detail = row[\"detail\"];\n if (Array.isArray(detail)) {\n // FastAPI's validation shape: `{msg, loc}`. The FIELD is in `loc` and the\n // field is the whole point — \"Field required\" alone names nothing.\n const messages = detail\n .map((entry) => {\n if (typeof entry !== \"object\" || entry === null) return String(entry);\n const row = entry as Record<string, unknown>;\n const message = String(row[\"msg\"] ?? \"\");\n const where = Array.isArray(row[\"loc\"])\n ? row[\"loc\"].filter((part): part is string => typeof part === \"string\").join(\".\")\n : \"\";\n return where.length > 0 ? `${message} (${where})` : message;\n })\n .filter((entry) => entry.length > 0);\n return messages.length === 0 ? null : { reason: messages.join(\"; \"), remedy: \"\" };\n }\n if (typeof detail === \"object\" && detail !== null) return readServerRefusal(detail);\n // 🚨 NEVER A CLASS NAME. aidream's crash body is `message: \"Internal server\n // error: UnsupportedClientFeatureError\"` beside `user_message` for a person;\n // through 0.7.7 this read `message` whatever it said, so a crash reached a\n // host's toast as a Python class name (verifier, 2026-09-27). A raw message\n // now falls through to `user_message`. A HUMAN `message` still wins: Meet's\n // own sentences carry the specific reason and its \"Fix:\", which a generic\n // `user_message` wrapper would bury.\n const humanOrNull = (value: unknown): string | null =>\n typeof value === \"string\" && value.trim().length > 0 && !isRawServerText(value)\n ? value.trim()\n : null;\n const reason =\n humanOrNull(row[\"reason\"]) ??\n humanOrNull(row[\"message\"]) ??\n humanOrNull(row[\"user_message\"]) ??\n (typeof detail === \"string\" ? humanOrNull(detail) : null) ??\n (typeof row[\"message\"] === \"string\" ||\n typeof row[\"error\"] === \"string\" ||\n typeof detail === \"string\"\n ? SERVER_FAILED_SENTENCE\n : null);\n if (reason === null) return null;\n const remedy = typeof row[\"remedy\"] === \"string\" ? row[\"remedy\"] : \"\";\n if (remedy.length > 0) return { reason, remedy };\n // THE SERVER WRITES ITS REMEDY INLINE. Meet's own errors read\n // \"<what happened>. Fix: <what to do>.\" — splitting it here is what puts the\n // fix on its own line in the panel instead of burying it mid-paragraph.\n const fix = reason.indexOf(\"Fix:\");\n if (fix > 0) {\n return {\n reason: reason.slice(0, fix).trim(),\n remedy: reason.slice(fix + \"Fix:\".length).trim(),\n };\n }\n return { reason, remedy };\n}\n\nexport function createMeetAi(options: MeetAiOptions): MeetAi {\n /**\n * Every request here goes through the ONE org-aware client (MRI-A16), which\n * puts `x-organization-id` on it — the 0.1.3 lesson, now structural rather\n * than remembered. A refused session is translated into THIS surface's own\n * sentence, because \"sign in\" means something specific for the note-taker.\n */\n async function send(input: {\n path: string;\n body: Record<string, unknown>;\n operation: string;\n requireAuth: boolean;\n headers?: Record<string, string> | undefined;\n signal?: AbortSignal | undefined;\n }): Promise<Response> {\n try {\n return await options.api.request({\n path: input.path,\n method: \"POST\",\n requireAuth: input.requireAuth,\n operation: input.operation,\n body: input.body,\n ...(input.headers !== undefined ? { headers: input.headers } : {}),\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n });\n } catch (error) {\n if ((error as { code?: string }).code === \"session-unavailable\") {\n throw new MeetError(\n \"forbidden\",\n `${input.operation}: this action needs a signed-in host`,\n \"A guest cannot summon or dismiss the note-taker, and the control is hidden for \" +\n \"them. Sign in as the meeting's host to use it.\",\n );\n }\n throw error;\n }\n }\n\n async function refusalFor(response: Response, operation: string): Promise<NoteTakerRefusal> {\n let body: unknown = null;\n try {\n body = await response.json();\n } catch {\n try {\n body = await response.text();\n } catch {\n body = null;\n }\n }\n const parsed = readServerRefusal(body);\n // 🚨 A NAMED SERVER GAP, ANNOUNCED RATHER THAN PAPERED OVER. 0.3.0 stopped\n // sending `agent_id` because which agent transcribes is the server's\n // business (D2), but aidream's `NoteTakerStartRequest` still DECLARES that\n // field, so a server that has not landed the MRI-A2 side answers FastAPI's\n // validation error — which reads like a client bug to the person clicking.\n // This translates it into what is actually true. It never invents an id.\n if (response.status === 422 && (parsed?.reason ?? \"\").includes(\"agent_id\")) {\n return {\n status: 422,\n reason:\n \"This server still expects the caller to name the note-taker agent, and this \" +\n \"version of @ai-matrx/meet deliberately does not.\",\n remedy:\n \"The note-taker's identity is a Mandate binding the server resolves (D2). Update \" +\n \"aidream so `/v1/meet/agent/start` resolves the agent itself — register item \" +\n \"MRI-A2/A6. Until then the note-taker cannot be summoned from this surface, and \" +\n \"nothing about this meeting is being transcribed.\",\n };\n }\n return {\n status: response.status,\n reason:\n parsed?.reason ??\n `${operation}: the server answered ${response.status} with no reason.`,\n remedy:\n parsed?.remedy !== undefined && parsed.remedy.length > 0\n ? parsed.remedy\n : \"The server refused and said nothing more. The meeting continues WITHOUT the \" +\n \"note-taker and this surface says so — a silent failure here is a meeting \" +\n \"everyone believes is being minuted.\",\n };\n }\n\n async function dispatch(\n route: string,\n body: Record<string, unknown>,\n operation: string,\n ): Promise<NoteTakerDispatch> {\n // The session check happens BEFORE the request, so a guest's refusal is a\n // package sentence rather than a round trip the server would reject anyway.\n const response = await send({ path: route, body, operation, requireAuth: true });\n if (!response.ok) {\n const refusal = await refusalFor(response, operation);\n // THE REFUSAL IS STATE, not only an exception: every surface renders the\n // server's sentence until the next attempt, rather than a control that\n // looks like it did nothing.\n options.store.patch({ noteTakerRefusal: refusal });\n throw new MeetError(\n response.status === 403 ? \"forbidden\" : \"transport\",\n refusal.reason,\n refusal.remedy,\n );\n }\n options.store.patch({ noteTakerRefusal: null });\n let payload: unknown = null;\n try {\n payload = await response.json();\n } catch {\n payload = null;\n }\n const row =\n typeof payload === \"object\" && payload !== null\n ? (payload as Record<string, unknown>)\n : {};\n return {\n dispatched: row[\"dispatched\"] === true,\n detail: typeof row[\"detail\"] === \"string\" ? row[\"detail\"] : \"\",\n };\n }\n\n return {\n async ask({ meetingId, question, signal, onText, onPhase }) {\n const trimmed = question.trim();\n if (trimmed.length === 0) {\n throw new MeetError(\n \"misconfigured\",\n \"meet.ai.ask: an empty question\",\n \"Type a question. Nothing is sent until there is one.\",\n );\n }\n const roomToken = options.resolveRoomToken?.() ?? null;\n const response = await send({\n path: MEET_ROUTES.intelligenceAsk,\n // THE WHOLE BODY. No transcript, no context, no agent id (D16).\n body: { meeting_id: meetingId, question: trimmed },\n operation: \"meet.ai.ask\",\n // A GUEST MAY ASK. The server decides whether it can answer.\n requireAuth: false,\n ...(roomToken !== null ? { headers: { [MEETING_PASS_HEADER]: roomToken } } : {}),\n ...(signal !== undefined ? { signal } : {}),\n });\n if (!response.ok) {\n // Both refusals happen BEFORE the stream opens, by the server's design,\n // so this is an honest status rather than a stream that dies mid-answer.\n const refusal = await refusalFor(response, \"meet.ai.ask\");\n throw new MeetError(\n response.status === 403 || response.status === 401\n ? \"forbidden\"\n : response.status === 404\n ? \"not-found\"\n : \"transport\",\n refusal.reason,\n refusal.remedy,\n );\n }\n const body = response.body;\n if (body === null || body === undefined) {\n throw new MeetError(\n \"invalid-response\",\n \"meet.ai.ask: the answer arrived with no stream to read\",\n \"The route streams NDJSON. A response with no body means a proxy buffered or \" +\n \"stripped it — check for a gateway that does not pass streaming responses.\",\n );\n }\n let text = \"\";\n let coverage: MeetAnswerCoverage | null = null;\n let failure: ServerRefusal | null = null;\n for await (const envelope of readMatrxNdjsonStream(\n body,\n signal !== undefined ? { signal } : {},\n )) {\n if (envelope.event === MEET_ANSWER_EVENTS.chunk) {\n const piece = readText(envelope.data);\n if (piece.length > 0) {\n // THE ANSWER IS THE CONCATENATED CHUNKS — the server deliberately\n // sends no second copy in a terminal payload, so this accumulation\n // IS the answer, not an optimization.\n text += piece;\n onText?.(text);\n }\n continue;\n }\n if (envelope.event === MEET_ANSWER_EVENTS.info) {\n const parsed = readCoverage(envelope.data);\n if (parsed !== null) coverage = parsed;\n continue;\n }\n if (envelope.event === MEET_ANSWER_EVENTS.phase) {\n const phase = readText(envelope.data);\n onPhase?.(phase.length > 0 ? phase : String(envelope.data));\n continue;\n }\n if (envelope.event === MEET_ANSWER_EVENTS.error) {\n failure = readServerRefusal(envelope.data);\n }\n }\n if (failure !== null) {\n throw new MeetError(\n \"transport\",\n failure.reason,\n failure.remedy.length > 0\n ? failure.remedy\n : \"The answer stopped mid-stream and the server said why. Ask again once the \" +\n \"named cause is cleared.\",\n );\n }\n if (text.trim().length === 0) {\n throw new MeetError(\n \"invalid-response\",\n \"meet.ai.ask: the stream ended with no answer in it\",\n \"Nothing was returned to show. Check the meeting has a Holder bound to \" +\n \"`meet.live_intelligence` at /mandates, then ask again.\",\n );\n }\n return { text: text.trim(), coverage };\n },\n\n startNoteTaker: ({ meetingId, roomName }) =>\n dispatch(\n MEET_ROUTES.noteTakerStart,\n {\n // NO agent_id. Which agent transcribes is the server's business\n // (D2) — this package could not name one if it wanted to.\n meeting_id: meetingId,\n room_name: roomName,\n organization_id: options.api.organizationId,\n },\n \"meet.ai.startNoteTaker\",\n ),\n\n requestCaptions: async ({ meetingId }) => {\n const roomToken = options.resolveRoomToken?.() ?? null;\n const operation = \"meet.ai.requestCaptions\";\n try {\n const response = await send({\n path: MEET_ROUTES.noteTakerCaptions,\n body: { meeting_id: meetingId },\n operation,\n requireAuth: roomToken === null,\n ...(roomToken !== null ? { headers: { [MEETING_PASS_HEADER]: roomToken } } : {}),\n });\n if (!response.ok) {\n options.store.patch({ captionsRefusal: await refusalFor(response, operation) });\n return;\n }\n let payload: unknown = null;\n try {\n payload = await response.json();\n } catch {\n payload = null;\n }\n const row =\n typeof payload === \"object\" && payload !== null ? (payload as Record<string, unknown>) : {};\n const detail = typeof row[\"detail\"] === \"string\" ? row[\"detail\"] : \"\";\n // `dispatched: false` is also \"already in the room\"; the hook decides from the\n // note-taker's real state whether this sentence matters.\n options.store.patch({\n captionsRefusal: row[\"dispatched\"] === true ? null : { status: 200, reason: detail, remedy: \"\" },\n });\n } catch (error) {\n options.store.patch({\n captionsRefusal: {\n status: 0,\n reason: (error as Error).message,\n remedy: (error as { remedy?: string }).remedy ?? \"\",\n },\n });\n }\n },\n\n stopNoteTaker: ({ meetingId }) =>\n dispatch(\n MEET_ROUTES.noteTakerStop,\n { meeting_id: meetingId },\n \"meet.ai.stopNoteTaker\",\n ),\n };\n}\n","/**\n * ONE error classification for the whole package (C22).\n *\n * The banned alternative is a host catching a LiveKit or PostgREST error and\n * deciding for itself whether the camera is busy, the token expired, or the\n * meeting is locked — which is how the same five-branch `if` gets copy-pasted\n * into every consumer and drifts. Everything this package throws is a\n * `MeetError` with a stable `code` and a `remedy` SENTENCE. Nothing fails\n * silently, and nothing fails without saying what to do about it.\n *\n * The device codes exist because \"it doesn't work\" in a video product is almost\n * always one of four specific, differently-remediable things: permission\n * denied, no such device, device already in use by another app, or insecure\n * context (http, where `getUserMedia` does not exist at all). A single\n * \"media-error\" would make the UI say the wrong sentence three times out of\n * four.\n */\n\nexport type MeetErrorCode =\n | \"session-unavailable\"\n | \"forbidden\"\n | \"not-found\"\n | \"conflict\"\n | \"token\"\n | \"transport\"\n | \"invalid-response\"\n | \"misconfigured\"\n | \"permission-denied\"\n | \"device-not-found\"\n | \"device-in-use\"\n | \"insecure-context\"\n | \"media-unsupported\"\n | \"room-locked\"\n | \"lobby-denied\"\n | \"capacity\"\n /** Meet wave 1: the meeting was cancelled — its link resolves, its room never opens. */\n | \"cancelled\"\n | \"unknown\";\n\nexport class MeetError extends Error {\n readonly code: MeetErrorCode;\n /** What to DO. Rendered to the user for the device codes. */\n readonly remedy: string;\n override readonly cause?: unknown;\n /**\n * The join gate's reason code (CORE-DESIGN §2.5) when the server named one: `not_found`,\n * `cancelled`, `ended`, `locked`, `removed`, `lobby_rejects`, `not_invited`… The phase\n * renders `refused:<reason>` (or `ended`) from it — never from the HTTP status.\n */\n readonly reason: string | null;\n /**\n * A refusal that lifts by itself says when: whole seconds from the moment the server answered\n * (a denial's cooldown, `reknock_cooldown_seconds`). Null for every refusal that does not.\n */\n readonly retryAfterSeconds: number | null;\n\n constructor(\n code: MeetErrorCode,\n message: string,\n remedy: string,\n cause?: unknown,\n reason?: string | null,\n retryAfterSeconds?: number | null,\n ) {\n super(message);\n this.name = \"MeetError\";\n this.code = code;\n this.remedy = remedy;\n this.reason = reason ?? null;\n this.retryAfterSeconds = retryAfterSeconds ?? null;\n if (cause !== undefined) this.cause = cause;\n }\n\n /** True when retrying after the host re-establishes a session is the fix. */\n get isRetryable(): boolean {\n return (\n this.code === \"session-unavailable\" ||\n this.code === \"transport\" ||\n this.code === \"token\"\n );\n }\n\n /** True when the user, not the code, must act — the UI shows `remedy`. */\n get isUserActionable(): boolean {\n return (\n this.code === \"permission-denied\" ||\n this.code === \"device-not-found\" ||\n this.code === \"device-in-use\" ||\n this.code === \"insecure-context\"\n );\n }\n}\n\n/** What a person reads when the server failed without a sentence of its own. */\nexport const SERVER_FAILED_SENTENCE =\n \"The server hit an unexpected problem and nothing was changed.\";\n\n/**\n * A developer's text, not a person's: `Internal server error: SomeError`, a\n * bare `SomeError` class name, or a machine code like `internal_error`.\n */\nexport function isRawServerText(value: string): boolean {\n const text = value.trim();\n return (\n /^internal server error\\b/i.test(text) ||\n /^[A-Z][A-Za-z0-9]*(Error|Exception)$/.test(text) ||\n /^[a-z][a-z0-9]*(_[a-z0-9]+)+$/.test(text)\n );\n}\n\n/**\n * A database message a PERSON can read: our RPCs raise plain sentences (\"Only\n * the host can remove a guest.\"); Postgres's own errors (\"permission denied\n * for table …\", \"violates foreign key …\") and class names are not.\n */\nfunction humanDatabaseSentence(message: string): string | null {\n const text = message.trim();\n if (text.length === 0 || isRawServerText(text)) return null;\n if (\n /permission denied for|violates|relation \"|does not exist|syntax error|column \"|jwt|pgrst|duplicate key|null value in/i.test(\n text,\n )\n )\n return null;\n return text;\n}\n\ninterface ErrorLike {\n name?: string | undefined;\n message?: string | undefined;\n code?: string | undefined;\n}\n\nconst FORBIDDEN_CODES = new Set([\"42501\", \"PGRST301\", \"PGRST302\"]);\n// P0002 is how `platform.refuse_not_found` answers through PostgREST (\"no meeting for that\n// link\"): the one honest not-found, and the reason the join gate's `not_found` screen needs.\nconst NOT_FOUND_CODES = new Set([\"PGRST116\", \"PGRST205\", \"P0002\"]);\n// 40001: the row changed since the caller read it (meet_update_meeting's version\n// compare-and-swap). 55000: the object is in a state that refuses the change (a\n// cancelled meeting). Both are conflicts a person resolves by reloading.\nconst CONFLICT_CODES = new Set([\"23505\", \"40001\", \"55000\"]);\n\nconst SESSION_MARKERS = [\n \"auth session missing\",\n \"jwt expired\",\n \"no api key found\",\n \"refresh_token_not_found\",\n \"invalid claim: missing sub claim\",\n];\n\n/**\n * THE ONE PLACE a raw database/transport error becomes a typed one. Called at\n * every repository boundary; nothing else in the package inspects a raw error,\n * and no consumer should ever have to.\n */\nexport function normalizeMeetError(error: unknown, operation: string): MeetError {\n if (error instanceof MeetError) return error;\n\n const raw = (error ?? {}) as ErrorLike;\n const message = typeof raw.message === \"string\" ? raw.message : String(error);\n const code = typeof raw.code === \"string\" ? raw.code : undefined;\n const lowered = message.toLowerCase();\n\n if (raw.name === \"SessionUnavailableError\" || SESSION_MARKERS.some((m) => lowered.includes(m))) {\n return new MeetError(\n \"session-unavailable\",\n `${operation}: no Supabase session available (${message})`,\n \"Normal during sign-in and token refresh. The package retries once after the host's \" +\n \"session source resolves; log it as a warning, never as an error.\",\n error,\n );\n }\n if (code !== undefined && FORBIDDEN_CODES.has(code)) {\n // A PERSON reads `message` and `remedy` (toasts, panels); the database's\n // own words stay on `cause` for the developer. Through 0.7.8 this read\n // \"meet.x: denied by the database (42501: …)\" and a remedy about RLS.\n return new MeetError(\n \"forbidden\",\n humanDatabaseSentence(message) ?? \"You don't have access to do that in this meeting.\",\n \"Ask the meeting's host to do it, or to give you access.\",\n error,\n );\n }\n if (code !== undefined && NOT_FOUND_CODES.has(code)) {\n return new MeetError(\n \"not-found\",\n `${operation}: not found (${code}: ${message})`,\n \"The row is gone, or RLS hides it. If this is PGRST205 the communication.meet_* tables \" +\n \"are not deployed yet — see MEET_SCHEMA in repository.ts for the exact contract.\",\n error,\n \"not_found\",\n );\n }\n if (code !== undefined && CONFLICT_CODES.has(code)) {\n return new MeetError(\n \"conflict\",\n humanDatabaseSentence(message) ?? \"This meeting changed a moment ago, so nothing was saved.\",\n \"Reload to see the latest, then try again.\",\n error,\n );\n }\n const hint = (error as { hint?: unknown } | null)?.hint;\n return new MeetError(\n \"transport\",\n /failed to fetch|networkerror|load failed|network request failed/i.test(message)\n ? \"The server could not be reached.\"\n : (humanDatabaseSentence(message) ?? SERVER_FAILED_SENTENCE),\n typeof hint === \"string\" && humanDatabaseSentence(hint) !== null\n ? hint.trim()\n : \"Nothing was changed — retry in a moment; if it keeps happening, reload the page.\",\n error,\n );\n}\n\n/**\n * THE DEVICE ERROR TRANSLATOR — the second-most-copied block in every video app.\n *\n * `getUserMedia` rejects with DOMException names, and every one of them means\n * something a user could fix if we told them. This is the whole reason the\n * device layer lives in the package (C22 test 3: less-than-perfect unless done\n * perfectly).\n */\nexport function normalizeDeviceError(error: unknown, operation: string): MeetError {\n if (error instanceof MeetError) return error;\n const raw = (error ?? {}) as ErrorLike;\n const name = raw.name ?? \"\";\n const message = typeof raw.message === \"string\" ? raw.message : String(error);\n\n switch (name) {\n case \"NotAllowedError\":\n case \"SecurityError\":\n return new MeetError(\n \"permission-denied\",\n `${operation}: camera/microphone permission denied`,\n \"Allow camera and microphone access for this site, then rejoin. On macOS also check \" +\n \"System Settings › Privacy & Security — a browser denied there shows the same error.\",\n error,\n );\n case \"NotFoundError\":\n case \"OverconstrainedError\":\n return new MeetError(\n \"device-not-found\",\n `${operation}: no matching camera/microphone (${message})`,\n \"The selected device is gone — usually an unplugged headset. Pick another device in \" +\n \"the pre-join panel; the package falls back to the system default automatically.\",\n error,\n );\n case \"NotReadableError\":\n case \"AbortError\":\n return new MeetError(\n \"device-in-use\",\n `${operation}: the device is already in use (${message})`,\n \"Another application (or another tab) holds the camera. Close it and try again — the \" +\n \"hardware, not the meeting, is what refused.\",\n error,\n );\n case \"TypeError\":\n return new MeetError(\n \"insecure-context\",\n `${operation}: media devices are unavailable in this context (${message})`,\n \"getUserMedia only exists on https or localhost. Serve the app over https.\",\n error,\n );\n default:\n return new MeetError(\n \"media-unsupported\",\n `${operation}: ${message}`,\n \"This browser refused the media request for a reason the package does not recognize. \" +\n \"Report it with this message so the class gets a named remedy rather than a shrug.\",\n error,\n );\n }\n}\n\n/**\n * SCREEN SHARE IS NOT A CAMERA. `getDisplayMedia` rejects with `NotAllowedError`\n * when the person simply closes the picker, and that is a CHOICE, not a\n * permission problem — so it must never wear the camera/microphone sentence\n * `normalizeDeviceError` gives that same error name.\n *\n * WHAT BROWSERS ACTUALLY REPORT (the reason there are three outcomes, not two):\n * - Chrome / Edge: a closed picker is `NotAllowedError` \"Permission denied\"; an\n * OS-level refusal (macOS Screen Recording not granted) is `NotAllowedError`\n * \"Permission denied by system\"; a permissions-policy / enterprise block is\n * `NotAllowedError` naming the policy or `display-capture`. The MESSAGE is the\n * only difference, so it is what we read.\n * - Firefox and Safari: a closed picker, a denied OS permission and a policy\n * block are ALL `NotAllowedError` with the same generic message (\"The request\n * is not allowed by the user agent or the platform…\"). They CANNOT be told\n * apart. That is the `ambiguous` outcome: the first one stays silent (a person\n * who closed the picker wants no scolding), and the SECOND one in a row shows\n * the notice, so nobody is left clicking a dead button with no explanation.\n */\nexport type ScreenShareDenial = \"blocked\" | \"ambiguous\" | \"other\";\n\nconst SYSTEM_BLOCK = /by system|permissions?[- ]policy|display-capture|disallowed|not allowed by the system/i;\n\nexport function classifyScreenShareError(error: unknown): ScreenShareDenial {\n const read = (value: unknown, key: \"name\" | \"message\"): string => {\n const v = (value as ErrorLike | null | undefined)?.[key];\n return typeof v === \"string\" ? v : \"\";\n };\n const cause = error instanceof MeetError ? error.cause : undefined;\n const denied = [error, cause].some((value) => {\n const name = read(value, \"name\");\n return name === \"NotAllowedError\" || name === \"AbortError\" || name === \"SecurityError\";\n });\n if (denied) {\n const text = [read(error, \"message\"), read(cause, \"message\")].join(\" \");\n return SYSTEM_BLOCK.test(text) || [error, cause].some((v) => read(v, \"name\") === \"SecurityError\")\n ? \"blocked\"\n : \"ambiguous\";\n }\n // A MeetError we made ourselves for a plain denial (no browser error inside).\n if (error instanceof MeetError && error.code === \"permission-denied\" && cause === undefined) {\n return \"ambiguous\";\n }\n return \"other\";\n}\n\n/** True only for a denial that MAY be the person closing the picker. */\nexport function isScreenShareCancel(error: unknown): boolean {\n return classifyScreenShareError(error) === \"ambiguous\";\n}\n\nexport const SCREEN_SHARE_BLOCKED_REMEDY =\n \"Your browser or computer is blocking screen sharing. On a Mac, open System Settings > \" +\n \"Privacy & Security > Screen Recording, turn it on for your browser, then restart the \" +\n \"browser. On a work computer, ask your administrator. You are still in the call.\";\n\nexport function normalizeScreenShareError(error: unknown, operation: string): MeetError {\n if (error instanceof MeetError) return error;\n const raw = (error ?? {}) as ErrorLike;\n const message = typeof raw.message === \"string\" ? raw.message : String(error);\n const kind = classifyScreenShareError(error);\n if (kind === \"blocked\") {\n return new MeetError(\n \"permission-denied\",\n `${operation}: screen sharing is blocked (${message})`,\n SCREEN_SHARE_BLOCKED_REMEDY,\n error,\n );\n }\n if (kind === \"ambiguous\") {\n return new MeetError(\n \"permission-denied\",\n `${operation}: screen sharing was cancelled or refused`,\n \"Choose a screen, window or tab in the picker to share it. If no picker appeared, \" +\n SCREEN_SHARE_BLOCKED_REMEDY,\n error,\n );\n }\n return new MeetError(\n \"media-unsupported\",\n `${operation}: screen sharing is not available (${message})`,\n \"This browser or device could not share a screen. You are still in the call; try a \" +\n \"desktop browser to present.\",\n error,\n );\n}\n\n/** A response whose SHAPE is wrong — the ingress boundary, not the wire. */\nexport function invalidResponse(operation: string, detail: string): MeetError {\n return new MeetError(\n \"invalid-response\",\n `${operation}: ${detail}`,\n \"The server returned a shape this package does not accept. Rendering it would put a lie \" +\n \"on the screen, so it is refused here. Fix the endpoint's return contract.\",\n );\n}\n","/**\n * CS — THE CONNECTION SUPERVISOR (CORE-DESIGN §3.3, step S6b).\n *\n * The one owner of \"is this tab still connected, and if not, why, and what happens next\". It\n * never touches a browser global itself — the network signal and the reachability probe arrive\n * as ports (`NetworkPort`, `probe`), with web implementations below for the provider to hand in —\n * and it never moves the phase except through `store.dispatch` (via the engine's `autoRejoin`).\n *\n * What it decides:\n *\n * 1. **The `connection` region** (§3.2): `stable · signal_reconnecting · reconnecting · offline ·\n * server_unreachable`. `offline` = the device has no network (`navigator.onLine` false);\n * `server_unreachable` = online, but our API or the media server does not answer the probe.\n * The two get different screens because they have different fixes.\n * 2. **The banner debounce** (`reconnect_banner_delay_ms`): the region is honest at once (the\n * contract attribute), the banner appears only if the blip outlasts the delay (`net-blip`).\n * 3. **Auto-rejoin after the SDK gives up** (`auto_rejoin`, `auto_rejoin_max_attempts`): from\n * `disconnected`, when the network is back AND the probe answers, rejoin with a fresh mint into\n * the same identity — the server restores role / hand / admission from the person row. Attempts\n * are only spent while reachable; a long offline spell costs none. Manual Rejoin always works.\n * 4. **The reload-after-offline marker**: while in the call (or dropped from it) the tab keeps a\n * per-tab marker, so a page that Chrome replaced with its own offline page and then reloaded\n * comes back INTO the call instead of to the pre-join (`resume`). A deliberate exit clears it.\n * 5. **The jittered reconnect policy** handed to LiveKit (`createReconnectPolicy`): jitter from\n * attempt 0 (the SDK's default has none on its first two attempts — 650 people reconnecting\n * on the same millisecond is the thundering-herd issue), with the give-up limits as options.\n *\n * Others' drops (`net-others-see-drop`) are the pure ghost helpers at the bottom: an unannounced\n * departure keeps a `reconnecting` tile for `reconnect_grace_seconds`.\n */\n\nimport type { DriverConnectionState, DriverParticipant } from \"./room-driver\";\nimport type { JoinRequest } from \"./room\";\nimport type { ConnectionRegion, MeetStore } from \"./store\";\nimport type { MeetPhase } from \"./phase\";\n\n/* ------------------------------------------------------------------ policy (knobs) */\n\n/** The server-delivered connection knobs (CORE-DESIGN §4.2/§4.3), resolved per meeting. */\nexport interface ConnectionPolicy {\n /** `reconnect_banner_delay_ms`: how long a drop must last before the banner shows. */\n readonly bannerDelayMs: number;\n /** `auto_rejoin`: rejoin by itself after the SDK gives up. */\n readonly autoRejoin: boolean;\n /** `auto_rejoin_max_attempts`: rejoin attempts (while reachable) before only Rejoin is left. */\n readonly autoRejoinMaxAttempts: number;\n /** `reconnect_grace_seconds`: how long others keep a dropped person's `reconnecting` tile. */\n readonly reconnectGraceSeconds: number;\n /**\n * `video_auto_off_after_poor_seconds`: a poor path that lasts this long pauses the person's\n * camera so their audio survives (`net-video-auto-off`); 0 = never.\n */\n readonly videoAutoOffAfterPoorSeconds: number;\n /**\n * `captions_start_timeout_seconds`: how long \"Starting captions\" may last before the person is\n * told, honestly, that captions are unavailable (and offered Try again).\n */\n readonly captionsStartTimeoutSeconds: number;\n}\n\n/** The knob defaults (§4.3) — what an older server that sends nothing gets. */\nexport const DEFAULT_CONNECTION_POLICY: ConnectionPolicy = {\n bannerDelayMs: 5000,\n autoRejoin: true,\n autoRejoinMaxAttempts: 5,\n reconnectGraceSeconds: 30,\n videoAutoOffAfterPoorSeconds: 20,\n captionsStartTimeoutSeconds: 20,\n};\n\nfunction clampInt(value: unknown, min: number, max: number, fallback: number): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return fallback;\n return Math.min(max, Math.max(min, Math.round(value)));\n}\n\n/** The token's `connection_policy` object → the typed policy; anything missing is the default. */\nexport function connectionPolicyFrom(raw: unknown): ConnectionPolicy {\n if (typeof raw !== \"object\" || raw === null) return DEFAULT_CONNECTION_POLICY;\n const row = raw as Record<string, unknown>;\n const d = DEFAULT_CONNECTION_POLICY;\n return {\n bannerDelayMs: clampInt(row[\"reconnect_banner_delay_ms\"], 0, 10_000, d.bannerDelayMs),\n autoRejoin: typeof row[\"auto_rejoin\"] === \"boolean\" ? row[\"auto_rejoin\"] : d.autoRejoin,\n autoRejoinMaxAttempts: clampInt(row[\"auto_rejoin_max_attempts\"], 0, 20, d.autoRejoinMaxAttempts),\n reconnectGraceSeconds: clampInt(row[\"reconnect_grace_seconds\"], 0, 600, d.reconnectGraceSeconds),\n videoAutoOffAfterPoorSeconds: clampInt(row[\"video_auto_off_after_poor_seconds\"], 0, 600, d.videoAutoOffAfterPoorSeconds),\n captionsStartTimeoutSeconds: clampInt(row[\"captions_start_timeout_seconds\"], 1, 600, d.captionsStartTimeoutSeconds),\n };\n}\n\n/* ------------------------------------------------------------------ LiveKit policy */\n\n/** LiveKit's `ReconnectPolicy` shape, restated so this module never imports the SDK. */\nexport interface ReconnectPolicyLike {\n nextRetryDelayInMs(context: { readonly retryCount: number; readonly elapsedMs: number }): number | null;\n}\n\nexport interface ReconnectPolicyOptions {\n /** Base delay per attempt (ms); the last one repeats. Default: LiveKit's own schedule. */\n readonly delaysMs?: readonly number[];\n /** Stop after this long since the drop (ms). Default 45 000 — about the SDK's own ~44 s. */\n readonly giveUpMs?: number;\n /** Stop after this many attempts. Default 12. */\n readonly maxAttempts?: number;\n readonly random?: () => number;\n}\n\n/** livekit-client 2.22 `DEFAULT_RETRY_DELAYS_IN_MS` (verified in the SDK source, 2026-10-08). */\nexport const LIVEKIT_BASE_DELAYS_MS: readonly number[] = [0, 300, 1200, 2700, 4800, 7000];\n\n/**\n * THE JITTERED RECONNECT POLICY. Every attempt — attempt 0 included — gets a random spread of at\n * least 500 ms (half the base delay, capped at 2 s), so a room whose SFU blinks does not hear\n * every client come back on the same millisecond.\n */\nexport function createReconnectPolicy(options: ReconnectPolicyOptions = {}): ReconnectPolicyLike {\n const delays = options.delaysMs !== undefined && options.delaysMs.length > 0 ? options.delaysMs : LIVEKIT_BASE_DELAYS_MS;\n const giveUpMs = options.giveUpMs ?? 45_000;\n const maxAttempts = options.maxAttempts ?? 12;\n const random = options.random ?? Math.random;\n return {\n nextRetryDelayInMs({ retryCount, elapsedMs }) {\n if (retryCount >= maxAttempts || elapsedMs >= giveUpMs) return null;\n const base = delays[Math.min(retryCount, delays.length - 1)] ?? 0;\n const spread = Math.min(2000, Math.max(500, base / 2));\n return Math.round(base + random() * spread);\n },\n };\n}\n\n/* ------------------------------------------------------------------ ports */\n\n/** Whether the device has a network, and when that changes (the platform port's `network`). */\nexport interface NetworkPort {\n online(): boolean;\n subscribe(listener: (online: boolean) => void): () => void;\n}\n\n/** `navigator.onLine` + `online`/`offline` events; outside a browser it is always online. */\nexport function createWebNetworkPort(): NetworkPort {\n const scope = globalThis as unknown as {\n navigator?: { onLine?: boolean };\n addEventListener?: (type: string, fn: () => void) => void;\n removeEventListener?: (type: string, fn: () => void) => void;\n };\n return {\n online: () => scope.navigator?.onLine !== false,\n subscribe(listener) {\n if (typeof scope.addEventListener !== \"function\") return () => undefined;\n const on = (): void => listener(true);\n const off = (): void => listener(false);\n scope.addEventListener(\"online\", on);\n scope.addEventListener(\"offline\", off);\n return () => {\n scope.removeEventListener?.(\"online\", on);\n scope.removeEventListener?.(\"offline\", off);\n };\n },\n };\n}\n\n/**\n * THE REACHABILITY PROBE: does each URL answer at all? An opaque (`no-cors`) GET resolves for any\n * HTTP answer and rejects only when the network path is down — exactly the question, with no\n * CORS dependency. All URLs must answer; a timeout is a \"no\".\n */\nexport function createReachabilityProbe(urls: () => readonly string[], timeoutMs = 4000): () => Promise<boolean> {\n return async () => {\n const targets = urls().filter((url) => url.length > 0);\n if (targets.length === 0 || typeof fetch !== \"function\") return true;\n const one = async (url: string): Promise<boolean> => {\n const controller = typeof AbortController === \"function\" ? new AbortController() : null;\n const timer = setTimeout(() => controller?.abort(), timeoutMs);\n try {\n await fetch(url, {\n method: \"GET\",\n mode: \"no-cors\",\n cache: \"no-store\",\n credentials: \"omit\",\n ...(controller !== null ? { signal: controller.signal } : {}),\n });\n return true;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n };\n const answers = await Promise.all(targets.map(one));\n return answers.every(Boolean);\n };\n}\n\n/** `wss://x.livekit.cloud` → `https://x.livekit.cloud` (what the probe can GET). */\nexport function httpUrlOf(serverUrl: string): string {\n return serverUrl.replace(/^ws(s?):\\/\\//i, \"http$1://\");\n}\n\n/* ------------------------------------------------------------------ resume marker */\n\n/** Per-tab storage for the reload-after-offline marker (session storage on the web). */\nexport interface ResumeStorage {\n load(roomName: string): ResumeMarker | null;\n save(roomName: string, marker: ResumeMarker): void;\n clear(roomName: string): void;\n}\n\nexport interface ResumeMarker {\n readonly request: JoinRequest;\n /** Epoch ms of the last time this tab was in (or dropped from) the call. */\n readonly at: number;\n}\n\n/** How long after the last in-call moment a reload still goes straight back in. */\nexport const RESUME_WINDOW_MS = 15 * 60_000;\n\nconst RESUME_KEY = \"mx-meet:resume:\";\n\nexport function createWebResumeStorage(): ResumeStorage | undefined {\n const storage = (globalThis as unknown as { sessionStorage?: Storage }).sessionStorage;\n if (storage === undefined) return undefined;\n return {\n load(roomName) {\n try {\n const raw = storage.getItem(RESUME_KEY + roomName);\n if (raw === null) return null;\n const parsed = JSON.parse(raw) as ResumeMarker;\n return typeof parsed?.at === \"number\" && typeof parsed.request?.roomName === \"string\" ? parsed : null;\n } catch {\n return null;\n }\n },\n save(roomName, marker) {\n try {\n storage.setItem(RESUME_KEY + roomName, JSON.stringify(marker));\n } catch {\n // Storage full or blocked: a reload lands on the pre-join, as before. Not fatal.\n }\n },\n clear(roomName) {\n try {\n storage.removeItem(RESUME_KEY + roomName);\n } catch {\n // Already gone.\n }\n },\n };\n}\n\n/** Phases a deliberate or server-decided exit lands on: the marker is cleared, no resume. */\nconst CLEARS_RESUME = (phase: MeetPhase): boolean =>\n phase === \"left\" ||\n phase === \"removed\" ||\n phase === \"superseded\" ||\n phase === \"inactive\" ||\n phase === \"ended\" ||\n phase === \"record\" ||\n phase === \"prejoin\" ||\n phase === \"denied\" ||\n phase === \"knock_expired\" ||\n phase.startsWith(\"refused:\");\n\n/**\n * `wr-refresh-while-waiting`: a reload while waiting goes straight back to waiting (the same\n * person key, the same knock — the server keeps its place), never to the name or device steps.\n */\nconst WAITING_RESUMES = (phase: MeetPhase): boolean => phase.startsWith(\"waiting:\");\n\n/* ------------------------------------------------------------------ the supervisor */\n\nexport interface ConnectionSupervisorOptions {\n readonly store: MeetStore;\n readonly network: NetworkPort;\n /** Do our API and the media server answer? */\n readonly probe: () => Promise<boolean>;\n readonly policy: () => ConnectionPolicy;\n /** Rejoin from `disconnected` with a fresh mint (the engine's `autoRejoin`). */\n readonly autoRejoin: () => Promise<void>;\n /** The request to resume with (current media state folded in), or null. */\n readonly currentRequest: () => JoinRequest | null;\n readonly resume?: ResumeStorage | undefined;\n readonly now?: () => number;\n readonly random?: () => number;\n /** How often to re-probe while not stable (ms). Default 3000. */\n readonly probeEveryMs?: number;\n readonly report?: (level: \"info\" | \"warn\", message: string) => void;\n}\n\nexport interface ConnectionSupervisor {\n /** LiveKit's connection state, as the driver reports it. */\n driverState(state: DriverConnectionState): void;\n /** The region to show beside `disconnected` when the server's state read failed. */\n exitConnection(): Exclude<ConnectionRegion, \"stable\" | \"reconnecting\" | \"signal_reconnecting\">;\n /** The resume marker for `roomName` if it is fresh, else null (and a stale one is cleared). */\n resumable(roomName: string): ResumeMarker | null;\n dispose(): void;\n}\n\n/** Backoff before auto-rejoin attempt `n` (0-based): 1 s, 2 s, 4 s … capped at 15 s, jittered ±50%. */\nexport function autoRejoinDelayMs(attempt: number, random: () => number = Math.random): number {\n const base = Math.min(15_000, 1000 * 2 ** Math.max(0, attempt));\n return Math.round(base * (0.5 + random()));\n}\n\nexport function createConnectionSupervisor(options: ConnectionSupervisorOptions): ConnectionSupervisor {\n const { store, network } = options;\n const now = options.now ?? (() => Date.now());\n const random = options.random ?? Math.random;\n const probeEveryMs = options.probeEveryMs ?? 3000;\n\n /** What LiveKit last said, independent of the network: null = connected / not in a room. */\n let driverRegion: \"reconnecting\" | \"signal_reconnecting\" | null = null;\n let probeTimer: ReturnType<typeof setTimeout> | null = null;\n let probing = false;\n let bannerTimer: ReturnType<typeof setTimeout> | null = null;\n let rejoinTimer: ReturnType<typeof setTimeout> | null = null;\n let rejoinAttempts = 0;\n let lastPhase: MeetPhase = store.snapshot().phase;\n let lastSavedAt = 0;\n let disposed = false;\n\n const inCall = (): boolean => store.snapshot().phase === \"in_call\";\n const dropped = (): boolean => store.snapshot().phase === \"disconnected\";\n\n function setRegion(next: ConnectionRegion): void {\n if (store.snapshot().connection !== next) store.patch({ connection: next });\n }\n\n /** The region the facts say, given the last probe answer (true/false) or none (null). */\n function regionFor(reachable: boolean | null): ConnectionRegion {\n if (!network.online()) return \"offline\";\n if (reachable === false) return \"server_unreachable\";\n if (dropped()) return reachable === true ? \"stable\" : store.snapshot().connection;\n return driverRegion ?? \"stable\";\n }\n\n function needsWatch(): boolean {\n const snap = store.snapshot();\n if (dropped()) return true;\n return inCall() && (driverRegion !== null || snap.connection !== \"stable\");\n }\n\n function stopProbe(): void {\n if (probeTimer !== null) clearTimeout(probeTimer);\n probeTimer = null;\n }\n\n function scheduleProbe(delayMs: number): void {\n if (disposed || probeTimer !== null) return;\n probeTimer = setTimeout(() => {\n probeTimer = null;\n void runProbe();\n }, delayMs);\n }\n\n async function runProbe(): Promise<void> {\n if (disposed || probing) return;\n if (!needsWatch()) return;\n if (!network.online()) {\n setRegion(\"offline\");\n scheduleProbe(probeEveryMs);\n return;\n }\n probing = true;\n let reachable = false;\n try {\n reachable = await options.probe();\n } catch {\n reachable = false;\n } finally {\n probing = false;\n }\n if (disposed) return;\n if (inCall() || dropped()) setRegion(regionFor(reachable));\n if (dropped() && reachable) maybeRejoin();\n if (needsWatch()) scheduleProbe(Math.round(probeEveryMs * (0.75 + random() * 0.5)));\n }\n\n function syncBanner(): void {\n const snap = store.snapshot();\n const unstable = snap.phase === \"in_call\" && snap.connection !== \"stable\";\n if (!unstable) {\n if (bannerTimer !== null) clearTimeout(bannerTimer);\n bannerTimer = null;\n if (snap.connectionBanner) store.patch({ connectionBanner: false });\n return;\n }\n if (snap.connectionBanner || bannerTimer !== null) return;\n const delay = options.policy().bannerDelayMs;\n if (delay <= 0) {\n store.patch({ connectionBanner: true });\n return;\n }\n bannerTimer = setTimeout(() => {\n bannerTimer = null;\n const latest = store.snapshot();\n if (latest.phase === \"in_call\" && latest.connection !== \"stable\" && !latest.connectionBanner) {\n store.patch({ connectionBanner: true });\n }\n }, delay);\n }\n\n function stopRejoin(): void {\n if (rejoinTimer !== null) clearTimeout(rejoinTimer);\n rejoinTimer = null;\n }\n\n /** From `disconnected`, once reachable: one backoff-delayed attempt (if the policy allows). */\n function maybeRejoin(): void {\n if (disposed || rejoinTimer !== null || !dropped()) return;\n const policy = options.policy();\n if (!policy.autoRejoin) return;\n if (rejoinAttempts >= policy.autoRejoinMaxAttempts) return;\n if (!network.online()) return;\n const attempt = rejoinAttempts;\n rejoinTimer = setTimeout(() => {\n rejoinTimer = null;\n if (disposed || !dropped() || !network.online()) return;\n rejoinAttempts = attempt + 1;\n options.report?.(\"info\", `meet: rejoining by itself (attempt ${rejoinAttempts} of ${policy.autoRejoinMaxAttempts})`);\n void options.autoRejoin().catch(() => undefined);\n }, autoRejoinDelayMs(attempt, random));\n }\n\n function saveMarker(force: boolean): void {\n const resume = options.resume;\n const snap = store.snapshot();\n if (resume === undefined || snap.roomName === null) return;\n const at = now();\n if (!force && at - lastSavedAt < 10_000) return;\n const request = options.currentRequest();\n if (request === null) return;\n lastSavedAt = at;\n resume.save(snap.roomName, { request, at });\n }\n\n const unsubscribeStore = store.subscribe(() => {\n if (disposed) return;\n const snap = store.snapshot();\n const phase = snap.phase;\n const changed = phase !== lastPhase;\n lastPhase = phase;\n if (changed) {\n if (phase === \"in_call\") {\n rejoinAttempts = 0;\n stopRejoin();\n saveMarker(true);\n } else if (WAITING_RESUMES(phase)) {\n stopRejoin();\n driverRegion = null;\n saveMarker(true);\n } else if (phase === \"disconnected\") {\n driverRegion = null;\n saveMarker(true);\n // Reachable right now? Probe at once; the probe starts the rejoin.\n stopProbe();\n scheduleProbe(0);\n } else {\n stopRejoin();\n if (phase !== \"joining\") driverRegion = null;\n if (CLEARS_RESUME(phase) && snap.roomName !== null) options.resume?.clear(snap.roomName);\n }\n } else if (phase === \"in_call\" || phase === \"disconnected\") {\n saveMarker(false);\n }\n syncBanner();\n });\n\n const unsubscribeNetwork = network.subscribe((online) => {\n if (disposed) return;\n if (!online) {\n if (inCall() || dropped()) setRegion(\"offline\");\n stopRejoin();\n stopProbe();\n if (needsWatch()) scheduleProbe(probeEveryMs);\n return;\n }\n // Back online: is it really (our servers answer)? The probe decides the region and the rejoin.\n stopProbe();\n if (needsWatch()) scheduleProbe(0);\n });\n\n return {\n driverState(state) {\n if (disposed) return;\n if (state === \"connected\") {\n driverRegion = null;\n if (inCall() || store.snapshot().phase === \"joining\") setRegion(network.online() ? \"stable\" : \"offline\");\n if (!needsWatch()) stopProbe();\n return;\n }\n if (state === \"reconnecting\" || state === \"signal_reconnecting\") {\n driverRegion = state;\n if (inCall()) {\n // Honest at once: no network is `offline` before any probe; otherwise LiveKit's word,\n // until the probe says whether our servers answer.\n setRegion(network.online() ? state : \"offline\");\n stopProbe();\n scheduleProbe(0);\n }\n }\n },\n exitConnection() {\n return network.online() ? \"server_unreachable\" : \"offline\";\n },\n resumable(roomName) {\n const resume = options.resume;\n if (resume === undefined) return null;\n const marker = resume.load(roomName);\n if (marker === null) return null;\n if (now() - marker.at > RESUME_WINDOW_MS) {\n resume.clear(roomName);\n return null;\n }\n return marker;\n },\n dispose() {\n disposed = true;\n stopProbe();\n stopRejoin();\n if (bannerTimer !== null) clearTimeout(bannerTimer);\n bannerTimer = null;\n unsubscribeStore();\n unsubscribeNetwork();\n },\n };\n}\n\n/* ------------------------------------------------------------------ others' drops (ghosts) */\n\n/**\n * LiveKit departure reasons that mean \"they meant to go, or were sent\": no ghost. Anything else —\n * no reason, a signal close, a timeout, a media failure — is a drop, and the tile stays as\n * `reconnecting` for `reconnect_grace_seconds` so others see the truth instead of a vanished tile.\n */\nconst DELIBERATE_DEPARTURES: ReadonlySet<string> = new Set([\n \"CLIENT_INITIATED\",\n \"PARTICIPANT_REMOVED\",\n \"ROOM_DELETED\",\n \"ROOM_CLOSED\",\n \"DUPLICATE_IDENTITY\",\n \"USER_REJECTED\",\n \"USER_UNAVAILABLE\",\n \"SIP_TRUNK_FAILURE\",\n]);\n\nexport function isUnannouncedDeparture(reason: string | null): boolean {\n return reason === null || !DELIBERATE_DEPARTURES.has(reason);\n}\n\nexport interface Ghost {\n readonly participant: DriverParticipant;\n readonly until: number;\n}\n\n/**\n * The roster with the live ghosts merged in. A ghost whose identity is back in the live roster is\n * dropped (the person returned — one tile, never a copy); an expired one is dropped.\n */\nexport function mergeGhosts(\n live: readonly DriverParticipant[],\n ghosts: ReadonlyMap<string, Ghost>,\n at: number,\n): { readonly roster: readonly (DriverParticipant & { readonly reconnecting?: boolean })[]; readonly kept: Map<string, Ghost> } {\n const present = new Set(live.map((p) => p.identity));\n const kept = new Map<string, Ghost>();\n const extra: (DriverParticipant & { readonly reconnecting: boolean })[] = [];\n for (const [identity, ghost] of ghosts) {\n if (present.has(identity) || ghost.until <= at) continue;\n kept.set(identity, ghost);\n extra.push({\n ...ghost.participant,\n micEnabled: false,\n cameraEnabled: false,\n screenSharing: false,\n reconnecting: true,\n });\n }\n return { roster: [...live, ...extra], kept };\n}\n","/**\n * S8 — LEAVE INTENTS (CORE-DESIGN §2.7 `/v1/meet/leave`, §4.2 `host_leave_prompt` · `end_confirm`,\n * §11 `leave-or-end-choice`, `host-leave-assign`).\n *\n * What the host's Leave OFFERS is the meeting's behavior profile, resolved by the server and\n * carried on the token response (`leave_policy`). What each choice DOES is the server's: the\n * `/leave` door decides from the pass and the run row whether this person holds the run's host\n * powers. The skin owns only the words.\n */\n\nimport type { ParticipantRole } from \"./types\";\n\nexport type HostLeavePrompt = \"leave_or_end\" | \"must_assign_or_end\" | \"leave_only\";\n\nexport interface LeavePolicy {\n readonly hostLeavePrompt: HostLeavePrompt;\n /** Ask \"End for everyone?\" once more before ending (Teams). */\n readonly endConfirm: boolean;\n}\n\n/** The Meet profile's values — used only when the server sent none (an ad-hoc call). */\nexport const DEFAULT_LEAVE_POLICY: LeavePolicy = { hostLeavePrompt: \"leave_or_end\", endConfirm: false };\n\nconst PROMPTS: readonly HostLeavePrompt[] = [\"leave_or_end\", \"must_assign_or_end\", \"leave_only\"];\n\nexport function leavePolicyFrom(raw: unknown): LeavePolicy {\n if (typeof raw !== \"object\" || raw === null) return DEFAULT_LEAVE_POLICY;\n const value = raw as Record<string, unknown>;\n const prompt = value[\"host_leave_prompt\"];\n const confirm = value[\"end_confirm\"];\n return {\n hostLeavePrompt: PROMPTS.includes(prompt as HostLeavePrompt)\n ? (prompt as HostLeavePrompt)\n : DEFAULT_LEAVE_POLICY.hostLeavePrompt,\n endConfirm: typeof confirm === \"boolean\" ? confirm : DEFAULT_LEAVE_POLICY.endConfirm,\n };\n}\n\n/** One thing the Leave control can do. */\nexport type LeaveChoice = \"leave\" | \"assign_and_leave\" | \"end_for_all\";\n\n/**\n * The choices Leave offers, in order. Not the host → just leave (no menu). The host:\n * `leave_or_end` (Meet) → leave · end for everyone; `must_assign_or_end` (Zoom) → assign a new\n * host (when someone else is here) · end for everyone, with a plain leave only when nobody is\n * left to hand it to; `leave_only` (Teams) → leave · end meeting.\n */\nexport function leaveChoices(args: {\n readonly policy: LeavePolicy;\n readonly role: ParticipantRole | null;\n readonly othersPresent: boolean;\n}): readonly LeaveChoice[] {\n if (args.role !== \"host\") return [\"leave\"];\n switch (args.policy.hostLeavePrompt) {\n case \"must_assign_or_end\":\n return args.othersPresent ? [\"assign_and_leave\", \"end_for_all\"] : [\"leave\", \"end_for_all\"];\n case \"leave_only\":\n case \"leave_or_end\":\n return [\"leave\", \"end_for_all\"];\n }\n}\n","/**\n * THE TOKEN FLOW — the ONLY authorization path into a room (R1).\n *\n * 🚨 **THE CLIENT NEVER HOLDS A LIVEKIT SECRET.** Not in an env var, not in a\n * build-time constant, not \"just for local dev\". A LiveKit API secret in a\n * browser bundle mints a token for ANY room in the project, including one the\n * holder was never invited to — it is a full compromise of every meeting in the\n * organization. This package therefore has no code path that could accept one:\n * it asks aidream, and aidream mints.\n *\n * Four hard parts live here so no host reimplements them (C22):\n *\n * 1. **Single flight.** A React strict-mode double effect, a re-render, and a\n * reconnect all ask at once; they share ONE request. Two tokens for one\n * identity is not harmless — LiveKit disconnects the older session, which\n * looks exactly like \"my call randomly drops when I open the tab\".\n * 2. **Expiry is honored BEFORE it bites.** A token is refreshed at 80% of its\n * life, not when a reconnect fails at 100%. A meeting that outlives its\n * token and cannot reconnect is the classic 2-hour-meeting bug.\n * 3. **Guests are first-class, not an exception.** A guest has no session and\n * the request carries a display name instead; the SERVER decides what a\n * guest may do. There is no client-side \"is this person allowed\" branch.\n * 4. **A missing session is a lifecycle moment.** Between sign-in and token\n * arrival, the fetch retries ONCE after the host's session resolves and then\n * reports `session-unavailable` as a warning — never a red error, never a\n * retry storm.\n */\n\nimport { connectionPolicyFrom, type ConnectionPolicy } from \"./connection-supervisor\";\nimport { leavePolicyFrom, type LeavePolicy } from \"./leave\";\nimport type { MeetApi } from \"./api\";\nimport { invalidResponse, MeetError } from \"./errors\";\nimport type { JoinRefusalReason } from \"./reasons\";\nimport type {\n AdmissionState,\n ObserversVisibleTo,\n MeetIdentity,\n ParticipantIdentity,\n ParticipantRole,\n ParticipantStanding,\n RoomName,\n} from \"./types\";\n\nexport interface RoomToken {\n readonly token: string;\n readonly serverUrl: string;\n readonly roomName: RoomName;\n /** S7: the PERSON key (`user:<id>` · `guest:<device>`) — what every roster tile and row uses. */\n readonly identity: ParticipantIdentity;\n /** S7: this tab's LiveKit identity, `<person>~<tab>` (the person key from an older server). */\n readonly connectionIdentity?: string | undefined;\n /** S7: `companion` for a \"join here too\" connection (no audio publish, speaker off). */\n readonly connectionKind?: \"main\" | \"companion\" | undefined;\n /**\n * S7: set when the person is ALREADY in the room on another tab or device and has not chosen\n * yet — the meeting's `second_device` rule (`ask_switch_or_join` · `ask_transfer_or_add`). No\n * LiveKit token was issued (`token` is empty); the pre-join offers the choice.\n */\n readonly secondDeviceChoice?: string | null | undefined;\n /** S7: the meeting's `second_device` rule, always — told to a second tab of this browser. */\n readonly secondDeviceRule?: string | null | undefined;\n readonly displayName: string;\n /** ISO. The package refreshes at 80% of the remaining life. */\n readonly expiresAt: string;\n /** What the SERVER granted. The client renders from this; it never decides. */\n readonly canPublish: boolean;\n readonly canSubscribe: boolean;\n readonly canRecord: boolean;\n readonly isHost: boolean;\n /**\n * 🚨 THE SERVER'S WORD ON WHO THIS JOINER IS (MRI-A11) — `host · cohost · presenter ·\n * participant · viewer` (CORE-DESIGN §2.1). The same string is minted into the LiveKit\n * token's participant metadata and its `matrx.participant.role` attribute.\n */\n readonly role: ParticipantRole;\n /** Where this joiner stands with the meeting — beside the role, never inside it. */\n readonly standing: ParticipantStanding | null;\n /**\n * 🚨 THE MEETING PASS (CORE-DESIGN §2.5) — the credential for every aidream meet route,\n * sent as `x-meet-pass`. It is NOT the LiveKit token (`token`), which is for the media\n * connection only. Null for an ad-hoc call room (no meeting) or an older server.\n */\n readonly meetingPass: string | null;\n readonly meetingPassExpiresAt: string | null;\n /** The run (`meet_sessions.id`) this join belongs to; null while waiting before any run. */\n readonly sessionId: string | null;\n /** True when the server put this joiner in the lobby (D6). */\n readonly lobby: boolean;\n /**\n * S4: what the mint wrote on the person row — `admitted` · `knocking` · `waiting_host`.\n * A waiting person then reads `GET /v1/meet/state` every `pollSeconds`.\n */\n readonly admissionState?: AdmissionState | undefined;\n readonly pollSeconds?: number | undefined;\n /**\n * Meet wave 5: the breakout room this pass is for, or null for the main room. The\n * pass is still keyed by the MEETING's room name — it is this person's pass for the\n * meeting, and the server accepts a breakout pass wherever the meeting's is asked.\n */\n readonly breakoutRoomId?: string | null | undefined;\n /** HR-360 wave 2 (MD-16): who sees the \"Observing\" line in People (knob). */\n readonly observersVisibleTo?: ObserversVisibleTo | undefined;\n /**\n * S6b: the meeting's connection knobs (`reconnect_banner_delay_ms`, `auto_rejoin`,\n * `auto_rejoin_max_attempts`, `reconnect_grace_seconds`), resolved by the server's policy\n * ladder. Absent from an older server → the knob defaults.\n */\n readonly connectionPolicy?: ConnectionPolicy | undefined;\n /** S8: what the host's Leave offers for this meeting (`host_leave_prompt`, `end_confirm`). */\n readonly leavePolicy?: LeavePolicy | undefined;\n /** host-reclaim (`host_reclaim = prompt`): the returning owner may take the host powers back. */\n readonly canReclaim?: boolean | undefined;\n}\n\nexport type { AccessTokenSource } from \"./api\";\n\nexport interface TokenClientOptions {\n /** The ONE org-aware aidream client (MRI-A16). Owns base url and headers. */\n api: MeetApi;\n /** Injected clock, so expiry logic is provable without waiting two hours. */\n now?: (() => number) | undefined;\n /**\n * Where a GUEST's pass survives a page close (MRF-1). Absent means passes\n * live only in memory, which is correct for a member (they have a session to\n * mint a new one from) and is why this is optional rather than assumed.\n */\n storage?: RoomTokenStorage | undefined;\n}\n\n/**\n * 🚨 **WHY A GUEST'S PASS MUST OUTLIVE THE TAB (MRF-1).**\n *\n * A guest has no session, so their only credential for a meeting is the room\n * token this server minted for them. After the meeting ends, `/token` refuses\n * to mint another one — correctly: the room is gone and nobody may rejoin it.\n * So a guest who closes the tab and comes back to read the summary has, in a\n * purely in-memory client, *no way to prove they were ever there* — and the one\n * honest thing left to show them is a refusal.\n *\n * Persisting the pass is what makes the durable link durable for them. It is\n * per-device by nature (it is stored beside the device id that minted it) and\n * it expires on its own: `meet.room_token_ttl_seconds` is the organization's\n * setting for how long, and an expired pass produces the renew-the-link\n * sentence, never \"you were never here\".\n */\nexport interface RoomTokenStorage {\n read(roomName: RoomName): string | null;\n write(roomName: RoomName, serialized: string): void;\n remove(roomName: RoomName): void;\n}\n\n/** `localStorage` when it exists; a no-op that SAYS so otherwise. */\nexport function createWebRoomTokenStorage(\n onDiagnostic?: (message: string, remedy: string) => void,\n): RoomTokenStorage {\n const store = (globalThis as { localStorage?: Storage }).localStorage;\n const key = (roomName: RoomName): string => `mx.meet.pass.${roomName}`;\n if (store === undefined) {\n // NEVER SILENT. A guest in a browser with no storage will be refused the\n // record after they close the tab, and that is worth a line rather than a\n // mystery.\n onDiagnostic?.(\n \"This browser exposes no localStorage, so a guest's meeting pass cannot be kept.\",\n \"A guest who closes the tab will have to open the meeting link again before the \" +\n \"record opens. Signed-in members are unaffected.\",\n );\n return { read: () => null, write: () => undefined, remove: () => undefined };\n }\n return {\n read(roomName) {\n try {\n return store.getItem(key(roomName));\n } catch {\n return null;\n }\n },\n write(roomName, serialized) {\n try {\n store.setItem(key(roomName), serialized);\n } catch {\n /* a full or blocked store is a lost convenience, never a lost join */\n }\n },\n remove(roomName) {\n try {\n store.removeItem(key(roomName));\n } catch {\n /* see write */\n }\n },\n };\n}\n\n/**\n * The meeting pass a GUEST's browser kept for a room (MRF-1), or null — read from the same\n * storage `createWebRoomTokenStorage` writes. Used by \"keep your notes\" after sign-up.\n */\nexport function rememberedMeetingPass(\n roomName: RoomName,\n storage: RoomTokenStorage = createWebRoomTokenStorage(),\n): string | null {\n const raw = storage.read(roomName);\n if (raw === null) return null;\n try {\n const parsed = JSON.parse(raw) as Partial<RoomToken>;\n if (parsed.standing !== \"guest\") return null;\n return typeof parsed.meetingPass === \"string\" && parsed.meetingPass.length > 0\n ? parsed.meetingPass\n : null;\n } catch {\n return null;\n }\n}\n\nexport interface TokenRequest {\n readonly roomName: RoomName;\n readonly identity: MeetIdentity;\n /** Present for a meeting join; absent for an ad-hoc call room. */\n readonly meetingId?: string | undefined;\n /** The public meeting slug, when joining by link without knowing the id. */\n readonly slug?: string | undefined;\n /** Meet wave 5: a breakout room of this meeting; absent = the main room. */\n readonly breakoutRoomId?: string | null | undefined;\n /** HR-360 wave 2 (MD-16): ask to join as a silent observer. The server decides. */\n readonly observe?: boolean | undefined;\n /** S7: this tab's claimed id (`core/tab-claim.ts`) — the LiveKit identity is `<person>~<tab>`. */\n readonly tabId?: string | undefined;\n /** S7: the person's answer when they are already in this meeting on another tab or device. */\n readonly secondDevice?: SecondDeviceAnswer | undefined;\n}\n\n/** S7: what a person already in the meeting elsewhere chose (CORE-DESIGN §2.1). */\nexport type SecondDeviceAnswer = \"switch_here\" | \"join_too\";\n\nexport interface TokenClient {\n /** A currently-valid token, minting or refreshing only when it must. */\n get(request: TokenRequest): Promise<RoomToken>;\n /** Drop the cached token for a room (after a `denied`, or on leave). */\n invalidate(roomName: RoomName): void;\n /** True when the cached token is inside its refresh window. */\n needsRefresh(roomName: RoomName): boolean;\n /**\n * The pass this DEVICE holds for a room, **including an expired one** (MRF-1).\n *\n * 🚨 The expired case is the whole reason this returns the token instead of\n * `null`: \"your pass expired, open the link again\" and \"you were never in\n * this meeting\" are different sentences, and only the caller that can see the\n * expiry can say the right one. Never used to join — `get` mints for that.\n */\n remembered(roomName: RoomName): RoomToken | null;\n}\n\n/** The aidream routes this package calls. ONE place, so a rename is one edit. */\n/**\n * EVERY aidream ROUTE THIS PACKAGE CALLS, in one object.\n *\n * This is the contract in both directions: aidream carries a test\n * (`services/meet/tests/test_meet_service.py`) that reads THIS object out of\n * this source file and fails if the server stops serving one of these paths.\n * The package is the spec.\n *\n * `meetingEnd` is pinned here BEFORE the server serves it (MRI-A6 owns landing\n * it). That is deliberate and it is not a lie on screen: the control that calls\n * it renders, and a 404 from a server that has not landed A6 is reported as the\n * honest sentence \"Ending a meeting is not available on this server yet\" —\n * never a dead button, never a spinner, and never a silent no-op.\n */\nexport const MEET_ROUTES = {\n token: \"/api/v1/meet/token\",\n recordingStart: \"/api/v1/meet/recording/start\",\n recordingStop: \"/api/v1/meet/recording/stop\",\n noteTakerStart: \"/api/v1/meet/agent/start\",\n noteTakerStop: \"/api/v1/meet/agent/stop\",\n /** Anyone in the meeting (guests with the room pass included) asks for captions for themselves. */\n noteTakerCaptions: \"/api/v1/meet/agent/captions\",\n /** In-meeting Q&A. Streams NDJSON; body is `{meeting_id, question}` only. */\n intelligenceAsk: \"/api/v1/meet/intelligence/ask\",\n /** A host ends the meeting for everyone. Served by MRI-A6. */\n meetingEnd: \"/api/v1/meet/end\",\n /**\n * THE POST-MEETING RECORD FOR A READER WITH NO DATABASE (MRF-1). A `GET`,\n * authorized by the room token this device already holds, carried in the\n * `x-meet-pass` header — never a query parameter, because a bearer\n * credential in a URL lands in access logs, referrers and history.\n */\n record: \"/api/v1/meet/record\",\n /** A host chooses what guests may read of ONE meeting (MRF-1). */\n recordAccess: \"/api/v1/meet/record/access\",\n /**\n * KEEP YOUR NOTES (2026-10-01): a guest who just created an account presents\n * their kept pass in `x-meet-pass` with their new session, and the\n * meeting they attended becomes theirs to read. Idempotent; never a grant.\n */\n claim: \"/api/v1/meet/claim\",\n /**\n * Meet wave 1 (0.8): bring every invitee up to date after a write — sends the\n * invitation / update / cancellation emails with their `.ics` and re-queues\n * reminders. Idempotent. `{meeting_id}` is substituted by `meetingPath`.\n */\n meetingAnnounce: \"/api/v1/meet/meetings/{meeting_id}/announce\",\n /** Put a one-off meeting on the caller's own Google Calendar (gated by the server). */\n meetingGoogleCalendar: \"/api/v1/meet/meetings/{meeting_id}/google-calendar\",\n /**\n * Meet wave 3: the recap email after the meeting. `GET` = the draft (subject,\n * body from the wrap-up, who it can go to); `POST` = send what the host\n * reviewed (`dry_run` renders and queues nothing). Never sent automatically.\n */\n meetingFollowUp: \"/api/v1/meet/meetings/{meeting_id}/follow-up\",\n /**\n * Meet wave 2: EVERY in-meeting host power (mute, stop video, mute all, ask to\n * unmute, spotlight, lower hands, co-host, hand over, rename, remove, report).\n * Decided by the server from the meeting itself; a guest's pass rides\n * `x-meet-pass`.\n */\n moderate: \"/api/v1/meet/moderate\",\n /** Meet wave 2: the SAVED chat — `POST` sends to everyone, `GET ?meeting_id=` reads history. */\n chat: \"/api/v1/meet/chat\",\n /**\n * Meet wave 5: polls, Q&A, the shared whiteboard and breakout rooms. `POST` acts\n * (`{meeting_id, action, …}`), `GET ?meeting_id=` reads what this viewer may see.\n */\n collab: \"/api/v1/meet/collab\",\n /** Meet wave 5: translate finished caption lines into this viewer's language. */\n captionsTranslate: \"/api/v1/meet/captions/translate\",\n /**\n * S4 — THE STATE READ (CORE-DESIGN §2.6): `GET ?meeting_id=` with `x-meet-pass`. This\n * person's admission (lazy expiry applied), whether anyone who can admit is here, the run's\n * phase. Read every `waiting_poll_seconds` while waiting and on every `mx.state.changed`.\n */\n state: \"/api/v1/meet/state\",\n /** S4: `withdraw` (stop waiting) or `leave` (close this tab); the pass may ride in the body. */\n leave: \"/api/v1/meet/leave\",\n /**\n * S8: a host's browser just called an admission DOOR (`meet_admit` · `meet_deny` ·\n * `meet_undeny` · `meet_admit_all`). Payload-free: the server republishes the room's\n * replicated state (the People badge's waiting count) from the database truth.\n */\n admissionChanged: \"/api/v1/meet/admission/changed\",\n /** S4: a pass holder (guests included) saw the recording notice, for this run. */\n consent: \"/api/v1/meet/consent\",\n} as const;\n\n/** A `MEET_ROUTES` template with its `{meeting_id}` filled in, URL-encoded. */\nexport function meetingPath(template: string, meetingId: string): string {\n return template.replace(\"{meeting_id}\", encodeURIComponent(meetingId));\n}\n\n/** The header the meeting pass rides on (CORE-DESIGN §2.5). Pinned on both sides. */\nexport const MEETING_PASS_HEADER = \"x-meet-pass\";\n\n/** The `x-meet-pass` header for a held token, or no header at all. */\nexport function meetingPassHeaders(token: RoomToken | null | undefined): Record<string, string> {\n const pass = token?.meetingPass ?? null;\n return pass !== null && pass.length > 0 ? { [MEETING_PASS_HEADER]: pass } : {};\n}\n\n/** Refresh at 80% of the token's life — never at the moment it dies. */\nconst REFRESH_RATIO = 0.8;\n\ninterface CacheEntry {\n token: RoomToken;\n mintedAt: number;\n inFlight: Promise<RoomToken> | null;\n}\n\nfunction readString(source: Record<string, unknown>, key: string): string | null {\n const value = source[key];\n return typeof value === \"string\" && value.length > 0 ? value : null;\n}\n\nfunction readBoolean(source: Record<string, unknown>, key: string, fallback: boolean): boolean {\n const value = source[key];\n return typeof value === \"boolean\" ? value : fallback;\n}\n\nconst TOKEN_STANDINGS: readonly ParticipantStanding[] = [\"member\", \"invited\", \"outsider\", \"guest\"];\n\nfunction readStanding(source: Record<string, unknown>, key: string): ParticipantStanding | null {\n const value = source[key];\n return TOKEN_STANDINGS.includes(value as ParticipantStanding) ? (value as ParticipantStanding) : null;\n}\n\nconst TOKEN_ROLES: readonly ParticipantRole[] = [\"host\", \"cohost\", \"presenter\", \"participant\", \"viewer\"];\n\n/** A word this package does not know grants nothing — it is not a host. */\nfunction readRole(source: Record<string, unknown>, key: string): ParticipantRole | null {\n const value = source[key];\n return TOKEN_ROLES.includes(value as ParticipantRole) ? (value as ParticipantRole) : null;\n}\n\nexport function parseRoomToken(body: unknown, roomName: RoomName): RoomToken {\n if (typeof body !== \"object\" || body === null) {\n throw invalidResponse(\"meet.token\", \"the response body was not an object\");\n }\n const envelope = body as Record<string, unknown>;\n // aidream's ordinary envelope is `{ success, data, msg }`; a bare object is\n // accepted too so a self-hosted route does not have to imitate it.\n const payload =\n typeof envelope[\"data\"] === \"object\" && envelope[\"data\"] !== null\n ? (envelope[\"data\"] as Record<string, unknown>)\n : envelope;\n\n const isHost = readBoolean(payload, \"is_host\", false);\n const token = readString(payload, \"token\");\n const serverUrl = readString(payload, \"server_url\") ?? readString(payload, \"serverUrl\");\n const identity = readString(payload, \"identity\");\n const expiresAt = readString(payload, \"expires_at\") ?? readString(payload, \"expiresAt\");\n\n const secondDeviceChoice = readString(payload, \"second_device_choice\");\n if ((token === null && secondDeviceChoice === null) || serverUrl === null || identity === null) {\n throw invalidResponse(\n \"meet.token\",\n \"the response is missing token, server_url, or identity — joining with a partial \" +\n \"token would fail inside LiveKit with an opaque error instead of here\",\n );\n }\n if (expiresAt === null) {\n throw invalidResponse(\n \"meet.token\",\n \"the response carries no expires_at. Without it the package cannot refresh before \" +\n \"the token dies, and a long meeting silently loses its ability to reconnect\",\n );\n }\n const personKey = readString(payload, \"person_key\") ?? identity;\n return {\n token: token ?? \"\",\n serverUrl,\n roomName,\n identity: personKey as ParticipantIdentity,\n connectionIdentity: readString(payload, \"connection_identity\") ?? personKey,\n connectionKind: readString(payload, \"connection_kind\") === \"companion\" ? \"companion\" : \"main\",\n secondDeviceChoice,\n secondDeviceRule: readString(payload, \"second_device_rule\"),\n displayName: readString(payload, \"display_name\") ?? readString(payload, \"displayName\") ?? identity,\n expiresAt,\n canPublish: readBoolean(payload, \"can_publish\", true),\n canSubscribe: readBoolean(payload, \"can_subscribe\", true),\n canRecord: readBoolean(payload, \"can_record\", false),\n isHost,\n // An older server sends `is_host` and no `role`; a guest join sends neither.\n // Deriving the role from what DID arrive keeps a host a host against a\n // server that has not landed MRI-A11 — and never invents a host from\n // silence, because both fallbacks below grant nothing.\n // A word this package does not know grants nothing: `participant`, never a host.\n role: readRole(payload, \"role\") ?? (isHost ? \"host\" : \"participant\"),\n standing: readStanding(payload, \"standing\"),\n meetingPass: readString(payload, \"meeting_pass\"),\n meetingPassExpiresAt: readString(payload, \"meeting_pass_expires_at\"),\n sessionId: readString(payload, \"session_id\"),\n lobby: readBoolean(payload, \"lobby\", false),\n admissionState: (readString(payload, \"admission_state\") as AdmissionState | null) ?? undefined,\n pollSeconds: typeof payload[\"poll_seconds\"] === \"number\" ? (payload[\"poll_seconds\"] as number) : undefined,\n breakoutRoomId: readString(payload, \"breakout_room_id\") ?? null,\n observersVisibleTo: readString(payload, \"observers_visible_to\") === \"hosts\" ? \"hosts\" : \"everyone\",\n connectionPolicy: connectionPolicyFrom(payload[\"connection_policy\"]),\n leavePolicy: leavePolicyFrom(payload[\"leave_policy\"]),\n canReclaim: payload[\"can_reclaim\"] === true,\n };\n}\n\nexport function createTokenClient(options: TokenClientOptions): TokenClient {\n const cache = new Map<RoomName, CacheEntry>();\n const now = options.now ?? (() => Date.now());\n const storage = options.storage;\n\n /** A GUEST's pass is kept; a member's is not — they can always mint again. */\n function remember(token: RoomToken): void {\n if (storage === undefined) return;\n if (token.standing !== \"guest\") return;\n storage.write(token.roomName, JSON.stringify(token));\n }\n\n function recall(roomName: RoomName): RoomToken | null {\n if (storage === undefined) return null;\n const raw = storage.read(roomName);\n if (raw === null) return null;\n try {\n const parsed = JSON.parse(raw) as RoomToken;\n // A stored blob that is not a pass for THIS room proves nothing, and a\n // half-written one must not be handed out as a credential.\n if (typeof parsed.token !== \"string\" || parsed.token.length === 0) return null;\n if (parsed.roomName !== roomName) return null;\n return parsed;\n } catch {\n storage.remove(roomName);\n return null;\n }\n }\n\n function expired(entry: CacheEntry): boolean {\n const expiry = Date.parse(entry.token.expiresAt);\n if (Number.isNaN(expiry)) return true;\n const life = expiry - entry.mintedAt;\n if (life <= 0) return true;\n return now() >= entry.mintedAt + life * REFRESH_RATIO;\n }\n\n async function request(input: TokenRequest, retryOnMissingSession: boolean): Promise<RoomToken> {\n const guest = input.identity.userId === null;\n let response: Response;\n try {\n // THE ORGANIZATION IS A HEADER, NOT ONLY A BODY FIELD — and it is put\n // there by `core/api.ts`, the one client, so no caller can forget it\n // (MRI-A16). A body-only `organization_id` looked right and never\n // arrived: the org-admission middleware refuses first, 400, before the\n // meet router is ever reached.\n response = await options.api.request({\n path: MEET_ROUTES.token,\n method: \"POST\",\n // A GUEST HAS NO SESSION AND IS STILL A FIRST-CLASS JOINER (D6), so\n // only an authenticated join requires one.\n requireAuth: !guest,\n operation: \"meet.token\",\n body: {\n room_name: input.roomName,\n ...(input.meetingId !== undefined ? { meeting_id: input.meetingId } : {}),\n ...(input.slug !== undefined ? { slug: input.slug } : {}),\n ...(input.breakoutRoomId != null ? { breakout_room_id: input.breakoutRoomId } : {}),\n ...(input.observe === true ? { observe: true } : {}),\n organization_id: input.identity.organizationId,\n display_name: input.identity.displayName,\n device_id: input.identity.deviceId,\n guest,\n ...(input.tabId !== undefined ? { tab_id: input.tabId } : {}),\n ...(input.secondDevice !== undefined ? { second_device: input.secondDevice } : {}),\n },\n });\n } catch (error) {\n if (\n retryOnMissingSession &&\n (error as { code?: string }).code === \"session-unavailable\"\n ) {\n // ONE retry, after the host's session source has had a turn. This is\n // the cure for \"909 captured errors in 0.6 seconds\": a read that lands\n // between sign-in and token arrival is a lifecycle moment.\n await Promise.resolve();\n return request(input, false);\n }\n throw error;\n }\n\n if (response.status === 401 || response.status === 403) {\n // THE SERVER'S SENTENCE IS THE ONE A PERSON READS. aidream answers every\n // refusal with `{ message, remedy }` (\"The host did not admit you…\", \"A host\n // removed you…\"); the fallback only covers a body that says nothing.\n const said = await serverSentence(response);\n throw new MeetError(\n \"forbidden\",\n said?.message ?? \"You can't join this meeting.\",\n said?.remedy ??\n \"Ask the person who shared the link to let you in, then open the link again.\",\n undefined,\n said?.reason ?? null,\n said?.retryAfterSeconds ?? null,\n );\n }\n if (response.status === 404) {\n throw new MeetError(\n \"not-found\",\n \"meet.token: no such meeting\",\n \"The link is wrong or the meeting was deleted. Show the 'meeting not found' screen; \" +\n \"a durable link that 404s is never a retry.\",\n undefined,\n \"not_found\" satisfies JoinRefusalReason,\n );\n }\n if (response.status === 410) {\n // Meet wave 1: a CANCELLED meeting. The link resolves; the room never opens.\n // The server's own sentence (with the host's reason) is the message.\n const said = await serverSentence(response);\n throw new MeetError(\n \"cancelled\",\n said?.message ?? \"This meeting was cancelled.\",\n \"Nobody can join a cancelled meeting. Show the cancelled screen with this sentence; \" +\n \"ask the host to schedule a new one.\",\n undefined,\n said?.reason ?? (\"cancelled\" satisfies JoinRefusalReason),\n );\n }\n if (response.status === 423) {\n const said = await serverSentence(response);\n throw new MeetError(\n \"room-locked\",\n said?.message ?? \"The meeting is locked.\",\n said?.remedy ??\n \"The host locked the room. Ask them to unlock it, then join again.\",\n undefined,\n said?.reason ?? (\"locked\" satisfies JoinRefusalReason),\n );\n }\n if (response.status === 503) {\n throw new MeetError(\n \"misconfigured\",\n \"meet.token: the meeting service is not configured\",\n \"aidream is missing LIVEKIT_URL / LIVEKIT_API_KEY / LIVEKIT_API_SECRET. That is a \" +\n \"server deployment fix; the client cannot and must not supply them.\",\n );\n }\n if (!response.ok) {\n throw new MeetError(\n \"token\",\n `meet.token: the server returned ${response.status}`,\n \"Transient. The package retries the join once; if it persists check aidream's meet \" +\n \"router logs for the room name in question.\",\n );\n }\n\n return parseRoomToken(await response.json(), input.roomName);\n }\n\n return {\n get(input) {\n const entry = cache.get(input.roomName);\n // A pass for the main room is not a pass for a breakout room, and back.\n const sameRoom =\n entry !== undefined &&\n (entry.token.breakoutRoomId ?? null) === (input.breakoutRoomId ?? null) &&\n // An observer's pass is not a participant's, and back (MD-16).\n (entry.token.role === \"viewer\") === (input.observe === true);\n if (entry !== undefined && sameRoom) {\n if (entry.inFlight !== null) return entry.inFlight;\n if (!expired(entry)) return Promise.resolve(entry.token);\n }\n // SINGLE FLIGHT. Every concurrent caller shares this promise, so a strict\n // mode double-mount cannot mint two tokens for one identity.\n const pending = request(input, true)\n .then((token) => {\n if (token.secondDeviceChoice != null) {\n // A QUESTION, NOT A CREDENTIAL (S7): never cached, never remembered.\n cache.delete(input.roomName);\n return token;\n }\n cache.set(input.roomName, { token, mintedAt: now(), inFlight: null });\n // MRF-1: kept HERE, at the one place a pass is ever minted, so no\n // future caller can acquire one without it being remembered.\n remember(token);\n return token;\n })\n .catch((error: unknown) => {\n // A failure is NEVER cached — one wobble would otherwise leave the\n // room unjoinable until a reload.\n cache.delete(input.roomName);\n throw error;\n });\n cache.set(input.roomName, {\n token: entry?.token ?? placeholder(input.roomName),\n mintedAt: entry?.mintedAt ?? now(),\n inFlight: pending,\n });\n return pending;\n },\n invalidate(roomName) {\n cache.delete(roomName);\n // 🚨 THE STORED PASS SURVIVES `invalidate` ON PURPOSE. This is called on\n // leave and after a refused mint, and a guest leaving a meeting is\n // exactly the person who comes back to read its summary. What `get`\n // refuses to REUSE and what the record read may PRESENT are two different\n // questions; clearing the pass here would answer the second one wrong.\n },\n needsRefresh(roomName) {\n const entry = cache.get(roomName);\n return entry === undefined || expired(entry);\n },\n remembered(roomName) {\n // The in-flight `placeholder()` occupies the cache slot with an EMPTY\n // token string and is never handed out — including here, where handing it\n // out would present \"\" as a credential and earn a 403 that means nothing.\n const live = cache.get(roomName)?.token;\n if (live !== undefined && live.token.length > 0) return live;\n return recall(roomName);\n },\n };\n}\n\n/** Never handed out: it only occupies the slot while a mint is in flight. */\nfunction placeholder(roomName: RoomName): RoomToken {\n return {\n token: \"\",\n serverUrl: \"\",\n roomName,\n identity: \"\" as ParticipantIdentity,\n connectionIdentity: \"\",\n connectionKind: \"main\",\n secondDeviceChoice: null,\n displayName: \"\",\n expiresAt: new Date(0).toISOString(),\n canPublish: false,\n canSubscribe: false,\n canRecord: false,\n isHost: false,\n role: \"participant\",\n standing: null,\n meetingPass: null,\n meetingPassExpiresAt: null,\n sessionId: null,\n lobby: false,\n breakoutRoomId: null,\n };\n}\n\n/**\n * The `{ message, remedy }` aidream puts on a refusal (`MeetError` →\n * `{ error, message, user_message, remedy }`, or FastAPI's `{ detail: {…} }`),\n * or null when the body carries no sentence.\n */\nasync function serverSentence(\n response: Response,\n): Promise<{\n message: string;\n remedy: string | undefined;\n reason: string | null;\n retryAfterSeconds: number | null;\n} | null> {\n try {\n const body = (await response.json()) as Record<string, unknown> | null;\n const source =\n body !== null && typeof body.detail === \"object\" && body.detail !== null\n ? (body.detail as Record<string, unknown>)\n : body;\n if (source === null) return null;\n const message =\n typeof source.user_message === \"string\" && source.user_message.trim() !== \"\"\n ? source.user_message\n : typeof source.message === \"string\" && source.message.trim() !== \"\"\n ? source.message\n : null;\n if (message === null) return null;\n const remedy =\n typeof source.remedy === \"string\" && source.remedy.trim() !== \"\" ? source.remedy : undefined;\n const reason = typeof source.reason === \"string\" && source.reason !== \"\" ? source.reason : null;\n const retry = source.retry_after_seconds;\n const retryAfterSeconds = typeof retry === \"number\" && Number.isFinite(retry) && retry > 0 ? Math.ceil(retry) : null;\n return { message, remedy, reason, retryAfterSeconds };\n } catch {\n return null;\n }\n}\n","/**\n * THE NOTE-TAKER, AS A SURFACE MUST RENDER IT.\n *\n * 🚨 **THE PACKAGE NEVER GUESSES WHAT THE AGENT IS DOING.** Two facts decide\n * everything here and both come from outside this client: the meeting row's\n * `metadata.note_taker` (written by the server with a CAS, MRI-A2/D9) and\n * whether the agent participant is actually in the LiveKit room. A spinner —\n * \"starting…\" held open by a client-side timer — is the exact lie this file\n * exists to prevent: it survives a worker that never came, a dispatch nobody\n * claimed, and a job that died on a release.\n *\n * 🚨 **A REFUSAL IS RENDERED VERBATIM.** `/agent/start` answers a 503 whose body\n * carries `reason` and `remedy` written by the readiness gate (D7) — \"no worker\n * is registered under matrx-note-taker\", \"every worker is at capacity\",\n * \"the fleet is draining\". Those sentences are the truth about the system and\n * they reach the screen unedited. This package adds no sentence of its own on\n * top of one the server already wrote.\n *\n * The agent's identity is DETERMINISTIC — `agent:note_taker:<meeting_id>` (D9) —\n * which is what makes \"is it in the room\" answerable without a round trip, and\n * what makes a second note-taker impossible to mistake for the first.\n */\n\nimport type { JsonObject, MeetParticipant, MeetingRecord, NoteTakerState } from \"./types\";\n\n/** The one spelling of the agent's participant identity (D9). */\nexport const NOTE_TAKER_IDENTITY_PREFIX = \"agent:note_taker:\";\n\nexport function noteTakerIdentity(meetingId: string): string {\n return `${NOTE_TAKER_IDENTITY_PREFIX}${meetingId}`;\n}\n\n/**\n * The participant attribute the note-taker sets on itself so EVERY client —\n * including one that cannot read the token metadata — knows a notice is owed\n * (D10).\n */\nexport const CONSENT_NOTICE_ATTRIBUTE = \"matrx.agent.consent_notice\";\n\n/** Where the state lives on the meeting row. One spelling, one file. */\nexport const NOTE_TAKER_METADATA_KEY = \"note_taker\";\n\nconst STATES: readonly NoteTakerState[] = [\n \"absent\",\n \"joining\",\n \"transcribing\",\n \"handoff_pending\",\n \"ended\",\n];\n\n/** The server's refusal, as it wrote it. Never paraphrased. */\nexport interface NoteTakerRefusal {\n readonly reason: string;\n readonly remedy: string;\n readonly status: number;\n}\n\nexport interface NoteTakerStatus {\n readonly state: NoteTakerState;\n /** The server's own words for why it is in this state, when it gave any. */\n readonly detail: string | null;\n /** True when the agent participant is in the room RIGHT NOW. */\n readonly present: boolean;\n /** The last dispatch refusal, rendered verbatim. */\n readonly refusal: NoteTakerRefusal | null;\n}\n\nfunction readState(value: unknown): NoteTakerState | null {\n // The server's `dispatching` (summoned, not yet in the room) reads as `joining` on screen.\n if (value === \"dispatching\") return \"joining\";\n return typeof value === \"string\" && STATES.includes(value as NoteTakerState)\n ? (value as NoteTakerState)\n : null;\n}\n\n/** Is this participant the note-taker (or any agent that owes a notice, D10)? */\nexport function isNoteTaker(participant: MeetParticipant): boolean {\n return (\n participant.identity.startsWith(NOTE_TAKER_IDENTITY_PREFIX) ||\n participant.attributes[CONSENT_NOTICE_ATTRIBUTE] !== undefined ||\n participant.isAgent\n );\n}\n\n/** The agent participant in the room, if any. */\nexport function noteTakerParticipant(\n participants: readonly MeetParticipant[],\n): MeetParticipant | null {\n return participants.find(isNoteTaker) ?? null;\n}\n\n/** The exact deterministic actor whose room presence can corroborate the row. */\nfunction exactNoteTakerParticipant(\n meeting: MeetingRecord | null,\n participants: readonly MeetParticipant[],\n): MeetParticipant | null {\n if (meeting === null) return null;\n const identity = noteTakerIdentity(meeting.id);\n return participants.find((participant) => participant.identity === identity) ?? null;\n}\n\n/**\n * THE STATE, from the two facts that decide it.\n *\n * The durable row is the authority for work. Exact room presence corroborates\n * only a durable `transcribing` state; an arbitrary agent, a consent marker,\n * or a stale note-taker participant never proves speech capture.\n */\nexport function noteTakerStatus(args: {\n meeting: MeetingRecord | null;\n participants: readonly MeetParticipant[];\n refusal?: NoteTakerRefusal | null;\n}): NoteTakerStatus {\n const present = exactNoteTakerParticipant(args.meeting, args.participants) !== null;\n const raw = args.meeting?.metadata?.[NOTE_TAKER_METADATA_KEY];\n const row: JsonObject =\n typeof raw === \"object\" && raw !== null && !Array.isArray(raw) ? raw : {};\n const recorded = readState(row[\"state\"]);\n const detail = typeof row[\"reason\"] === \"string\" && row[\"reason\"].length > 0\n ? row[\"reason\"]\n : null;\n const refusal = args.refusal ?? null;\n\n if (recorded === null) {\n return { state: \"absent\", detail, present, refusal };\n }\n if (recorded === \"transcribing\" && !present) {\n return { state: \"handoff_pending\", detail, present, refusal };\n }\n return { state: recorded, detail, present, refusal };\n}\n\n/**\n * THE SENTENCE FOR EACH STATE. One place, so the room, the panel and any future\n * surface say the same true thing — and so a state can never render as a bare\n * enum name.\n */\nexport function noteTakerLabel(status: NoteTakerStatus): string {\n switch (status.state) {\n case \"joining\":\n return \"The note-taker is joining this meeting\";\n case \"transcribing\":\n return \"The note-taker is connected and listening for speech\";\n case \"handoff_pending\":\n return \"The note-taker's transcription is interrupted; reconnection is pending\";\n case \"ended\":\n return \"The note-taker has stopped\";\n case \"absent\":\n default:\n return status.present\n ? \"A meeting assistant is connected. Transcription is not confirmed\"\n : \"No note-taker is in this meeting\";\n }\n}\n","/**\n * CAPTIONS ARE HINTS; SEGMENTS ARE TRUTH (D16).\n *\n * Two independent streams describe the same speech and they arrive out of order\n * with different identities:\n *\n * 1. **Captions** ride the LiveKit `lk.transcription` text stream, keyed by\n * `lk.segment_id`, interim-then-final, milliseconds behind the voice. They\n * exist so a person can READ the room live. Their `sender_identity` is\n * supplied by the publishing client and LiveKit does not validate it, so\n * nothing downstream — notes, Q&A, the wrap-up — may ever read them.\n * 2. **Records** are `communication.meet_transcript_segments` rows, written\n * server-side by the note-taker with a deterministic id (D6) and delivered\n * here by Postgres Changes. They are the meeting's transcript.\n *\n * The reconciliation below is what keeps the screen from saying everything\n * twice. A record REPLACES the caption it supersedes — matched by the caption\n * id the worker carries into the row, and failing that by speaker + text — and\n * a caption never overwrites a record, however late it lands.\n *\n * Everything here is a pure function over a list so the rules are provable\n * without a store, a socket, or a browser.\n */\n\nimport type { ParticipantIdentity, TranscriptSegment } from \"./types\";\n\n/** Comparable text: case, punctuation and spacing are not differences. */\nexport function normalizeSpoken(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, \" \")\n .trim();\n}\n\nfunction supersedes(record: TranscriptSegment, caption: TranscriptSegment): boolean {\n if (caption.source !== \"caption\") return false;\n if (caption.identity !== record.identity) return false;\n if (record.captionId !== null && caption.captionId === record.captionId) return true;\n const left = normalizeSpoken(record.text);\n const right = normalizeSpoken(caption.text);\n if (left.length === 0 || right.length === 0) return false;\n // An interim caption is a PREFIX of what was finally said; a final caption and\n // its record usually differ only in punctuation. Both are the same utterance.\n return left === right || left.startsWith(right) || right.startsWith(left);\n}\n\n/**\n * A caption arrived. Interim replaces interim in place; a final never loses to a\n * late interim; a record is never touched.\n */\nexport function mergeCaption(\n list: readonly TranscriptSegment[],\n caption: TranscriptSegment,\n): readonly TranscriptSegment[] {\n const index = list.findIndex(\n (entry) => entry.captionId !== null && entry.captionId === caption.captionId,\n );\n if (index === -1) {\n // The record for this utterance may already be here (the row can beat the\n // caption on a slow media path). Do not draw the same sentence twice.\n if (list.some((entry) => entry.source === \"record\" && supersedes(entry, caption))) {\n return list;\n }\n return [...list, caption];\n }\n const existing = list[index];\n if (existing === undefined) return list;\n // TRUTH WINS. A caption cannot revert a durable row, and a late interim cannot\n // revert a final — that is a finished sentence visibly becoming a half-word.\n if (existing.source === \"record\") return list;\n if (existing.final && !caption.final) return list;\n return list.map((entry, position) => (position === index ? caption : entry));\n}\n\n/**\n * A durable row arrived. It replaces the caption it supersedes — in place, so\n * the line does not jump down the panel — or lands as a new line if no caption\n * ever showed it (a surface that joined late, or captions that never came).\n */\nexport function mergeRecord(\n list: readonly TranscriptSegment[],\n record: TranscriptSegment,\n): readonly TranscriptSegment[] {\n const byId = list.findIndex((entry) => entry.source === \"record\" && entry.id === record.id);\n if (byId !== -1) {\n const existing = list[byId];\n if (existing !== undefined && existing.text === record.text && existing.final === record.final) {\n return list;\n }\n return list.map((entry, position) => (position === byId ? record : entry));\n }\n const captionIndex = list.findIndex((entry) => supersedes(record, entry));\n if (captionIndex === -1) return [...list, record];\n return list.map((entry, position) => (position === captionIndex ? record : entry));\n}\n\n/** The transcript a person reads: chronological, and never an empty line. */\nexport function readableTranscript(\n list: readonly TranscriptSegment[],\n): readonly TranscriptSegment[] {\n return [...list]\n .filter((entry) => entry.text.trim().length > 0)\n .sort((left, right) => left.startedAt.localeCompare(right.startedAt));\n}\n\n/** Build a caption line from the media plane. */\nexport function captionSegment(args: {\n captionId: string;\n meetingId: TranscriptSegment[\"meetingId\"];\n identity: ParticipantIdentity;\n speaker: string;\n text: string;\n final: boolean;\n startedAt: string;\n}): TranscriptSegment {\n return {\n id: `caption:${args.captionId}`,\n meetingId: args.meetingId,\n identity: args.identity,\n speaker: args.speaker,\n text: args.text,\n startedAt: args.startedAt,\n final: args.final,\n source: \"caption\",\n captionId: args.captionId,\n };\n}\n","/**\n * THE MEET CHANNEL NAMESPACES.\n *\n * Zero hand-rolled `.channel(` in this package (the realtime doctrine). Every\n * topic is declared through `@ai-matrx/realtime`'s registry, which refuses a\n * colliding re-declaration.\n *\n * THREE channels, each because it has a different AUDIENCE:\n *\n * - `meet-user` (per user): where a call REACHES you — the ring, the cancel,\n * the \"answered on another device\". You are subscribed to this everywhere the\n * provider is mounted, whether or not you are in a meeting.\n * - `meet-room` (per room): the pre-media room — who is knocking, who was\n * admitted, whether recording started. It carries the moments that happen\n * BEFORE and AFTER media, which LiveKit's own data plane cannot: a guest in\n * the lobby is not in the LiveKit room yet, by construction (D6).\n * - `meet-meeting` (per meeting): the durable record's feed — transcript\n * segments, notes, the recording row going `available` — so a surface that is\n * NOT in the call (a meeting page, an admin console) stays live.\n *\n * 🚨 The in-MEDIA control plane (hand raise, reactions, in-room chat) rides\n * LiveKit data messages, NOT these channels — see `DATA_TOPICS`. Splitting them\n * this way is deliberate: anything that must reach a guest with no Supabase\n * session travels on the media plane, where their room token is the authority.\n */\n\nimport { defineChannelNamespace, type ChannelNamespace } from \"@ai-matrx/realtime\";\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\nimport type { MeetingId, RoomName, UserId } from \"./types\";\n\ninterface Namespaces {\n user: ChannelNamespace;\n room: ChannelNamespace;\n meeting: ChannelNamespace;\n}\n\nfunction namespaces(): Namespaces {\n // `defineChannelNamespace` throws on a conflicting re-declaration, so these\n // are created lazily through a `globalThis` slot: with dual ESM/CJS output\n // the module can be evaluated twice in one process, and a second identical\n // declaration must be a no-op rather than a crash.\n return globalSlot<Namespaces>(\"ai-matrx.meet.channel-namespaces\", () => ({\n user: defineChannelNamespace({\n namespace: \"meet-user\",\n parts: [\"userId\"],\n description:\n \"One user's calling presence: incoming rings, cancellations, and the \" +\n \"answered-elsewhere signal that stops every other device ringing.\",\n }),\n room: defineChannelNamespace({\n namespace: \"meet-room\",\n parts: [\"roomName\"],\n description:\n \"One media room's out-of-band plane: lobby knocks and admissions, lock \" +\n \"state, and recording state — the moments that happen before a guest is \" +\n \"in the LiveKit room at all.\",\n }),\n meeting: defineChannelNamespace({\n namespace: \"meet-meeting\",\n parts: [\"meetingId\"],\n description:\n \"One meeting record's live feed: transcript segments, AI notes, and the \" +\n \"recording row settling — for surfaces that are not in the call.\",\n }),\n }));\n}\n\nexport function userTopic(userId: UserId): string {\n return namespaces().user.topic({ userId });\n}\n\nexport function roomTopic(roomName: RoomName): string {\n return namespaces().room.topic({ roomName });\n}\n\nexport function meetingTopic(meetingId: MeetingId): string {\n return namespaces().meeting.topic({ meetingId });\n}\n\n/** Broadcast event names. One place, so a sender and a receiver cannot drift. */\nexport const MEET_EVENTS = {\n /** A call is ringing you. Carries the whole invite; no follow-up read. */\n ring: \"mx.call.ring\",\n /** The caller hung up before you answered. */\n cancel: \"mx.call.cancel\",\n /** You answered (or declined) on another device. Stop ringing here. */\n settled: \"mx.call.settled\",\n /**\n * 🚨 S4 (CORE-DESIGN §2.6): the ONE room-channel event — payload-free \"something changed,\n * read again\". Nothing a person must see rides it: a waiting person learns admitted /\n * denied / expired / ended by READING their row (`GET /v1/meet/state`) on a timer and on\n * every hint. The four payload broadcasts (`mx.lobby.knock`, `mx.lobby.admission`,\n * `mx.room.lock`, `mx.room.recording`) are gone with their senders and receivers. The\n * server publishes the same event on the same topic (aidream `admission.room_topic`).\n */\n stateChanged: \"mx.state.changed\",\n} as const;\n\n/**\n * 🚨 `mx.meeting.transcript` and `mx.meeting.note` WERE declared here and\n * emitted and subscribed by nothing — a broadcast fallback that existed only as\n * a name (MRI-A10 found it; feedback `b25db4d4`). They are gone. The thing they\n * described is real now and lives somewhere better: the platform publishes the\n * durable rows into the LIVEKIT ROOM under `matrx.meet.*` (`core/room-events.ts`,\n * MRI-A12), where a session-less guest's room token is the authority. Sending\n * them over a Supabase broadcast channel would have required the guest to hold a\n * Supabase connection, which is the exact thing they do not have.\n */\n\nexport type MeetEvent = (typeof MEET_EVENTS)[keyof typeof MEET_EVENTS];\n","/**\n * THE JOIN GATE'S REASON CODES — ONE VOCABULARY (CORE-DESIGN §2.5).\n *\n * aidream's `services/meet/errors.py` (`JOIN_REFUSAL_REASONS`) emits exactly these codes and a\n * server test fails if the two lists differ; the consuming app decides every refusal screen by\n * one of them — never by the HTTP status and never by matching message text. Skins own the\n * words; the phase renders `refused:<reason>`, except `ended`, which renders the `ended` phase.\n */\nexport const JOIN_REFUSAL_REASONS = [\n \"not_found\",\n \"cancelled\",\n \"ended\",\n \"expired\",\n \"too_early\",\n \"sign_in_required\",\n \"wrong_account\",\n \"not_invited\",\n \"locked\",\n \"removed\",\n \"lobby_rejects\",\n \"meeting_full\",\n \"platform_capacity\",\n \"verification_required\",\n \"registration_required\",\n] as const;\n\nexport type JoinRefusalReason = (typeof JOIN_REFUSAL_REASONS)[number];\n\n/** Is `value` one of the codes the join gate speaks? */\nexport function isJoinRefusalReason(value: unknown): value is JoinRefusalReason {\n return typeof value === \"string\" && (JOIN_REFUSAL_REASONS as readonly string[]).includes(value);\n}\n","/**\n * THE PHASE MACHINE — the one place a meeting tab's top-level phase changes (CORE-DESIGN §3.1,\n * step S6a).\n *\n * The values ARE the observation contract's `data-meet-phase` column (HARNESS-CONTRACT §2), so\n * the core renders its own state with no translation layer. Every change goes through\n * `nextPhase(from, event)`: an explicit table of which event may move which phase where. An\n * event the table does not allow from the current phase is REFUSED (the store keeps the phase\n * and reports it) — never applied.\n *\n * 🚨 THE INVARIANT (guard: `phase.test.ts`, `in-call-phase-guard.test.ts`): nothing enters\n * `prejoin` except `resolving` (the link resolved) or the person's own click. In-call outcomes —\n * share, devices, recording, captions, chat, collab, moderation — live in REGIONS and have no\n * event here at all, so the MD-01 class (\"a cancelled share picker threw a live meeting back to\n * Ready to join?\") cannot recur: from `in_call` the only ways out are the exit-reason\n * resolution (§3.4), the person's own Leave, and the meeting ending.\n *\n * 1:1 calls run on this same machine (§3.6): ringing is a pre-room phase set, and an answered\n * call goes `joining` → `in_call` exactly like a meeting.\n */\n\nimport { isJoinRefusalReason, type JoinRefusalReason } from \"./reasons\";\nimport type { AdmissionState } from \"./types\";\n\n/** Every gate reason except `ended`, which renders the `ended` phase (contract §2). */\nexport type RefusedReason = Exclude<JoinRefusalReason, \"ended\">;\n\n/** Where the person is (HARNESS-CONTRACT §2, verbatim). */\nexport type MeetPhase =\n | \"resolving\"\n | \"unsupported\"\n | `refused:${RefusedReason}`\n | \"guest_name\"\n | \"prejoin\"\n | \"joining\"\n | \"waiting:host_not_started\"\n | \"waiting:knocking\"\n | \"denied\"\n | \"knock_expired\"\n | \"in_call\"\n | \"disconnected\"\n | \"left\"\n | \"removed\"\n | \"superseded\"\n | \"inactive\"\n | \"ended\"\n | \"record\"\n | \"ringing_out\"\n | \"ringing_in\"\n | \"call_declined\"\n | \"call_missed\"\n | \"call_busy\"\n | \"answered_elsewhere\";\n\n/** The phases a §3.4 exit-reason resolution can land on. */\nexport type ExitPhase = \"removed\" | \"superseded\" | \"inactive\" | \"ended\" | \"disconnected\" | \"waiting:knocking\";\n\n/** How a 1:1 call ended before it connected (§3.6). */\nexport type CallOutcomePhase = \"call_declined\" | \"call_missed\" | \"call_busy\" | \"answered_elsewhere\";\n\n/**\n * Every event that can move the phase. `click: true` marks the ones only the person's own\n * press may send — the guard test reads it.\n */\nexport type PhaseEvent =\n /** The link resolved on page load / mount: `resolving` → `prejoin` (first entry). */\n | { readonly type: \"resolved\" }\n /** Capability gate failed: this browser cannot join. */\n | { readonly type: \"unsupported\" }\n /**\n * The link's door said, BEFORE any Join, that this run cannot be joined (`ended`, `cancelled`):\n * `resolving` → that screen, never the pre-join with a Join button that will be refused.\n */\n | { readonly type: \"link_state\"; readonly reason: string | null }\n /** The person pressed Join / Rejoin / Ask again. */\n | { readonly type: \"join\"; readonly click: true }\n /**\n * S6b: the connection supervisor rejoins by itself from `disconnected` once the network and our\n * servers answer (`auto_rejoin`). Not a click — and it can only ever lead to `joining`.\n */\n | { readonly type: \"auto_rejoin\" }\n /**\n * S6b: the page reloaded while this tab was in (or dropped from) the call — Chrome's offline\n * page reloading, a refresh — so `resolving` goes straight back to `joining`, never pre-join.\n */\n | { readonly type: \"resumed\" }\n /** The person chose to go back to the device check from `left` / `disconnected`. */\n | { readonly type: \"back_to_prejoin\"; readonly click: true }\n /** The person pressed Try again on a refusal: look the link up again. */\n | { readonly type: \"retry\"; readonly click: true }\n /** The server said wait (or decided a wait): the admission row's own word. */\n | { readonly type: \"admission\"; readonly admission: AdmissionState }\n /** Admitted from the waiting room: the core mints the real pass and joins. */\n | { readonly type: \"admitted\" }\n /** LiveKit said connected. */\n | { readonly type: \"connected\" }\n /**\n * S7: the server answered the join with a QUESTION — this person is already in the meeting on\n * another tab or device — so `joining` goes back to `prejoin`, which offers the choice.\n */\n | { readonly type: \"second_device\" }\n /** The join itself failed: a gate reason (→ `refused:*` / `ended`) or none (→ `disconnected`). */\n | { readonly type: \"join_failed\"; readonly reason: string | null }\n /** §3.4: the exit-reason resolution decided where an in-call person went. */\n | { readonly type: \"exit\"; readonly to: ExitPhase }\n /** My Leave (in the room, joining, or withdrawing from the waiting room). */\n | { readonly type: \"leave\" }\n /** The meeting (or this run of it) is over, by any carrier. */\n | { readonly type: \"ended\" }\n /** §3.6: I placed a call / a call rings me while nothing else is on screen. */\n | { readonly type: \"ring_out\" }\n | { readonly type: \"ring_in\" }\n /** §3.6: an answered call (by me, or my callee) — join its room. */\n | { readonly type: \"call_answered\" }\n /** §3.6: the call ended before it connected. */\n | { readonly type: \"call_outcome\"; readonly outcome: CallOutcomePhase }\n /**\n * Back to the start: a different meeting mounted or the call surface was dismissed (refused\n * from a live phase — leave first). `teardown: true` is the engine's own dispose: it has\n * already closed the connection, so nothing live remains to protect and the reset applies from\n * any phase.\n */\n | { readonly type: \"reset\"; readonly teardown?: true };\n\nexport type PhaseEventType = PhaseEvent[\"type\"];\n\n/** Phases in which this tab holds (or is getting) a live media connection. */\nexport const LIVE_PHASES: ReadonlySet<MeetPhase> = new Set<MeetPhase>([\"joining\", \"in_call\"]);\n\n/** The waiting-room phase set (the core reads the server's state while in one of these). */\nexport const WAITING_PHASES: ReadonlySet<MeetPhase> = new Set<MeetPhase>([\n \"waiting:host_not_started\",\n \"waiting:knocking\",\n \"denied\",\n \"knock_expired\",\n]);\n\n/** A phase the person can press Join / Rejoin from. */\nconst JOINABLE_FROM: ReadonlySet<MeetPhase> = new Set<MeetPhase>([\n \"prejoin\",\n \"left\",\n \"disconnected\",\n \"superseded\",\n \"inactive\",\n \"denied\",\n \"knock_expired\",\n // A refused knock whose rule lets the person ask again (a cooldown that ran out): the machine\n // allows the press; the server's gate decides whether it knocks or is refused again.\n \"refused:lobby_rejects\",\n]);\n\n/**\n * Refusals that lift by themselves or by a host's act (the lock opens, the window opens, a seat\n * frees): Try again re-asks the gate with the same request. Every other refusal stays a refusal.\n */\nexport const RETRYABLE_REFUSALS: ReadonlySet<MeetPhase> = new Set<MeetPhase>([\n \"refused:locked\",\n \"refused:too_early\",\n \"refused:meeting_full\",\n \"refused:platform_capacity\",\n]);\n\n/** A call that never connected has no meeting to end; an ended meeting stays ended. */\nconst NOT_ENDABLE: ReadonlySet<MeetPhase> = new Set<MeetPhase>([\n \"ended\",\n \"record\",\n \"unsupported\",\n \"ringing_out\",\n \"ringing_in\",\n \"call_declined\",\n \"call_missed\",\n \"call_busy\",\n \"answered_elsewhere\",\n]);\n\nconst RINGING: ReadonlySet<MeetPhase> = new Set<MeetPhase>([\"ringing_out\", \"ringing_in\"]);\n\n/** Nothing live, nothing pending: a call may take the screen from here. */\nexport const IDLE_PHASES: ReadonlySet<MeetPhase> = new Set<MeetPhase>([\n \"resolving\",\n \"left\",\n \"call_declined\",\n \"call_missed\",\n \"call_busy\",\n \"answered_elsewhere\",\n]);\n\nexport function isLivePhase(phase: MeetPhase): boolean {\n return LIVE_PHASES.has(phase);\n}\n\nexport function isWaitingPhase(phase: MeetPhase): boolean {\n return WAITING_PHASES.has(phase);\n}\n\nexport function isRefusedPhase(phase: MeetPhase): phase is `refused:${RefusedReason}` {\n return phase.startsWith(\"refused:\");\n}\n\n/** The reason a refused / terminal phase carries (`data-meet-reason`). */\nexport function reasonOf(phase: MeetPhase): string | null {\n return isRefusedPhase(phase) ? phase.slice(\"refused:\".length) : null;\n}\n\n/** A gate reason → the phase it renders. `ended` is the ended phase, never `refused:ended`. */\nexport function refusalPhase(reason: string | null): MeetPhase {\n if (reason === null || !isJoinRefusalReason(reason)) return \"disconnected\";\n return reason === \"ended\" ? \"ended\" : (`refused:${reason}` as const);\n}\n\n/** The server's admission word → the waiting phase it renders (§3.1, S4). */\nexport function admissionPhase(admission: AdmissionState): MeetPhase | null {\n switch (admission) {\n case \"waiting_host\":\n return \"waiting:host_not_started\";\n case \"knocking\":\n return \"waiting:knocking\";\n case \"denied\":\n return \"denied\";\n case \"expired\":\n return \"knock_expired\";\n case \"withdrawn\":\n return \"left\";\n case \"ended\":\n return \"ended\";\n case \"removed\":\n return \"refused:removed\";\n default:\n // `admitted` is never a waiting phase: it is a join (`admitted` event).\n return null;\n }\n}\n\n/**\n * THE TABLE. Returns the next phase, or null when `event` may not move `from` (refused).\n * Pure — the store applies it, and the guard tests enumerate it.\n */\nexport function nextPhase(from: MeetPhase, event: PhaseEvent): MeetPhase | null {\n switch (event.type) {\n case \"resolved\":\n return from === \"resolving\" ? \"prejoin\" : null;\n case \"unsupported\":\n return from === \"resolving\" ? \"unsupported\" : null;\n case \"link_state\":\n return from === \"resolving\" && event.reason !== null && isJoinRefusalReason(event.reason)\n ? refusalPhase(event.reason)\n : null;\n case \"join\":\n // A refusal's Try again re-asks the gate with the same request (CORE-DESIGN §11\n // `meeting-locked-join`, `before-start-early-entry`): the server decides again.\n return JOINABLE_FROM.has(from) || RETRYABLE_REFUSALS.has(from) ? \"joining\" : null;\n case \"auto_rejoin\":\n return from === \"disconnected\" ? \"joining\" : null;\n case \"resumed\":\n return from === \"resolving\" ? \"joining\" : null;\n case \"back_to_prejoin\":\n return from === \"left\" || from === \"disconnected\" ? \"prejoin\" : null;\n case \"retry\":\n return isRefusedPhase(from) || from === \"unsupported\" ? \"resolving\" : null;\n case \"admission\": {\n // The server's word on a wait applies while joining or waiting — never to a live call\n // (being sent back to waiting from the room is an EXIT, resolved by §3.4).\n if (from !== \"joining\" && !WAITING_PHASES.has(from)) return null;\n return admissionPhase(event.admission);\n }\n case \"admitted\":\n return WAITING_PHASES.has(from) ? \"joining\" : null;\n case \"connected\":\n return from === \"joining\" ? \"in_call\" : null;\n case \"second_device\":\n return from === \"joining\" ? \"prejoin\" : null;\n case \"join_failed\":\n // ONLY a join can fail the join. A failure reported while in the room is not this event.\n return from === \"joining\" ? refusalPhase(event.reason) : null;\n case \"exit\":\n return from === \"in_call\" || from === \"joining\" ? event.to : null;\n case \"leave\":\n return from === \"in_call\" || from === \"joining\" || WAITING_PHASES.has(from) ? \"left\" : null;\n case \"ended\":\n return NOT_ENDABLE.has(from) ? null : \"ended\";\n case \"ring_out\":\n return IDLE_PHASES.has(from) || from === \"prejoin\" ? \"ringing_out\" : null;\n case \"ring_in\":\n return IDLE_PHASES.has(from) ? \"ringing_in\" : null;\n case \"call_answered\":\n return RINGING.has(from) ? \"joining\" : null;\n case \"call_outcome\":\n return RINGING.has(from) ? event.outcome : null;\n case \"reset\":\n // Never out from under a live connection: leave first.\n return LIVE_PHASES.has(from) && event.teardown !== true ? null : \"resolving\";\n }\n}\n\n/* ------------------------------------------------------- §3.4 exit-reason resolution (ER) */\n\n/** What the server's state read (`GET /v1/meet/state?connection=`) says about this exit. */\nexport interface ExitServerWord {\n /** `meet_connections.exit_reason` of THIS connection, if the server recorded one. */\n readonly exitReason: string | null;\n /** The run's phase: `ended` means the meeting (this run) is over. */\n readonly sessionPhase: string | null;\n readonly meetingPhase?: string | null;\n}\n\nexport interface ExitInput {\n /** The server's word; `null` = the state read failed (offline / unreachable); undefined = no ST for this room (a call). */\n readonly server: ExitServerWord | null | undefined;\n /** LiveKit's `DisconnectReason` name (`PARTICIPANT_REMOVED`, `DUPLICATE_IDENTITY`, …), or null. */\n readonly hint: string | null;\n /** The page was frozen (Page Lifecycle) when the connection dropped. */\n readonly frozen?: boolean;\n}\n\nexport interface ExitResolution {\n readonly to: ExitPhase;\n /** `data-meet-reason` for the exit (null when the phase says it all). */\n readonly reason: string | null;\n /** `data-meet-connection` to show beside `disconnected`. */\n readonly connection: \"stable\" | \"offline\" | \"server_unreachable\";\n /** A notice code the skin renders with it (`sent-to-waiting`), when one applies. */\n readonly notice: string | null;\n}\n\n/** §3.4, row by row. Pure: the engine gathers the inputs, this decides. */\nexport function resolveExit(input: ExitInput): ExitResolution {\n const { server, hint } = input;\n if (server === null) {\n return { to: \"disconnected\", reason: null, connection: \"server_unreachable\", notice: null };\n }\n const word = server?.exitReason ?? null;\n if (server !== undefined && (server.sessionPhase === \"ended\" || server.meetingPhase === \"ended\" || word === \"ended\" || word === \"time_limit\")) {\n return { to: \"ended\", reason: word === \"time_limit\" ? \"time_limit\" : \"ended\", connection: \"stable\", notice: null };\n }\n if (word === \"removed\") return { to: \"removed\", reason: \"removed\", connection: \"stable\", notice: null };\n if (word === \"displaced\") return { to: \"superseded\", reason: \"displaced\", connection: \"stable\", notice: null };\n if (word === \"sent_to_waiting\") {\n return { to: \"waiting:knocking\", reason: \"sent_to_waiting\", connection: \"stable\", notice: \"sent-to-waiting\" };\n }\n if (hint === \"DUPLICATE_IDENTITY\") return { to: \"superseded\", reason: \"displaced\", connection: \"stable\", notice: null };\n if (hint === \"PARTICIPANT_REMOVED\" && word === null && server === undefined) {\n // A call room has no state read: LiveKit's own word is all there is.\n return { to: \"removed\", reason: \"removed\", connection: \"stable\", notice: null };\n }\n if (hint === \"ROOM_DELETED\" && server === undefined) {\n return { to: \"ended\", reason: \"ended\", connection: \"stable\", notice: null };\n }\n if (input.frozen === true) return { to: \"inactive\", reason: \"inactive\", connection: \"stable\", notice: null };\n return { to: \"disconnected\", reason: null, connection: \"stable\", notice: null };\n}\n","/**\n * THE MEET DOMAIN MODEL.\n *\n * D3: an audio call, a video call, and a meeting are ONE thing — a room. An\n * audio call is a room with video off; a meeting is a room plus scheduling,\n * lobby and host metadata. That ruling is expressed here as ONE `MeetRoom`\n * identity carried by both `/calls` and `/meetings`, not as three parallel\n * hierarchies that drift.\n *\n * Identity-bearing values are BRANDED (C22). A `MeetingId` and a `RoomName` are\n * both strings and are NOT interchangeable — passing the meeting's row id where\n * LiveKit wants the room name produces a room nobody else joins, which looks\n * exactly like \"the other person's camera is broken\".\n */\n\ndeclare const brand: unique symbol;\ntype Brand<T, B extends string> = T & { readonly [brand]: B };\n\nexport type MeetingId = Brand<string, \"MeetingId\">;\nexport type CallId = Brand<string, \"CallId\">;\n/** The LiveKit room name. THE MEDIA ROOM KEY — durable for a meeting link. */\nexport type RoomName = Brand<string, \"RoomName\">;\nexport type UserId = Brand<string, \"UserId\">;\nexport type OrganizationId = Brand<string, \"OrganizationId\">;\n/** LiveKit participant identity. `user:<uuid>` or `guest:<opaque>`. */\nexport type ParticipantIdentity = Brand<string, \"ParticipantIdentity\">;\n\nexport const asMeetingId = (value: string): MeetingId => value as MeetingId;\nexport const asCallId = (value: string): CallId => value as CallId;\nexport const asRoomName = (value: string): RoomName => value as RoomName;\nexport const asUserId = (value: string): UserId => value as UserId;\nexport const asOrganizationId = (value: string): OrganizationId =>\n value as OrganizationId;\nexport const asParticipantIdentity = (value: string): ParticipantIdentity =>\n value as ParticipantIdentity;\n\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\nexport type JsonObject = { [key: string]: JsonValue };\n\n/** Audio-only or with video. A call escalates; it never becomes a new call. */\nexport type CallMode = \"audio\" | \"video\";\n\n/**\n * WHO IS IN THE ROOM, as far as authorization is concerned.\n *\n * A guest has no `userId` — by construction, not by convention. Everything that\n * requires authentication (host powers, recording, starting a call) takes a\n * `userId` and therefore cannot compile against a guest identity (D6).\n */\nexport interface MeetIdentity {\n readonly userId: UserId | null;\n readonly organizationId: OrganizationId;\n readonly displayName: string;\n readonly avatarUrl: string | null;\n /** Stable per-device value so a reconnect keeps ONE LiveKit identity. */\n readonly deviceId: string;\n}\n\nexport function isGuest(identity: MeetIdentity): boolean {\n return identity.userId === null;\n}\n\n/**\n * WHO SOMEBODY IS IN A ROOM — the SERVER's word, never this client's (MRI-A11).\n *\n * THE ROLE VOCABULARY (CORE-DESIGN §2.1, S3): `host · cohost · presenter · participant ·\n * viewer`. `host` is `meet_meetings.host_user_id`; `cohost` is a real `iam` grant on the\n * meeting. Being a guest is a STANDING (`ParticipantStanding`), never a role: a guest is a\n * `participant` whose standing is `guest`. Pinned against aidream's `config.py` by\n * `test_meet_service.py`.\n */\nexport type ParticipantRole = \"host\" | \"cohost\" | \"presenter\" | \"participant\" | \"viewer\";\n\n/**\n * Where a person stands with the meeting, beside the role: `member` of its organization,\n * `invited` (shared with them), `outsider` (signed in, followed the link), `guest` (no\n * session). The server mints it (attribute `matrx.participant.standing`).\n */\nexport type ParticipantStanding = \"member\" | \"invited\" | \"outsider\" | \"guest\";\n\n/** Rank, so \"can this actor do that to that participant\" is one comparison. */\nexport const ROLE_RANK: Readonly<Record<ParticipantRole, number>> = {\n host: 4,\n cohost: 3,\n presenter: 2,\n participant: 1,\n viewer: 0,\n};\n\n/** Does this standing follow the link-holder (guest) rules? `outsider` and `guest`. */\nexport function isLinkFollower(standing: ParticipantStanding | null | undefined): boolean {\n return standing === \"guest\" || standing === \"outsider\";\n}\n\n/**\n * 🚨 THE ONE PREDICATE FOR \"DOES THIS PERSON HOLD HOST POWERS\".\n *\n * Every host control in this package answers this question and no other. It is\n * a pure function of a SERVER-minted role, so there is no path by which a\n * client's own state can grant it anything.\n */\nexport function isHostRole(role: ParticipantRole): boolean {\n return role === \"host\" || role === \"cohost\";\n}\n\n/**\n * THE SILENT OBSERVER (HR-360 wave 2, MD-16) — the watch-only role `viewer` the core design\n * already froze: publishes nothing, has no tile on anyone's stage, and is listed in People\n * as \"Observing\". The server decides it at the join gate; nothing here grants it.\n */\nexport function isObserverRole(role: ParticipantRole | null | undefined): boolean {\n return role === \"viewer\";\n}\n\n/** Who sees the \"Observing\" line in People — knob `meet.observers_visible_to`. */\nexport type ObserversVisibleTo = \"everyone\" | \"hosts\";\n\n/**\n * THE LOBBY LADDER (D6). A guest link-follower starts `knocking`; the host\n * admits or denies. An authenticated member of the meeting's org is `admitted`\n * on arrival unless the meeting is locked.\n */\nexport type AdmissionState =\n | \"waiting_host\"\n | \"knocking\"\n | \"admitted\"\n | \"denied\"\n | \"expired\"\n | \"withdrawn\"\n | \"removed\"\n | \"ended\";\n\n/** States a person is still WAITING in (CORE-DESIGN §2.2). */\nexport const WAITING_ADMISSION_STATES: readonly AdmissionState[] = [\"waiting_host\", \"knocking\"];\n\nexport interface LobbyEntry {\n readonly identity: ParticipantIdentity;\n readonly displayName: string;\n readonly avatarUrl: string | null;\n readonly userId: UserId | null;\n readonly state: AdmissionState;\n readonly knockedAt: string;\n /** When an unanswered knock lapses (`knock_timeout_minutes`); null = never. */\n readonly knockExpiresAt?: string | null | undefined;\n readonly decidedAt: string | null;\n readonly decidedBy: UserId | null;\n /**\n * The server's flag on this knock (`mod-removed-bypass`): `previously_removed` when its coarse\n * signals match someone a host removed from this meeting. Advice for the host, never a block.\n */\n readonly flag?: string | null | undefined;\n}\n\nexport type ConnectionQuality = \"excellent\" | \"good\" | \"poor\" | \"lost\" | \"unknown\";\n\n/** One person in the room, as the UI needs them. Media state is a FACT here. */\nexport interface MeetParticipant {\n readonly identity: ParticipantIdentity;\n readonly userId: UserId | null;\n readonly displayName: string;\n readonly avatarUrl: string | null;\n readonly role: ParticipantRole;\n /** Where they stand with the meeting (server-minted); null when the server sent none. */\n readonly standing?: ParticipantStanding | null;\n /**\n * Whether the SERVER granted this participant the power to start a recording\n * (MRI-A11). It is not `role === \"host\"`: a meeting whose recording policy is\n * `always-on` is the server's to start and a host may not touch it, and a\n * `disabled` policy grants it to nobody. The client renders this; it never\n * recomputes the policy.\n */\n readonly canRecord: boolean;\n readonly isLocal: boolean;\n readonly isAgent: boolean;\n readonly micEnabled: boolean;\n readonly cameraEnabled: boolean;\n readonly screenSharing: boolean;\n readonly speaking: boolean;\n readonly handRaised: boolean;\n /**\n * When the hand went up (epoch ms), or null. The HAND QUEUE is ordered by it —\n * first raised, first called on (Meet wave 2). Optional so a roster built by a\n * host before 0.8 still type-checks.\n */\n readonly handRaisedAt?: number | null | undefined;\n readonly quality: ConnectionQuality;\n /**\n * S6b (`net-others-see-drop`): this person dropped without leaving and is held as a\n * `reconnecting` tile for `reconnect_grace_seconds`. Absent/false = a live participant.\n */\n readonly reconnecting?: boolean | undefined;\n /**\n * P1 `net-video-auto-off`: this person's camera is paused by their network (to keep their\n * audio), not by their choice — from their own heartbeat. Absent/false = not paused.\n */\n readonly videoPaused?: boolean | undefined;\n readonly joinedAt: string;\n /**\n * LiveKit participant attributes, verbatim. The note-taker announces itself\n * here (`matrx.agent.consent_notice`), which is how a consent notice can be\n * rendered for a participant whose token metadata this client cannot read.\n */\n readonly attributes: Readonly<Record<string, string>>;\n}\n\nexport type MeetingKind = \"instant\" | \"scheduled\" | \"recurring\";\n\n/**\n * WHAT THE LINK'S CURRENT RUN IS, read through the no-pass link door (`meet_link_state`) so the\n * entry screen can say it before any Join (CORE-DESIGN §2.3). One vocabulary: `reason` is a\n * join-gate reason code (`ended` · `cancelled`) or null; no private data rides it.\n */\nexport interface LinkRunState {\n readonly state: \"open\" | \"not_started\" | \"ended\" | \"cancelled\";\n readonly reason: \"ended\" | \"cancelled\" | null;\n /** `ended`: the next run's start (null = the meeting can never run again). `not_started`: when it starts. */\n readonly nextStart: string | null;\n}\n\nexport interface MeetingRecord {\n readonly id: MeetingId;\n readonly organizationId: OrganizationId;\n /** Durable: the same room name before, during, and after (R3). */\n readonly roomName: RoomName;\n /** The public slug in a meeting link. Durable for recurring meetings. */\n readonly slug: string;\n readonly title: string;\n readonly kind: MeetingKind;\n readonly hostUserId: UserId;\n readonly scheduledFor: string | null;\n readonly scheduledDurationMinutes: number | null;\n readonly startedAt: string | null;\n readonly endedAt: string | null;\n /** Present only on a row resolved through the link door; absent = unknown (never guessed). */\n readonly linkRun?: LinkRunState | undefined;\n readonly locked: boolean;\n readonly lobbyEnabled: boolean;\n readonly recordingPolicy: RecordingPolicy;\n readonly aiEnabled: boolean;\n readonly metadata: JsonObject;\n /*\n * ── MEET WAVE 1 — management (0.8). Always present on a row read from the\n * database by this package; optional in the type only so a hand-built\n * fixture from before 0.8 still compiles.\n */\n /** IANA zone the meeting's wall-clock time is authored in (`America/Los_Angeles`). */\n readonly timeZone?: string;\n /** Description / agenda text. */\n readonly agenda?: string | null;\n /** RFC 5545 RRULE subset — one row and one durable link per series. */\n readonly recurrenceRule?: string | null;\n /** Signed-in members and invitees may enter before a host arrives. */\n readonly joinBeforeHost?: boolean;\n /** Cancel is a STATE: the link still resolves and says so; nobody can join. */\n readonly cancelledAt?: string | null;\n readonly cancellationReason?: string | null;\n /** Archived (soft deleted). Restorable by the host or a co-host. */\n readonly deletedAt?: string | null;\n /** iCalendar SEQUENCE — bumped by the database on every reschedule and on cancel. */\n readonly calendarSequence?: number;\n /** Row version, for the compare-and-swap in `updateMeeting`. */\n readonly version?: number;\n}\n\n/* ─────────────────────────────── MEET WAVE 1 — management vocabulary (0.8) */\n\n/** An invitee's answer. `needs_action` = not answered yet (iCalendar NEEDS-ACTION). */\nexport type RsvpState = \"needs_action\" | \"accepted\" | \"declined\" | \"tentative\";\n/** The answers a person can give. */\nexport type RsvpAnswer = Exclude<RsvpState, \"needs_action\">;\n/** A co-host holds host moderation through a real `iam` admin grant on the meeting. */\nexport type InviteeRole = \"invitee\" | \"cohost\";\n\n/** One person a meeting was sent to (`communication.meet_invitees`). */\nexport interface MeetingInvitee {\n readonly id: string;\n readonly meetingId: MeetingId;\n /** Set when the person has an account — they skip the waiting room and answer in-app. */\n readonly userId: UserId | null;\n readonly email: string | null;\n readonly displayName: string | null;\n readonly role: InviteeRole;\n readonly rsvpState: RsvpState;\n readonly respondedAt: string | null;\n readonly rsvpNote: string | null;\n /** The calendar_sequence of the last invitation they were sent; null = never told. */\n readonly lastSentSequence: number | null;\n readonly lastSentAt: string | null;\n}\n\n/** Who to invite: an account (`userId`) or an address (`email`), or both. */\nexport interface InviteeInput {\n readonly userId?: UserId | undefined;\n readonly email?: string | undefined;\n readonly displayName?: string | undefined;\n /** Omit to keep an existing invitee's role; `cohost` needs the host or a co-host. */\n readonly role?: InviteeRole | undefined;\n}\n\n/** Where one occurrence of a meeting stands. */\nexport type OccurrenceState = \"scheduled\" | \"moved\" | \"cancelled\";\n\n/** One occurrence — for a one-off meeting, the meeting itself. */\nexport interface MeetingOccurrence {\n readonly meetingId: MeetingId;\n /** The start the rule generates (iCalendar RECURRENCE-ID). The key for exceptions. */\n readonly originalStart: string;\n /** When it actually happens (differs from `originalStart` only when `moved`). */\n readonly occurrenceStart: string;\n readonly durationMinutes: number | null;\n readonly state: OccurrenceState;\n readonly sequence: number;\n}\n\n/** One row of the Upcoming list — an occurrence plus what the list shows about its meeting. */\nexport interface UpcomingOccurrence extends MeetingOccurrence {\n readonly organizationId: OrganizationId;\n readonly title: string;\n readonly slug: string;\n readonly kind: MeetingKind;\n readonly hostUserId: UserId;\n readonly timeZone: string;\n readonly meetingCancelled: boolean;\n /** The signed-in reader's own answer, when they are an invitee. */\n readonly myRsvp: RsvpState | null;\n /** `host`, `cohost` or `invitee` — null when the reader simply can see it (org member). */\n readonly myRole: \"host\" | InviteeRole | null;\n}\n\n/** An exception row (`communication.meet_occurrence_overrides`). */\nexport interface OccurrenceOverride {\n readonly id: string;\n readonly meetingId: MeetingId;\n readonly originalStart: string;\n readonly state: \"cancelled\" | \"moved\";\n readonly newStart: string | null;\n readonly newDurationMinutes: number | null;\n readonly reason: string | null;\n readonly calendarSequence: number;\n /** Set when the exception was withdrawn (`restore`) and the occurrence is back on schedule. */\n readonly deletedAt: string | null;\n}\n\n/** Settings a meeting carries; unset ones come from the person's / organization's knobs. */\nexport interface MeetingSettings {\n readonly lobbyEnabled?: boolean | undefined;\n readonly recordingPolicy?: RecordingPolicy | undefined;\n readonly aiEnabled?: boolean | undefined;\n readonly joinBeforeHost?: boolean | undefined;\n}\n\n/** A partial edit. `null` clears a clearable field (agenda, recurrence rule, time). */\nexport interface MeetingChanges extends MeetingSettings {\n readonly title?: string | undefined;\n readonly agenda?: string | null | undefined;\n readonly timeZone?: string | undefined;\n readonly scheduledFor?: string | null | undefined;\n readonly scheduledDurationMinutes?: number | null | undefined;\n readonly recurrenceRule?: string | null | undefined;\n /**\n * When the SERIES moves (start, rule or zone), carry its per-date changes\n * (cancelled or moved occurrences) to the new schedule instead of archiving\n * them — Google Calendar's and Outlook's \"keep changes to individual events\".\n * Absent or false archives them (restorable). Ask the person; never decide.\n */\n readonly keepExceptions?: boolean | undefined;\n}\n\n/** What the emailed RSVP link shows and answers (no account needed). */\nexport type RsvpInvitation =\n | {\n readonly ok: true;\n readonly meeting: {\n readonly title: string;\n readonly slug: string;\n readonly scheduledFor: string | null;\n readonly durationMinutes: number | null;\n readonly timeZone: string;\n readonly agenda: string | null;\n readonly recurrenceRule: string | null;\n readonly cancelledAt: string | null;\n readonly cancellationReason: string | null;\n readonly endedAt: string | null;\n };\n readonly invitee: {\n readonly displayName: string | null;\n readonly email: string | null;\n readonly rsvpState: RsvpState;\n readonly respondedAt: string | null;\n };\n }\n | { readonly ok: false; readonly message: string };\n\n/** Org-configurable, never hardcoded taste (Law 6 / \"opinions become knobs\"). */\nexport type RecordingPolicy = \"disabled\" | \"host-controlled\" | \"always-on\";\n\nexport type RecordingState =\n | \"idle\"\n | \"starting\"\n | \"recording\"\n | \"stopping\"\n | \"processing\"\n | \"available\"\n /** Bytes were purged under retention; notes and transcript remain durable. */\n | \"expired\"\n | \"failed\";\n\n/**\n * WHICH transport last said what the recording is doing (MRI-A16).\n *\n * `local` — this browser's own optimistic `starting`/`stopping`, which is all\n * the indicator ever had through 0.4.7, and the reason it never went solid.\n * `room` — the room's replicated state (metadata), the ONLY feed a guest has.\n * `record` — the durable `meet_recordings` row. It outranks both (D16).\n */\nexport type RecordingSource = \"local\" | \"room\" | \"record\";\n\n/** The server's durable retention receipt; never inferred from a browser clock. */\nexport interface RecordingRetention {\n readonly operationId: string;\n readonly policyId: string;\n readonly policyUpdatedAt: string;\n readonly phase: \"warning\" | \"expired\";\n readonly dueAt: string;\n readonly expiredAt: string | null;\n}\n\nexport interface RecordingRecord {\n readonly id: string;\n readonly meetingId: MeetingId;\n readonly state: RecordingState;\n readonly startedAt: string | null;\n readonly endedAt: string | null;\n readonly startedBy: UserId | null;\n /** The PLATFORM file id. Never a provider URL (R3). */\n readonly fileId: string | null;\n readonly failureReason: string | null;\n /** Present when central retention warned or purged this recording. */\n readonly retention: RecordingRetention | null;\n}\n\n/**\n * WHERE A TRANSCRIPT LINE CAME FROM — and therefore how much it is worth.\n *\n * D16: `lk.transcription` is published by a room participant and its\n * `sender_identity` is client-supplied and unvalidated by LiveKit, so a caption\n * is a HINT — good enough to read live, never good enough to reason from. A\n * `record` line is a durable `meet_transcript_segments` row written server-side\n * by the note-taker: that is the transcript. A record always beats a caption;\n * a caption never overwrites a record.\n */\nexport type TranscriptSource = \"caption\" | \"record\";\n\nexport interface TranscriptSegment {\n readonly id: string;\n readonly meetingId: MeetingId;\n readonly identity: ParticipantIdentity;\n readonly speaker: string;\n readonly text: string;\n readonly startedAt: string;\n /** Interim text is replaced in place by its final; it is never appended. */\n readonly final: boolean;\n readonly source: TranscriptSource;\n /**\n * The `lk.segment_id` this line was captioned under, when known. A durable\n * row carries it too (the worker writes it into the row's metadata), which is\n * what lets a record REPLACE the caption it supersedes rather than doubling it.\n */\n readonly captionId: string | null;\n}\n\n/**\n * SOMEBODY WHO WAS IN THE MEETING — the durable `meet_participants` row, which\n * outlives the LiveKit room (MRI-D2). `MeetParticipant` above is a live tile;\n * THIS is the attendance record a person reads a week later.\n */\nexport interface MeetingAttendee {\n readonly id: string;\n readonly meetingId: MeetingId;\n readonly identity: ParticipantIdentity;\n readonly displayName: string;\n readonly avatarUrl: string | null;\n readonly userId: UserId | null;\n readonly role: ParticipantRole;\n readonly admissionState: AdmissionState;\n /** When this person knocked, and when an unanswered knock lapses (null = never). */\n readonly knockedAt?: string | null | undefined;\n readonly knockExpiresAt?: string | null | undefined;\n /** The server's flag on a knock (`metadata.knock_flag`), e.g. `previously_removed`. */\n readonly knockFlag?: string | null | undefined;\n readonly isAgent: boolean;\n /** FIRST arrival in the room — a rejoin never moves it. */\n readonly joinedAt: string | null;\n /** Null while in the room, else the last time they left. */\n readonly leftAt: string | null;\n /**\n * Every stay in the room, oldest first (`metadata.sessions`, written by the\n * server's one attendance rule). Empty for a row recorded before stays were\n * kept; the report then falls back to `joinedAt`–`leftAt`.\n */\n readonly sessions?: readonly AttendanceStay[] | undefined;\n}\n\n/** One stay in the room: arrived, and left (null = still there or never recorded). */\nexport interface AttendanceStay {\n readonly joinedAt: string;\n readonly leftAt: string | null;\n}\n\nexport type MeetingNoteKind = \"note\" | \"decision\" | \"action-item\";\n\n/**\n * ONE ROW OF WHAT THE MEETING DECIDED. `meet_notes.kind` is a live CHECK\n * constraint of exactly three values (D5) — there is no `summary` kind and no\n * `transcript` kind — so the wrap-up summary is a `note` carrying\n * `metadata.role = \"summary\"`, and a live-window note carries `metadata.window`.\n * Both are projected here so the UI never re-reads raw metadata.\n */\nexport interface MeetingNote {\n readonly id: string;\n readonly meetingId: MeetingId;\n readonly text: string;\n readonly createdAt: string;\n readonly kind: MeetingNoteKind;\n /** Set ONLY when the assignee resolved to a real user (D18). */\n readonly assigneeUserId: UserId | null;\n /** Who the item is for, as a person reads it. A guest keeps their name here. */\n readonly assigneeDisplayName: string | null;\n /** `\"summary\"` for the wrap-up; `null` for everything else. */\n readonly role: \"summary\" | null;\n /** The live-notes window index this came out of, when it came from one. */\n readonly window: number | null;\n /**\n * 🚨 THE WRAP-UP IS THE AUTHORITATIVE POST-MEETING SET (D20).\n *\n * A live-notes window and the wrap-up both write decisions and action items,\n * and on production 2026-09-09 meeting `9cb-39f8-782` carried the SAME\n * decision twice — once from the note-taker at 04:43:57 and once from the\n * wrap-up at 04:47:15 — and the record view listed both. A reader cannot tell\n * a repeated decision from two decisions.\n *\n * So when the wrap-up lands, the server MARKS the window-written decisions\n * and action items it supersedes (`metadata.superseded_by` = the wrap-up's\n * run id) and never deletes them: the row is still the timeline of what was\n * known at 04:43. This field carries that marker, and the record view and the\n * assistant panel render only the rows where it is `null`.\n */\n readonly supersededBy: string | null;\n /**\n * WHICH TRANSPORT DELIVERED THIS COPY (MRI-A12). The same durable row reaches\n * a signed-in member twice — once as a `meet_notes` row over Postgres Changes\n * (`\"record\"`) and once as a `matrx.meet.note` packet on the media plane\n * (`\"room\"`), which is the ONLY copy a session-less guest can receive, because\n * `anon` holds no grant on the table (MRI-A10).\n *\n * They carry the same id and the same words, so the store renders one note.\n * Where they could ever disagree, `\"record\"` wins and `\"room\"` never overwrites\n * it — the database is the record and the packet is the courtesy copy. That is\n * the same precedence captions and segments already follow (D16).\n */\n readonly source: \"record\" | \"room\";\n}\n\n/**\n * THE WRAP-UP, ANNOUNCED (MRI-A12). The summary, decisions and action items\n * arrive as ordinary notes; this says the pass that wrote them FINISHED, which\n * is the difference between \"no summary yet\" and \"no summary is coming\".\n *\n * 🚨 It reaches a screen only while the room still exists. `POST /v1/meet/end`\n * deletes the LiveKit room and the wrap-up runs afterwards, so on the ordinary\n * ending path nobody is in the room to be told — the post-meeting record view is\n * where the summary is read. A surface must therefore treat `null` as \"not\n * announced\", never as \"not written\".\n */\nexport interface WrapUpAnnouncement {\n readonly meetingId: MeetingId;\n /** The `meet_notes` ids the pass wrote, in the order it wrote them. */\n readonly noteIds: readonly string[];\n /** How many durable transcript segments it summarized. */\n readonly segmentCount: number;\n readonly at: string;\n}\n\n/**\n * THE NOTE-TAKER'S STATE, as the meeting row records it\n * (`meet_meetings.metadata.note_taker.state`, MRI-A2). Five values and no\n * spinner: a surface renders the state it is in, or the server's refusal.\n *\n * - `absent` — nobody summoned it. The control offers to.\n * - `joining` — dispatched; it has not entered the room yet.\n * - `transcribing` — the server recorded active transcription and the exact\n * deterministic actor is present in the room.\n * - `handoff_pending` — a row that said transcribing no longer has that exact\n * actor in the room. The next state and any missed audio are not inferred.\n * - `ended` — the recorded note-taker stopped. The cause is not inferred.\n */\nexport type NoteTakerState =\n | \"absent\"\n | \"joining\"\n | \"transcribing\"\n | \"handoff_pending\"\n | \"ended\";\n\n/** The lifecycle of a 1:1 or ad-hoc group call (R2). */\nexport type CallState =\n | \"idle\"\n | \"dialing\"\n | \"ringing\"\n | \"connecting\"\n | \"active\"\n | \"declined\"\n | \"missed\"\n | \"busy\"\n | \"cancelled\"\n | \"ended\"\n | \"failed\";\n\nexport interface CallInvite {\n readonly id: CallId;\n readonly organizationId: OrganizationId;\n readonly roomName: RoomName;\n readonly mode: CallMode;\n readonly callerUserId: UserId;\n readonly callerName: string;\n readonly callerAvatarUrl: string | null;\n readonly calleeUserIds: readonly UserId[];\n /** The durable conversation the invite was delivered into, if any. */\n readonly conversationId: string | null;\n readonly createdAt: string;\n readonly expiresAt: string;\n readonly state: CallState;\n readonly declineMessage: string | null;\n}\n\n/** A person the UI can call. Deliberately minimal: hosts already have this. */\nexport interface CallablePerson {\n readonly userId: UserId;\n readonly displayName: string;\n readonly avatarUrl: string | null;\n}\n\nexport type StageLayout = \"grid\" | \"speaker\" | \"sidebar\";\n","/**\n * THE CALL CENTER — ringing, answering, and the six ways a call ends badly.\n *\n * A call reaches you on whatever surface you are on (R2), which is why this is\n * mounted by the provider and not by a call page. It is also why delivery is\n * DUAL by design, not by belt-and-braces nervousness:\n *\n * - **The durable leg** is the invite ROW plus its Postgres Changes event. It\n * survives a reload, a late-joining tab, and a dropped socket, and it is what\n * makes a missed call a fact rather than a lost packet.\n * - **The instant leg** is a broadcast on the callee's user channel, which\n * arrives without a database round trip.\n *\n * Both are deduplicated on the invite id, so a call rings ONCE however it\n * arrives. Neither is a fallback for the other: removing the durable leg loses\n * calls, and removing the instant leg makes ringing feel slow.\n *\n * 🚨 THE FOUR RULES THAT MAKE RINGING SURVIVABLE\n *\n * 1. **A ring always ends.** Every outgoing call arms a timeout that writes\n * `missed`. A ring with no terminal state is a phone that never stops.\n * 2. **Answering on one device stops every other.** The `settled` signal on\n * your OWN user channel silences the other tabs. Without it, answering on a\n * laptop leaves a phone ringing in the room.\n * 3. **Busy is answered, not ignored.** Being in an active call auto-declines\n * with `busy`, so the caller learns something instead of ringing out.\n * 4. **The state machine is a MACHINE.** Transitions are validated: a\n * `declined` call cannot become `active` because a late accept arrived.\n */\n\nimport type { ChannelHandle, RealtimeManager } from \"@ai-matrx/realtime\";\nimport { MEET_EVENTS, userTopic } from \"./channels\";\nimport { MeetError } from \"./errors\";\nimport type { MeetRepository } from \"./repository\";\nimport type { MeetStore } from \"./store\";\nimport { isLivePhase, type CallOutcomePhase } from \"./phase\";\nimport {\n asCallId,\n asRoomName,\n type CallId,\n type CallInvite,\n type CallMode,\n type CallState,\n type MeetIdentity,\n type RoomName,\n type UserId,\n} from \"./types\";\n\n/** Which states may follow which. An invalid transition throws, never silently applies. */\nconst TRANSITIONS: Readonly<Record<CallState, readonly CallState[]>> = {\n idle: [\"dialing\", \"ringing\"],\n dialing: [\"ringing\", \"connecting\", \"cancelled\", \"declined\", \"busy\", \"missed\", \"failed\"],\n ringing: [\"connecting\", \"declined\", \"busy\", \"missed\", \"cancelled\", \"failed\"],\n connecting: [\"active\", \"failed\", \"ended\"],\n active: [\"ended\", \"failed\"],\n declined: [],\n missed: [],\n busy: [],\n cancelled: [],\n ended: [],\n failed: [],\n};\n\nexport function canTransition(from: CallState, to: CallState): boolean {\n return TRANSITIONS[from].includes(to);\n}\n\nexport interface CallCenterOptions {\n manager: RealtimeManager;\n repository: MeetRepository;\n store: MeetStore;\n identity: MeetIdentity;\n /** Ring for this long before the call is `missed`. Org knob, not a constant. */\n ringTimeoutMs?: number | undefined;\n onIncoming?: ((invite: CallInvite) => void) | undefined;\n onSettled?: ((invite: CallInvite) => void) | undefined;\n /**\n * §3.6: MY outgoing call was answered — join its room on the same engine and machine\n * (`ringing_out` → `joining` → `in_call`). The provider wires this to `engine.join`.\n */\n onAnswered?: ((invite: CallInvite) => void) | undefined;\n onDiagnostic?: ((event: { level: \"info\" | \"warn\" | \"error\"; message: string; remedy?: string }) => void) | undefined;\n timers?: {\n setTimeout: (run: () => void, ms: number) => unknown;\n clearTimeout: (handle: unknown) => void;\n now: () => number;\n };\n}\n\nexport interface CallCenter {\n /** Subscribe to this user's calling channel. Call once, from the provider. */\n start(): Promise<void>;\n /** Ring someone. Returns the invite the UI shows as \"calling…\". */\n call(args: {\n people: readonly UserId[];\n mode: CallMode;\n conversationId?: string | undefined;\n }): Promise<CallInvite>;\n accept(id: CallId, mode: CallMode): Promise<CallInvite>;\n decline(id: CallId, message?: string): Promise<CallInvite>;\n cancel(id: CallId): Promise<CallInvite>;\n /** Every invite currently ringing this device. */\n incoming(): readonly CallInvite[];\n outgoing(): CallInvite | null;\n /** Feed a ring that arrived through another path (a messaging action chip). */\n ingest(invite: CallInvite): void;\n dispose(): void;\n}\n\nconst DEFAULT_RING_TIMEOUT_MS = 45_000;\n\nexport function createCallCenter(options: CallCenterOptions): CallCenter {\n const { manager, repository, identity } = options;\n const timers = options.timers ?? {\n setTimeout: (run, ms) => setTimeout(run, ms),\n clearTimeout: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),\n now: () => Date.now(),\n };\n const ringTimeoutMs = options.ringTimeoutMs ?? DEFAULT_RING_TIMEOUT_MS;\n\n const incoming = new Map<CallId, CallInvite>();\n const timeouts = new Map<CallId, unknown>();\n let outgoing: CallInvite | null = null;\n let channel: ChannelHandle | null = null;\n\n /**\n * Send-only handles on OTHER people's user channels.\n *\n * 🚨 CACHED AND CLOSED, deliberately. Since `@ai-matrx/realtime` 0.2.0 a\n * channel is ref-counted per DECLARED topic, so `manager.open(...).send(...)`\n * without a matching `close()` increments a refcount that is never released —\n * a leaked subscription per ring, forever. One handle per peer, all closed on\n * dispose.\n */\n const outbound = new Map<string, ChannelHandle>();\n\n function to(userId: UserId): ChannelHandle {\n const topic = userTopic(userId);\n const existing = outbound.get(topic);\n if (existing !== undefined) return existing;\n const opened = manager.open({ topic });\n outbound.set(topic, opened);\n return opened;\n }\n\n function report(level: \"info\" | \"warn\" | \"error\", message: string, remedy?: string): void {\n options.onDiagnostic?.(remedy === undefined ? { level, message } : { level, message, remedy });\n }\n\n function requireUser(operation: string): UserId {\n if (identity.userId === null) {\n throw new MeetError(\n \"forbidden\",\n `${operation}: a guest cannot place or receive calls`,\n \"Guests join meetings by link (D6); calling requires an account. Render the join-by-\" +\n \"link path instead of a call button for guest identities.\",\n );\n }\n return identity.userId;\n }\n\n function clearRing(id: CallId): void {\n const handle = timeouts.get(id);\n if (handle !== undefined) {\n timers.clearTimeout(handle);\n timeouts.delete(id);\n }\n }\n\n function settleLocal(invite: CallInvite): void {\n clearRing(invite.id);\n incoming.delete(invite.id);\n if (outgoing !== null && outgoing.id === invite.id) outgoing = invite;\n // §3.6 on the machine: a ring that ended here without this device answering it.\n const held = options.store.snapshot().call;\n if (held !== null && held.invite.id === invite.id && held.direction === \"incoming\") {\n const outcome: CallOutcomePhase | \"reset\" =\n invite.state === \"connecting\" || invite.state === \"active\"\n ? \"answered_elsewhere\"\n : invite.state === \"declined\"\n ? \"reset\"\n : \"call_missed\";\n const moved = outcome === \"reset\"\n ? options.store.dispatch({ type: \"reset\" })\n : options.store.dispatch({ type: \"call_outcome\", outcome });\n if (moved) options.store.patch({ call: outcome === \"reset\" ? null : { ...held, invite } });\n }\n options.onSettled?.(invite);\n }\n\n /** §3.6: the callee's answer reached the caller. */\n function settleOutgoing(invite: CallInvite): void {\n if (outgoing === null || outgoing.id !== invite.id) return;\n clearRing(invite.id);\n outgoing = invite;\n const store = options.store;\n const held = store.snapshot().call;\n if (held !== null && held.invite.id === invite.id) store.patch({ call: { ...held, invite } });\n switch (invite.state) {\n case \"connecting\":\n case \"active\":\n if (store.dispatch({ type: \"call_answered\" })) options.onAnswered?.(invite);\n break;\n case \"declined\":\n store.dispatch({ type: \"call_outcome\", outcome: \"call_declined\" });\n break;\n case \"busy\":\n store.dispatch({ type: \"call_outcome\", outcome: \"call_busy\" });\n break;\n case \"missed\":\n case \"failed\":\n store.dispatch({ type: \"call_outcome\", outcome: \"call_missed\" });\n break;\n default:\n break;\n }\n options.onSettled?.(invite);\n }\n\n function ingest(invite: CallInvite): void {\n // ONE ring per invite, whichever leg arrived first.\n if (incoming.has(invite.id)) return;\n if (invite.state !== \"ringing\" && invite.state !== \"dialing\") return;\n if (identity.userId === null) return;\n if (invite.callerUserId === identity.userId) return;\n\n // RULE 3: busy is ANSWERED. The caller learns why rather than ringing out.\n const phase = options.store.snapshot().phase;\n if (isLivePhase(phase) || phase === \"ringing_out\") {\n void repository\n .settleCallInvite(invite.id, \"busy\", null)\n .then((settled) => {\n // The CALLER is who needs to know. Sending this on my own topic\n // would tell my other devices something they already worked out and\n // leave the caller ringing out into silence.\n to(settled.callerUserId).send(MEET_EVENTS.settled, settled);\n })\n .catch((error: unknown) => {\n report(\"warn\", `meet.calls: could not report busy — ${String(error)}`,\n \"The caller will ring out and see a missed call. Harmless, but the busy signal \" +\n \"is what makes a second call feel intentional.\");\n });\n return;\n }\n\n incoming.set(invite.id, { ...invite, state: \"ringing\" });\n // §3.6: when nothing else is on screen the ring IS the phase; otherwise (a pre-join, a\n // waiting room) the card rings over it and the phase stays where the person is.\n if (options.store.snapshot().call === null && options.store.dispatch({ type: \"ring_in\" })) {\n options.store.patch({ call: { invite: { ...invite, state: \"ringing\" }, direction: \"incoming\" } });\n }\n options.onIncoming?.(invite);\n // RULE 1: every ring ends. Even if the caller vanishes mid-ring.\n timeouts.set(\n invite.id,\n timers.setTimeout(() => {\n const held = incoming.get(invite.id);\n if (held === undefined) return;\n settleLocal({ ...held, state: \"missed\" });\n }, ringTimeoutMs),\n );\n }\n\n return {\n async start() {\n const me = requireUser(\"meet.calls.start\");\n channel = manager.open({\n topic: userTopic(me),\n broadcast: [\n {\n event: MEET_EVENTS.ring,\n onMessage: (message) => {\n ingest(message.data as CallInvite);\n },\n },\n {\n event: MEET_EVENTS.cancel,\n onMessage: (message) => {\n const invite = message.data as CallInvite;\n const held = incoming.get(invite.id);\n if (held === undefined) return;\n settleLocal({ ...held, state: \"cancelled\" });\n },\n },\n {\n event: MEET_EVENTS.settled,\n onMessage: (message) => {\n // RULE 2: answered elsewhere. Stop ringing HERE.\n const invite = message.data as CallInvite;\n const held = incoming.get(invite.id);\n if (held !== undefined) settleLocal(invite);\n // THE CALLER'S LEG: the callee answered, declined or was busy. Through 0.10 this\n // handler only knew incoming rings, so a caller never learned the call was answered.\n else if (outgoing?.id === invite.id) settleOutgoing(invite);\n },\n },\n ],\n postgresChanges: [\n {\n event: \"INSERT\",\n schema: repository.schema,\n table: repository.tables.invites,\n filter: `callee_ids=cs.{${me}}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: (delivery) => {\n if (delivery.row === null) return;\n ingest(repository.projectInvite(delivery.row));\n },\n },\n {\n event: \"UPDATE\",\n schema: repository.schema,\n table: repository.tables.invites,\n filter: `callee_ids=cs.{${me}}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: (delivery) => {\n if (delivery.row === null) return;\n const invite = repository.projectInvite(delivery.row);\n if (invite.state !== \"ringing\" && invite.state !== \"dialing\") {\n const held = incoming.get(invite.id);\n if (held !== undefined) settleLocal(invite);\n }\n },\n },\n ],\n // THE BACKFILL DOOR. Realtime has no replay: a socket that dropped for\n // 20 seconds must re-read, or a call placed during the gap rings on the\n // caller's screen and nowhere else.\n onBackfill: async () => {\n const pending = await repository.pendingInvites(me);\n for (const invite of pending) ingest(invite);\n },\n });\n const pending = await repository.pendingInvites(me);\n for (const invite of pending) ingest(invite);\n },\n\n async call({ people, mode, conversationId }) {\n const me = requireUser(\"meet.calls.call\");\n if (people.length === 0) {\n throw new MeetError(\n \"misconfigured\",\n \"meet.calls.call: no callees\",\n \"Pass at least one user id. An empty call would create a room nobody is invited to.\",\n );\n }\n // This is a compatible proposal only. The server authors the room name;\n // outgoing state, signaling and joins must use the returned persisted invite.\n const roomName: RoomName = asRoomName(\n `call-${me}-${timers.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`,\n );\n const invite = await repository.createCallInvite({\n roomName,\n mode,\n callerUserId: me,\n callerName: identity.displayName,\n callerAvatarUrl: identity.avatarUrl,\n calleeUserIds: people,\n organizationId: identity.organizationId,\n conversationId: conversationId ?? null,\n expiresAt: new Date(timers.now() + ringTimeoutMs).toISOString(),\n });\n outgoing = invite;\n // §3.6: placing a call is a phase on the same machine.\n if (options.store.dispatch({ type: \"ring_out\" })) {\n options.store.patch({ call: { invite, direction: \"outgoing\" } });\n }\n\n // The instant leg. The durable leg already happened — the row is written.\n for (const callee of people) {\n to(callee).send(MEET_EVENTS.ring, invite);\n }\n timeouts.set(\n invite.id,\n timers.setTimeout(() => {\n if (outgoing?.id !== invite.id) return;\n void repository\n .settleCallInvite(invite.id, \"missed\", null)\n .then((settled) => {\n settleOutgoing(settled);\n })\n .catch(() => undefined);\n }, ringTimeoutMs),\n );\n return invite;\n },\n\n async accept(id, mode) {\n const held = incoming.get(id);\n if (held !== undefined && !canTransition(held.state, \"connecting\")) {\n throw new MeetError(\n \"conflict\",\n `meet.calls.accept: this call is already ${held.state}`,\n \"A late accept must not resurrect a settled call. Close the ringing UI; the caller \" +\n \"has already moved on.\",\n );\n }\n // §3.6: the server settles FIRST. A settle that fails (the caller gave up, the network\n // dropped) throws with the phase still `ringing_in` — the ring screen is honest — instead of\n // stranding the tab on `joining` with nothing joining. Only a settled answer moves it.\n const settled = await repository.settleCallInvite(id, \"connecting\", null);\n // `ringing_in` → `joining` BEFORE this device hears its own settle, so it never reads its\n // own answer as \"answered elsewhere\". The join itself is the provider's.\n if (options.store.snapshot().phase === \"ringing_in\") options.store.dispatch({ type: \"call_answered\" });\n options.store.patch({ call: { invite: settled, direction: \"incoming\" } });\n settleLocal(settled);\n // Tell my OTHER devices to stop ringing, and the caller to stop waiting.\n channel?.send(MEET_EVENTS.settled, settled);\n to(settled.callerUserId).send(MEET_EVENTS.settled, { ...settled, mode });\n return settled;\n },\n\n async decline(id, message) {\n const settled = await repository.settleCallInvite(id, \"declined\", message ?? null);\n if (options.store.snapshot().call?.invite.id === id && options.store.dispatch({ type: \"reset\" })) {\n options.store.patch({ call: null });\n }\n settleLocal(settled);\n channel?.send(MEET_EVENTS.settled, settled);\n to(settled.callerUserId).send(MEET_EVENTS.settled, settled);\n return settled;\n },\n\n async cancel(id) {\n const settled = await repository.settleCallInvite(id, \"cancelled\", null);\n clearRing(id);\n if (outgoing?.id === id) outgoing = settled;\n if (options.store.snapshot().call?.invite.id === id && options.store.dispatch({ type: \"reset\" })) {\n options.store.patch({ call: null });\n }\n for (const callee of settled.calleeUserIds) {\n to(callee).send(MEET_EVENTS.cancel, settled);\n }\n return settled;\n },\n\n incoming: () => [...incoming.values()],\n outgoing: () => outgoing,\n ingest,\n\n dispose() {\n for (const id of [...timeouts.keys()]) clearRing(asCallId(id));\n incoming.clear();\n for (const handle of outbound.values()) handle.close();\n outbound.clear();\n channel?.close();\n channel = null;\n },\n };\n}\n","/**\n * THE DATA CONTRACT — the ONE place a meet table or RPC name exists.\n *\n * 🚨 **STATE OF THE SCHEMA (read this before believing a green test).** The\n * `communication.meet_*` tables below are the canonical schema DESIGNED by this\n * package per R8; they are **not deployed to Matrx Main yet**. They were placed\n * by inspecting the live database on 2026-08-31: `communication` is the schema\n * that already holds `dm_*` (messaging) and `sms_*` (telephony), so meet takes\n * the `meet_*` prefix in the same schema rather than inventing a new one. Until\n * the migration lands, every read here fails LOUDLY with `not-found` and a\n * remedy naming this file — never silently, and never with a fabricated row.\n * The migration and the aidream server half are one deliverable; see FEATURE.md\n * § Deliberately NOT in this package.\n *\n * Four rules hold this file together — the same four that hold messaging's:\n *\n * 1. **Org is explicit on every write (R5).** Not defaulted by a trigger we\n * hope fires — passed, and refused in-package when absent.\n * 2. **Authorization is the database's job.** There is no permission branch in\n * this file. An RPC that says no is a `forbidden` MeetError, never a\n * client-side re-decision. Guest capability is scoped by the room TOKEN, not\n * by a row this client could write.\n * 3. **Writes go through SECURITY DEFINER RPCs; reads are RLS-backed table\n * reads** (C17's hybrid ruling). Every write here is an RPC, which is also\n * what keeps admission and settle-a-call ATOMIC — two hosts clicking \"admit\"\n * and a callee accepting on two devices are the ordinary case, not the edge.\n * 4. **Pagination is keyset, terminated by a unique column.** `(created_at,\n * id)` — never a timestamp alone, which duplicates or skips rows whenever\n * two share a millisecond.\n *\n * The Supabase client is accepted STRUCTURALLY, through the shape\n * `@ai-matrx/messaging` already owns. One definition of \"a Supabase client\" for\n * the whole communications family; this package adds no second copy.\n *\n * 🚨 AND THE PUBLIC BOUNDARY IS THE SHALLOW ONE (0.2.1). `RepositoryOptions`\n * and `<MeetProvider client>` take `MessagingSupabaseClient` — method names and\n * arity, results as `unknown` — never the rich `SupabaseLike`. Checking a\n * host's real `SupabaseClient<Database>` (built from generated types) against\n * the rich shape makes tsc chase PostgREST's schema machinery until it bails\n * with `TS2589: Type instantiation is excessively deep and possibly infinite`,\n * so the only way to pass the client a host ALREADY HAS was a cast — which is\n * what the first real consumer had to write (register MRI-D1). Messaging 0.9.0\n * learned this and wrote it down; this package did not apply the lesson until\n * now. The rich shape stays the INTERNAL contract, and we narrow to it ONCE,\n * here, in-package.\n */\n\nimport { scheduleSettingsPatch } from \"./app-panel\";\nimport type { MessagingSupabaseClient, SupabaseLike } from \"@ai-matrx/messaging\";\nimport { invalidResponse, MeetError, normalizeMeetError } from \"./errors\";\nimport {\n asCallId,\n asMeetingId,\n asOrganizationId,\n asParticipantIdentity,\n asRoomName,\n asUserId,\n type CallId,\n type CallInvite,\n type CallMode,\n type CallState,\n type JsonObject,\n type MeetingId,\n type MeetingKind,\n type MeetingAttendee,\n type MeetingNote,\n type LinkRunState,\n type MeetingRecord,\n type ParticipantRole,\n type AdmissionState,\n type OrganizationId,\n type RecordingPolicy,\n type RecordingRecord,\n type RecordingRetention,\n type RecordingState,\n type RoomName,\n type TranscriptSegment,\n type UserId,\n type InviteeInput,\n type InviteeRole,\n type MeetingChanges,\n type AttendanceStay,\n type MeetingInvitee,\n type MeetingOccurrence,\n type MeetingSettings,\n type OccurrenceOverride,\n type OccurrenceState,\n type RsvpAnswer,\n type RsvpInvitation,\n type RsvpState,\n type UpcomingOccurrence,\n} from \"./types\";\n\n/** The schema. Meet is never in `public`. */\nexport const MEET_SCHEMA = \"communication\";\n\nexport const TABLES = {\n meetings: \"meet_meetings\",\n participants: \"meet_participants\",\n invites: \"meet_call_invites\",\n recordings: \"meet_recordings\",\n transcripts: \"meet_transcript_segments\",\n notes: \"meet_notes\",\n /** Wave 1 (0.8): who a meeting was sent to, and what they answered. */\n invitees: \"meet_invitees\",\n /** Wave 1 (0.8): one occurrence of a series cancelled or moved. */\n occurrenceOverrides: \"meet_occurrence_overrides\",\n /** S5: one row per RUN of a meeting; carries that run's note-taker state. */\n sessions: \"meet_sessions\",\n} as const;\n\nexport const RPCS = {\n /** Atomic: create the invite row and stamp its expiry in one statement. */\n createCallInvite: \"meet_create_call_invite\",\n /** Atomic settle. Refuses a transition out of a terminal state. */\n settleCallInvite: \"meet_settle_call_invite\",\n /** Every invite still ringing this user — the reconnect backfill door. */\n pendingCallInvites: \"meet_pending_call_invites\",\n /** Instant meeting, or the durable one behind a recurring link. */\n getOrCreateMeeting: \"meet_get_or_create_meeting\",\n /** Resolve a durable public link, before/during/after its scheduled time. */\n meetingBySlug: \"meet_meeting_by_slug\",\n /** The current run of a durable link (open / not started / ended + next start), no pass needed. */\n linkState: \"meet_link_state\",\n /*\n * ── S4 ADMISSION (CORE-DESIGN §2.4). Each returns the decided row; the caller then\n * sends the payload-free `mx.state.changed` hint. Authorized by `who_may_admit`.\n */\n admit: \"meet_admit\",\n deny: \"meet_deny\",\n undeny: \"meet_undeny\",\n admitAll: \"meet_admit_all\",\n setLock: \"meet_set_lock\",\n recordConsent: \"meet_record_consent\",\n /** One meeting rule as the gate obeys it (the knob ladder, this meeting's rung included). */\n meetingRule: \"meet_policy\",\n /** A host sets one rule for THIS meeting (the `meet_meeting` rung); refusals come back as `{ok:false}`. */\n setMeetingRule: \"meet_policy_set\",\n /*\n * ── MEET WAVE 1 — management (0.8). Every write is a SECURITY DEFINER door\n * authorized by the meeting's host or a real iam grant (editor to change,\n * admin = co-host to archive or name a co-host) — never an organization\n * match. After any of these writes, call `announceMeeting` so the server\n * sends invitations / updates / cancellations and re-queues reminders.\n */\n /** Schedule with every setting at create; unset settings come from the knobs. */\n scheduleMeeting: \"meet_schedule_meeting\",\n /** Edit / reschedule — a JSON patch with a version compare-and-swap. */\n updateMeeting: \"meet_update_meeting\",\n /** Cancel is a state (`cancelled_at`); the link keeps resolving. */\n cancelMeeting: \"meet_cancel_meeting\",\n /** Archive (soft delete) — the invitations and exceptions cascade. */\n archiveMeeting: \"meet_archive_meeting\",\n restoreMeeting: \"meet_restore_meeting\",\n /** Cancel, move or restore ONE occurrence of a series by its original start. */\n setOccurrence: \"meet_set_occurrence\",\n addInvitees: \"meet_add_invitees\",\n setInviteeRole: \"meet_set_invitee_role\",\n removeInvitee: \"meet_remove_invitee\",\n /** A signed-in invitee answers for themselves. */\n respond: \"meet_respond\",\n /** The emailed RSVP link (no account): read and answer. */\n invitationByToken: \"meet_invitation_by_token\",\n respondByToken: \"meet_respond_by_token\",\n /** One meeting's occurrences between two instants (INVOKER — the reader's RLS). */\n meetingOccurrences: \"meet_meeting_occurrences\",\n /** Every occurrence the reader can see between two instants — the Upcoming list. */\n occurrencesBetween: \"meet_occurrences_between\",\n /*\n * ── MEET WAVE 3 — after the meeting. Both INVOKER (the reader's RLS) and both\n * scoped to the reader's OWN meetings (host, invitee, attended) — RLS is the\n * ceiling, never the view.\n */\n /** The recordings library. */\n myRecordings: \"meet_my_recordings\",\n /** Titles, agendas, current notes and final transcript lines; snippets mark hits with ⟦ ⟧. */\n search: \"meet_search\",\n} as const;\n\n/**\n * THE RPCs THAT RETURN NOTHING, verified against the live database\n * (`pg_get_function_result`, 2026-09-08). They go through `guardVoid`; every\n * other RPC returns a row and goes through `guard`. Wiring a void RPC to\n * `guard` makes a SUCCESSFUL write look like a failure — see `guardVoid`.\n */\nexport const VOID_RPCS: readonly string[] = [RPCS.recordConsent];\n\nconst MEETING_COLUMNS =\n \"id,organization_id,room_name,slug,title,kind,host_user_id,scheduled_for,\" +\n \"scheduled_duration_minutes,started_at,ended_at,locked,lobby_enabled,\" +\n \"recording_policy,ai_enabled,metadata,\" +\n // Wave 1 (0.8).\n \"time_zone,agenda,recurrence_rule,join_before_host,cancelled_at,cancellation_reason,\" +\n \"deleted_at,calendar_sequence,version\";\n\nconst INVITEE_COLUMNS =\n \"id,meeting_id,invitee_user_id,email,display_name,role,rsvp_state,responded_at,\" +\n \"rsvp_note,last_sent_sequence,last_sent_at\";\n\n/**\n * The invite row's columns. Not used by a `select` today — every invite read\n * and write goes through an RPC (C17's hybrid: writes are SECURITY DEFINER,\n * and a settle must be atomic) — but it is part of the contract S2's migration\n * has to satisfy, and it is what `projectInvite` reads out of an RPC's return\n * row. Keep it in step with that function.\n */\nconst INVITE_COLUMNS =\n \"id,organization_id,room_name,mode,caller_user_id,caller_name,caller_avatar_url,\" +\n \"callee_ids,conversation_id,created_at,expires_at,state,decline_message\";\n\nconst RECORDING_COLUMNS =\n \"id,meeting_id,state,started_at,ended_at,started_by,file_id,failure_reason\";\n\n/**\n * THE TRANSCRIPT'S COLUMNS. `is_final` — not `final` — is the live column name,\n * and only final rows are ever written (D6: an interim is a caption and a\n * caption is never persisted).\n */\nconst SEGMENT_COLUMNS = \"id,meeting_id,identity,speaker,text,started_at,is_final,metadata\";\n\n/** What the meeting decided. `kind` is a three-value CHECK constraint (D5). */\nconst NOTE_COLUMNS = \"id,meeting_id,text,kind,assignee_user_id,created_at,metadata\";\n\n/**\n * WHO WAS THERE. The row survives the room, which is the whole point: an\n * attendance list that only exists while LiveKit holds the room is not a record\n * of anything (MRI-D2).\n */\nconst ATTENDEE_COLUMNS =\n \"id,meeting_id,identity,participant_user_id,display_name,avatar_url,role,\" +\n \"admission_state,knocked_at,knock_expires_at,is_agent,joined_at,left_at,sessions:metadata->sessions,\" +\n \"knock_flag:metadata->>knock_flag\";\n\ntype Row = Record<string, unknown>;\n\n/** `metadata.sessions` → stays; anything malformed is skipped, never guessed. */\nfunction projectStays(value: unknown): readonly AttendanceStay[] {\n if (!Array.isArray(value)) return [];\n const stays: AttendanceStay[] = [];\n for (const item of value) {\n if (item === null || typeof item !== \"object\") continue;\n const joined = (item as Row)[\"joined_at\"];\n const left = (item as Row)[\"left_at\"];\n if (typeof joined !== \"string\" || joined.length === 0) continue;\n stays.push({ joinedAt: joined, leftAt: typeof left === \"string\" && left.length > 0 ? left : null });\n }\n return stays;\n}\n\nfunction text(row: Row, key: string): string | null {\n const value = row[key];\n return typeof value === \"string\" && value.length > 0 ? value : null;\n}\n\nfunction requireText(row: Row, key: string, operation: string): string {\n const value = text(row, key);\n if (value === null) {\n throw invalidResponse(operation, `the row is missing \"${key}\"`);\n }\n return value;\n}\n\nfunction bool(row: Row, key: string, fallback: boolean): boolean {\n const value = row[key];\n return typeof value === \"boolean\" ? value : fallback;\n}\n\nfunction stringArray(row: Row, key: string): readonly string[] {\n const value = row[key];\n if (!Array.isArray(value)) return [];\n return value.filter((entry): entry is string => typeof entry === \"string\");\n}\n\nconst CALL_STATES: readonly CallState[] = [\n \"idle\",\n \"dialing\",\n \"ringing\",\n \"connecting\",\n \"active\",\n \"declined\",\n \"missed\",\n \"busy\",\n \"cancelled\",\n \"ended\",\n \"failed\",\n];\n\nfunction callState(value: string | null): CallState {\n return CALL_STATES.includes(value as CallState) ? (value as CallState) : \"failed\";\n}\n\nexport interface CreateCallInviteInput {\n readonly roomName: RoomName;\n readonly mode: CallMode;\n readonly callerUserId: UserId;\n readonly callerName: string;\n readonly callerAvatarUrl: string | null;\n readonly calleeUserIds: readonly UserId[];\n readonly organizationId: OrganizationId;\n readonly conversationId: string | null;\n readonly expiresAt: string;\n}\n\nexport interface CreateMeetingInput {\n readonly organizationId: OrganizationId;\n readonly hostUserId: UserId;\n readonly title: string;\n readonly kind: MeetingKind;\n readonly scheduledFor?: string | null;\n readonly scheduledDurationMinutes?: number | null;\n readonly lobbyEnabled?: boolean;\n readonly recordingPolicy?: RecordingPolicy;\n readonly aiEnabled?: boolean;\n /** Reuse a durable slug (a recurring meeting keeps ONE link — R3). */\n readonly slug?: string | undefined;\n}\n\nexport interface MeetRepository {\n readonly schema: string;\n readonly tables: typeof TABLES;\n projectInvite(row: Row): CallInvite;\n projectMeeting(row: Row): MeetingRecord;\n createCallInvite(input: CreateCallInviteInput): Promise<CallInvite>;\n settleCallInvite(id: CallId, state: CallState, declineMessage: string | null): Promise<CallInvite>;\n pendingInvites(userId: UserId): Promise<readonly CallInvite[]>;\n getOrCreateMeeting(input: CreateMeetingInput): Promise<MeetingRecord>;\n meetingBySlug(slug: string): Promise<MeetingRecord>;\n meeting(id: MeetingId): Promise<MeetingRecord>;\n /**\n * Keyset paged, `(scheduled_for, id)`. Every meeting the person can access, in\n * ALL their organizations — `organizationId` is an OPTIONAL page filter\n * (absent/null = all organizations), never the active organization\n * (active-org-is-never-a-list-filter.md). Access is decided by RLS.\n */\n listMeetings(args: {\n organizationId?: OrganizationId | null;\n limit?: number;\n before?: { scheduledFor: string; id: MeetingId } | null;\n }): Promise<{ readonly items: readonly MeetingRecord[]; readonly hasMore: boolean }>;\n /** S4: one decision on one waiting person; returns the row as it now stands. */\n decideAdmission(args: {\n meetingId: MeetingId;\n identity: string;\n decision: \"admit\" | \"deny\" | \"undeny\";\n byUserId: UserId;\n }): Promise<MeetingAttendee>;\n /** S4: admit everyone still waiting (not expired); returns the admitted rows. */\n admitAll(meetingId: MeetingId, byUserId: UserId): Promise<readonly MeetingAttendee[]>;\n setLock(meetingId: MeetingId, locked: boolean, byUserId: UserId): Promise<MeetingRecord>;\n /** `wr-policy-who-waits`: read one rule of this meeting (`lobby_bypass`, `access_type`…). */\n meetingRule(meetingId: MeetingId, key: string): Promise<unknown>;\n /** A host sets one rule for this meeting; a refusal throws with the server's sentence. */\n setMeetingRule(meetingId: MeetingId, key: string, value: string | number | boolean): Promise<unknown>;\n projectSegment(row: Row): TranscriptSegment;\n projectNote(row: Row): MeetingNote;\n /** One `meet_recordings` row, however it arrived (row or room packet). */\n projectRecording(row: Row): RecordingRecord;\n /** One durable `meet_participants` row — attendance, and the lobby queue. */\n projectAttendee(row: Row): MeetingAttendee;\n projectAttendee(row: Row): MeetingAttendee;\n /**\n * WHO ATTENDED, oldest arrival first. Everyone the meeting ever admitted —\n * including the note-taker, which is a labelled participant and never a hidden\n * listener (R4) — so the record can say who was in the room AND that it was\n * transcribed.\n */\n attendees(meetingId: MeetingId): Promise<readonly MeetingAttendee[]>;\n /**\n * THE DURABLE TRANSCRIPT (D16). Final rows only — the table holds nothing\n * else — oldest first, capped so a four-hour meeting cannot hand a surface\n * fifty thousand rows in one read.\n */\n transcript(meetingId: MeetingId, limit?: number): Promise<readonly TranscriptSegment[]>;\n /** Live notes, decisions, action items and the wrap-up summary, oldest first. */\n notes(meetingId: MeetingId): Promise<readonly MeetingNote[]>;\n /** R5: consent is RECORDED, not assumed. One row per participant per recording. */\n recordConsent(args: {\n meetingId: MeetingId;\n identity: string;\n acknowledgedAt: string;\n }): Promise<void>;\n recordings(meetingId: MeetingId): Promise<readonly RecordingRecord[]>;\n /** Meet wave 3: every recording of the reader's own meetings, newest first. */\n myRecordings(args?: { includeArchived?: boolean; limit?: number }): Promise<readonly LibraryRecording[]>;\n /** Meet wave 3: search the reader's own meetings. */\n searchMeetings(query: string, limit?: number): Promise<readonly MeetingSearchHit[]>;\n /** Meet wave 3: name a recording (`null` = back to the meeting's title and date). */\n renameRecording(recordingId: string, title: string | null): Promise<void>;\n /** Meet wave 3: archive (soft delete) or restore a recording. Never a purge. */\n setRecordingArchived(recordingId: string, archived: boolean): Promise<void>;\n\n /* ── MEET WAVE 1 — management (0.8) ── */\n scheduleMeeting(input: ScheduleMeetingInput): Promise<MeetingRecord>;\n /** `expectedVersion` from the row you edited; a stale one is a `conflict`. */\n updateMeeting(\n meetingId: MeetingId,\n changes: MeetingChanges,\n options?: { expectedVersion?: number | null; byUserId?: UserId | null },\n ): Promise<MeetingRecord>;\n cancelMeeting(meetingId: MeetingId, reason: string | null, byUserId?: UserId | null): Promise<MeetingRecord>;\n archiveMeeting(meetingId: MeetingId, byUserId?: UserId | null): Promise<MeetingRecord>;\n restoreMeeting(meetingId: MeetingId, byUserId?: UserId | null): Promise<MeetingRecord>;\n setOccurrence(args: {\n meetingId: MeetingId;\n originalStart: string;\n action: \"cancel\" | \"move\" | \"restore\";\n newStart?: string | null;\n newDurationMinutes?: number | null;\n reason?: string | null;\n byUserId?: UserId | null;\n }): Promise<OccurrenceOverride>;\n meetingOccurrences(\n meetingId: MeetingId,\n range: { from: string; to: string; limit?: number },\n ): Promise<readonly MeetingOccurrence[]>;\n /** The Upcoming list: every occurrence the reader may see. `organizationId` FILTERS; RLS decides. */\n occurrencesBetween(range: {\n from: string;\n to: string;\n organizationId?: OrganizationId | null;\n limit?: number;\n }): Promise<readonly UpcomingOccurrence[]>;\n invitees(meetingId: MeetingId): Promise<readonly MeetingInvitee[]>;\n addInvitees(meetingId: MeetingId, people: readonly InviteeInput[], byUserId?: UserId | null): Promise<readonly MeetingInvitee[]>;\n setInviteeRole(inviteeId: string, role: InviteeRole, byUserId?: UserId | null): Promise<MeetingInvitee>;\n removeInvitee(inviteeId: string, byUserId?: UserId | null): Promise<MeetingInvitee>;\n respond(meetingId: MeetingId, answer: RsvpAnswer, note?: string | null): Promise<MeetingInvitee>;\n invitationByToken(secret: string): Promise<RsvpInvitation>;\n respondByToken(secret: string, answer: RsvpAnswer, note?: string | null): Promise<RsvpInvitation>;\n projectInvitee(row: Row): MeetingInvitee;\n}\n\nexport interface ScheduleMeetingInput {\n readonly organizationId: OrganizationId;\n readonly hostUserId: UserId;\n readonly title: string;\n readonly scheduledFor: string | null;\n /** IANA zone, e.g. `America/Los_Angeles`. The wall-clock time a series keeps across DST. */\n readonly timeZone: string;\n /** Omit to use the person's `meet.default_duration_minutes`. */\n readonly durationMinutes?: number | null | undefined;\n readonly agenda?: string | null | undefined;\n /** RRULE subset: FREQ=DAILY|WEEKLY|MONTHLY, INTERVAL, BYDAY, BYMONTHDAY, COUNT, UNTIL. */\n readonly recurrenceRule?: string | null | undefined;\n readonly settings?: MeetingSettings | undefined;\n /** HR-360 wave 2 (MD-15): the registered app panel the call opens beside it. */\n readonly appPanel?: { readonly key: string; readonly recordId: string } | undefined;\n /** HR-360 wave 2 (MD-16): user ids the host names as silent observers. */\n readonly observers?: readonly string[] | undefined;\n}\n\nexport interface RepositoryOptions {\n /**\n * Any Supabase client. Deliberately the SHALLOW public shape — pass the\n * `SupabaseClient<Database>` you already have, with no cast. See the header.\n */\n client: MessagingSupabaseClient;\n /** Resolve a session; used to retry ONCE across the sign-in gap. */\n session?: (() => Promise<unknown>) | undefined;\n}\n\n/**\n * 🚨 THE PROJECTIONS ARE MODULE-LEVEL AND EXPORTED, DELIBERATELY (MRI-A12).\n *\n * A durable Meet row reaches a client on one of two transports: as a\n * `meet_notes` / `meet_transcript_segments` row over Postgres Changes, which\n * needs a Supabase session and an org grant, or as a `matrx.meet.*` packet on\n * the LiveKit data plane, which needs only the room token — and is therefore the\n * ONLY one a session-less guest can receive (MRI-A10 settled that `anon` never\n * gets a grant on an org-scoped table).\n *\n * Both carry the same row. If each transport projected it with its own function,\n * a guest and a member would render the same meeting differently the first time\n * the two drifted. So there is one function per row shape, it takes no client,\n * and `core/room-events.ts` calls exactly these.\n */\nexport function projectSegment(row: Row): TranscriptSegment {\n const operation = \"meet.projectSegment\";\n const id = requireText(row, \"id\", operation);\n return {\n id,\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n identity: asParticipantIdentity(text(row, \"identity\") ?? \"unknown\"),\n // A nameless line is a line nobody can act on. The identity is a worse\n // name than a real one and a far better one than an empty string.\n speaker: text(row, \"speaker\") ?? text(row, \"identity\") ?? \"Someone\",\n text: text(row, \"text\") ?? \"\",\n startedAt: text(row, \"started_at\") ?? new Date(0).toISOString(),\n // The table holds finals only; a row that somehow says otherwise is still\n // a durable record and is rendered as one.\n final: bool(row, \"is_final\", true),\n source: \"record\",\n // The worker publishes the caption under THIS row's id (`lk.segment_id` =\n // the durable segment id), which is what lets a record replace the exact\n // caption it supersedes instead of doubling the line (D16).\n captionId: id,\n };\n}\n\nexport function projectNote(row: Row): MeetingNote {\n const operation = \"meet.projectNote\";\n const kind = text(row, \"kind\");\n const rawMetadata = row[\"metadata\"];\n const metadata: Record<string, unknown> =\n typeof rawMetadata === \"object\" && rawMetadata !== null && !Array.isArray(rawMetadata)\n ? (rawMetadata as Record<string, unknown>)\n : {};\n const assignee = metadata[\"assignee\"];\n const assigneeRow: Record<string, unknown> =\n typeof assignee === \"object\" && assignee !== null && !Array.isArray(assignee)\n ? (assignee as Record<string, unknown>)\n : {};\n const windowValue = metadata[\"window\"];\n const windowRow: Record<string, unknown> =\n typeof windowValue === \"object\" && windowValue !== null && !Array.isArray(windowValue)\n ? (windowValue as Record<string, unknown>)\n : {};\n const assigneeUserId = text(row, \"assignee_user_id\");\n const displayName = assigneeRow[\"display_name\"];\n return {\n id: requireText(row, \"id\", operation),\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n text: (text(row, \"text\") ?? \"\").trim(),\n createdAt: text(row, \"created_at\") ?? new Date().toISOString(),\n // An unknown kind is a NOTE, never dropped: a row the database accepted\n // is a row a person wrote down, whatever a future migration calls it.\n kind:\n kind === \"decision\" || kind === \"action-item\" ? (kind as MeetingNote[\"kind\"]) : \"note\",\n assigneeUserId: assigneeUserId === null ? null : asUserId(assigneeUserId),\n // D18: a guest never gets an `assignee_user_id` (the FK targets\n // `auth.users`), so their name lives in the metadata and is read here —\n // an action item whose owner has no name is an action item nobody owns.\n assigneeDisplayName:\n typeof displayName === \"string\" && displayName.length > 0 ? displayName : null,\n role: metadata[\"role\"] === \"summary\" ? \"summary\" : null,\n window: typeof windowRow[\"index\"] === \"number\" ? windowRow[\"index\"] : null,\n // D20. `superseded_by` is in `NOTE_PUBLIC_METADATA_KEYS` server-side, so a\n // session-less guest's room-borne copy carries it too — otherwise the guest\n // would be the one lane still reading a decision twice.\n supersededBy:\n typeof metadata[\"superseded_by\"] === \"string\" && metadata[\"superseded_by\"].length > 0\n ? (metadata[\"superseded_by\"] as string)\n : null,\n // THE DATABASE ROW. `core/room-events.ts` marks its copy of the same row\n // `\"room\"`, and the store's precedence rule reads this field (MRI-A12).\n source: \"record\",\n };\n}\n\n\n\n/* ─────────────────────────────── MEET WAVE 1 — management projections (0.8) */\n\nconst RSVP_STATES: readonly RsvpState[] = [\"needs_action\", \"accepted\", \"declined\", \"tentative\"];\nconst OCCURRENCE_STATES: readonly OccurrenceState[] = [\"scheduled\", \"moved\", \"cancelled\"];\n\nfunction rsvpState(value: string | null): RsvpState {\n return RSVP_STATES.includes(value as RsvpState) ? (value as RsvpState) : \"needs_action\";\n}\n\nfunction num(row: Row, key: string): number | null {\n const value = row[key];\n return typeof value === \"number\" ? value : null;\n}\n\n/** A database timestamp as ISO text. PostgREST sends strings; a test double may send Dates. */\nfunction when(row: Row, key: string): string | null {\n const value = row[key];\n if (value instanceof Date) return value.toISOString();\n return text(row, key);\n}\n\nexport function projectInvitee(row: Row): MeetingInvitee {\n const operation = \"meet.projectInvitee\";\n const userId = text(row, \"invitee_user_id\");\n return {\n id: requireText(row, \"id\", operation),\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n userId: userId === null ? null : asUserId(userId),\n email: text(row, \"email\"),\n displayName: text(row, \"display_name\"),\n role: text(row, \"role\") === \"cohost\" ? \"cohost\" : \"invitee\",\n rsvpState: rsvpState(text(row, \"rsvp_state\")),\n respondedAt: when(row, \"responded_at\"),\n rsvpNote: text(row, \"rsvp_note\"),\n lastSentSequence: num(row, \"last_sent_sequence\"),\n lastSentAt: when(row, \"last_sent_at\"),\n };\n}\n\nexport function projectOccurrence(row: Row): MeetingOccurrence {\n const operation = \"meet.projectOccurrence\";\n const state = text(row, \"state\");\n return {\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n originalStart: when(row, \"original_start\") ?? requireText(row, \"original_start\", operation),\n occurrenceStart: when(row, \"occurrence_start\") ?? requireText(row, \"occurrence_start\", operation),\n durationMinutes: num(row, \"duration_minutes\"),\n // An unknown state is shown as scheduled, never dropped: the row is real.\n state: OCCURRENCE_STATES.includes(state as OccurrenceState) ? (state as OccurrenceState) : \"scheduled\",\n sequence: num(row, \"sequence\") ?? 0,\n };\n}\n\nexport function projectUpcoming(row: Row): UpcomingOccurrence {\n const operation = \"meet.projectUpcoming\";\n const kind = text(row, \"kind\");\n const myRole = text(row, \"my_role\");\n const myRsvp = text(row, \"my_rsvp\");\n return {\n ...projectOccurrence(row),\n organizationId: asOrganizationId(requireText(row, \"organization_id\", operation)),\n title: text(row, \"title\") ?? \"Meeting\",\n slug: requireText(row, \"slug\", operation),\n kind: kind === \"scheduled\" || kind === \"recurring\" ? (kind as MeetingKind) : \"instant\",\n hostUserId: asUserId(requireText(row, \"host_user_id\", operation)),\n timeZone: text(row, \"time_zone\") ?? \"UTC\",\n meetingCancelled: bool(row, \"meeting_cancelled\", false),\n myRsvp: myRsvp === null ? null : rsvpState(myRsvp),\n myRole: myRole === \"host\" || myRole === \"cohost\" || myRole === \"invitee\" ? myRole : null,\n };\n}\n\nexport function projectOverride(row: Row): OccurrenceOverride {\n const operation = \"meet.projectOverride\";\n return {\n id: requireText(row, \"id\", operation),\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n originalStart: when(row, \"original_start\") ?? requireText(row, \"original_start\", operation),\n state: text(row, \"state\") === \"moved\" ? \"moved\" : \"cancelled\",\n newStart: when(row, \"new_start\"),\n newDurationMinutes: num(row, \"new_duration_minutes\"),\n reason: text(row, \"reason\"),\n calendarSequence: num(row, \"calendar_sequence\") ?? 0,\n deletedAt: when(row, \"deleted_at\"),\n };\n}\n\n/** The jsonb the RSVP doors return, typed. A body that is neither shape is refused loudly. */\nexport function projectRsvpInvitation(body: unknown): RsvpInvitation {\n const operation = \"meet.projectRsvpInvitation\";\n if (typeof body !== \"object\" || body === null) {\n throw invalidResponse(operation, \"the RSVP door returned no object\");\n }\n const row = body as Row;\n if (row[\"ok\"] !== true) {\n return {\n ok: false,\n message: text(row, \"message\") ?? \"This invitation link is no longer valid — ask the host to send it again.\",\n };\n }\n const meeting = (row[\"meeting\"] ?? {}) as Row;\n const invitee = (row[\"invitee\"] ?? {}) as Row;\n return {\n ok: true,\n meeting: {\n title: text(meeting, \"title\") ?? \"Meeting\",\n slug: requireText(meeting, \"slug\", operation),\n scheduledFor: when(meeting, \"scheduled_for\"),\n durationMinutes: num(meeting, \"duration_minutes\"),\n timeZone: text(meeting, \"time_zone\") ?? \"UTC\",\n agenda: text(meeting, \"agenda\"),\n recurrenceRule: text(meeting, \"recurrence_rule\"),\n cancelledAt: when(meeting, \"cancelled_at\"),\n cancellationReason: text(meeting, \"cancellation_reason\"),\n endedAt: when(meeting, \"ended_at\"),\n },\n invitee: {\n displayName: text(invitee, \"display_name\"),\n email: text(invitee, \"email\"),\n rsvpState: rsvpState(text(invitee, \"rsvp_state\")),\n respondedAt: when(invitee, \"responded_at\"),\n },\n };\n}\n\n/** Settings → the door's snake_case object, carrying ONLY what was set. */\nexport function settingsPatch(settings: MeetingSettings): Record<string, unknown> {\n const patch: Record<string, unknown> = {};\n if (settings.lobbyEnabled !== undefined) patch[\"lobby_enabled\"] = settings.lobbyEnabled;\n if (settings.recordingPolicy !== undefined) patch[\"recording_policy\"] = settings.recordingPolicy;\n if (settings.aiEnabled !== undefined) patch[\"ai_enabled\"] = settings.aiEnabled;\n if (settings.joinBeforeHost !== undefined) patch[\"join_before_host\"] = settings.joinBeforeHost;\n return patch;\n}\n\n/**\n * A partial edit → the `meet_update_meeting` patch. `undefined` = leave alone,\n * `null` = clear — the difference a JSON patch exists to carry.\n */\nexport function changesPatch(changes: MeetingChanges): Record<string, unknown> {\n const patch = settingsPatch(changes);\n if (changes.title !== undefined) patch[\"title\"] = changes.title;\n if (changes.agenda !== undefined) patch[\"agenda\"] = changes.agenda;\n if (changes.timeZone !== undefined) patch[\"time_zone\"] = changes.timeZone;\n if (changes.scheduledFor !== undefined) patch[\"scheduled_for\"] = changes.scheduledFor;\n if (changes.scheduledDurationMinutes !== undefined) {\n patch[\"scheduled_duration_minutes\"] = changes.scheduledDurationMinutes;\n }\n if (changes.recurrenceRule !== undefined) patch[\"recurrence_rule\"] = changes.recurrenceRule;\n if (changes.keepExceptions !== undefined) patch[\"keep_exceptions\"] = changes.keepExceptions;\n return patch;\n}\n\nexport function createMeetRepository(options: RepositoryOptions): MeetRepository {\n // THE ONE NARROWING, in-package. Everything below works against the rich\n // internal contract; no consumer ever has to satisfy it.\n const db = (options.client as SupabaseLike).schema(MEET_SCHEMA);\n\n /**\n * 🚨 A `RETURNS void` RPC RESOLVES TO `null`, AND THAT IS SUCCESS.\n *\n * `guard` below treats a null `data` as an invalid response, which is right\n * for every RPC that returns a row and CATASTROPHIC for one that returns\n * nothing: `communication.meet_admit_participant` and `meet_record_consent`\n * are both `RETURNS void`, so from the day this package shipped, admitting\n * somebody from the lobby and recording consent BOTH performed their write\n * and then threw \"the database returned no row\" on the way back — the write\n * landed, the client believed it failed, the broadcast that tells the guest\n * never went out, and the guest sat in the waiting room forever. Found in a\n * browser (MRI-A11); the RPC return types are pinned in `VOID_RPCS` below so\n * a future void RPC cannot be wired to the wrong guard by accident.\n */\n async function guardVoid(\n operation: string,\n run: () => PromiseLike<{ data: unknown; error: { message: string; code?: string } | null }>,\n ): Promise<void> {\n let response = await run();\n if (response.error !== null) {\n const normalized = normalizeMeetError(response.error, operation);\n if (normalized.code === \"session-unavailable\" && options.session !== undefined) {\n await options.session();\n response = await run();\n }\n if (response.error !== null) throw normalizeMeetError(response.error, operation);\n }\n // No row is expected and none is read. The absence of an error IS the\n // result — anything else would be inventing a postcondition the database\n // never promised.\n }\n\n /**\n * The link's current run. It adds to the lookup and never replaces it: a failed read leaves the\n * run UNKNOWN (undefined) and the join gate still decides on Join — an unknown is never guessed.\n */\n async function readLinkRun(slug: string): Promise<LinkRunState | undefined> {\n try {\n const response = await db.rpc<Row>(RPCS.linkState, { p_slug: slug });\n return projectLinkRun(response.error === null ? response.data : null);\n } catch {\n return undefined;\n }\n }\n\n async function guard<T>(operation: string, run: () => PromiseLike<{ data: T | null; error: { message: string; code?: string } | null }>): Promise<T> {\n let response = await run();\n if (response.error !== null) {\n const normalized = normalizeMeetError(response.error, operation);\n // ONE retry, and only for the sign-in gap. A permission denial is NEVER\n // retried — retrying one is how a request storm is built.\n if (normalized.code === \"session-unavailable\" && options.session !== undefined) {\n await options.session();\n response = await run();\n }\n if (response.error !== null) throw normalizeMeetError(response.error, operation);\n }\n if (response.data === null) {\n throw invalidResponse(operation, \"the database returned no row\");\n }\n return response.data;\n }\n\n function projectInvite(row: Row): CallInvite {\n const operation = \"meet.projectInvite\";\n const mode = text(row, \"mode\");\n return {\n id: asCallId(requireText(row, \"id\", operation)),\n organizationId: asOrganizationId(requireText(row, \"organization_id\", operation)),\n roomName: asRoomName(requireText(row, \"room_name\", operation)),\n mode: mode === \"video\" ? \"video\" : \"audio\",\n callerUserId: asUserId(requireText(row, \"caller_user_id\", operation)),\n callerName: text(row, \"caller_name\") ?? \"Someone\",\n callerAvatarUrl: text(row, \"caller_avatar_url\"),\n calleeUserIds: stringArray(row, \"callee_ids\").map(asUserId),\n conversationId: text(row, \"conversation_id\"),\n createdAt: requireText(row, \"created_at\", operation),\n expiresAt: text(row, \"expires_at\") ?? requireText(row, \"created_at\", operation),\n state: callState(text(row, \"state\")),\n declineMessage: text(row, \"decline_message\"),\n };\n }\n\n function projectMeeting(row: Row): MeetingRecord {\n const operation = \"meet.projectMeeting\";\n const kind = text(row, \"kind\");\n const policy = text(row, \"recording_policy\");\n const duration = row[\"scheduled_duration_minutes\"];\n const metadata = row[\"metadata\"];\n return {\n id: asMeetingId(requireText(row, \"id\", operation)),\n organizationId: asOrganizationId(requireText(row, \"organization_id\", operation)),\n roomName: asRoomName(requireText(row, \"room_name\", operation)),\n slug: requireText(row, \"slug\", operation),\n title: text(row, \"title\") ?? \"Meeting\",\n kind: kind === \"scheduled\" || kind === \"recurring\" ? (kind as MeetingKind) : \"instant\",\n hostUserId: asUserId(requireText(row, \"host_user_id\", operation)),\n scheduledFor: text(row, \"scheduled_for\"),\n scheduledDurationMinutes: typeof duration === \"number\" ? duration : null,\n startedAt: text(row, \"started_at\"),\n endedAt: text(row, \"ended_at\"),\n locked: bool(row, \"locked\", false),\n lobbyEnabled: bool(row, \"lobby_enabled\", true),\n recordingPolicy:\n policy === \"disabled\" || policy === \"always-on\"\n ? (policy as RecordingPolicy)\n : \"host-controlled\",\n aiEnabled: bool(row, \"ai_enabled\", true),\n metadata:\n typeof metadata === \"object\" && metadata !== null && !Array.isArray(metadata)\n ? (metadata as JsonObject)\n : {},\n timeZone: text(row, \"time_zone\") ?? \"UTC\",\n agenda: text(row, \"agenda\"),\n recurrenceRule: text(row, \"recurrence_rule\"),\n joinBeforeHost: bool(row, \"join_before_host\", true),\n cancelledAt: text(row, \"cancelled_at\"),\n cancellationReason: text(row, \"cancellation_reason\"),\n deletedAt: text(row, \"deleted_at\"),\n calendarSequence: typeof row[\"calendar_sequence\"] === \"number\" ? row[\"calendar_sequence\"] : 0,\n version: typeof row[\"version\"] === \"number\" ? row[\"version\"] : 1,\n };\n }\n\n function projectRecording(row: Row): RecordingRecord {\n const operation = \"meet.projectRecording\";\n const state = text(row, \"state\");\n const known: readonly RecordingState[] = [\n \"idle\",\n \"starting\",\n \"recording\",\n \"stopping\",\n \"processing\",\n \"available\",\n \"expired\",\n \"failed\",\n ];\n return {\n id: requireText(row, \"id\", operation),\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n state: known.includes(state as RecordingState) ? (state as RecordingState) : \"failed\",\n startedAt: text(row, \"started_at\"),\n endedAt: text(row, \"ended_at\"),\n startedBy: text(row, \"started_by\") === null ? null : asUserId(text(row, \"started_by\") as string),\n // A PLATFORM file id, never a provider URL (R3). A recording that only\n // exists as a LiveKit URL is orphaned the day the room is garbage\n // collected, which is the whole reason this column is not `url`.\n fileId: text(row, \"file_id\"),\n failureReason: text(row, \"failure_reason\"),\n retention: projectRecordingRetention(row[\"metadata\"]),\n };\n }\n\n function projectRecordingRetention(value: unknown): RecordingRetention | null {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return null;\n const retention = (value as Record<string, unknown>)[\"retention\"];\n if (typeof retention !== \"object\" || retention === null || Array.isArray(retention)) return null;\n const row = retention as Record<string, unknown>;\n const phase = row[\"phase\"];\n const operationId = row[\"operation_id\"];\n const policyId = row[\"policy_id\"];\n const policyUpdatedAt = row[\"policy_updated_at\"];\n const dueAt = row[\"due_at\"];\n const expiredAt = row[\"expired_at\"];\n if (\n (phase !== \"warning\" && phase !== \"expired\") ||\n typeof operationId !== \"string\" ||\n typeof policyId !== \"string\" ||\n typeof policyUpdatedAt !== \"string\" ||\n typeof dueAt !== \"string\" ||\n (expiredAt !== null && typeof expiredAt !== \"string\")\n ) return null;\n return { operationId, policyId, policyUpdatedAt, phase, dueAt, expiredAt };\n }\n\n function projectAttendee(row: Row): MeetingAttendee {\n const operation = \"meet.projectAttendee\";\n const role = text(row, \"role\");\n const admission = text(row, \"admission_state\");\n const known: readonly ParticipantRole[] = [\"host\", \"cohost\", \"presenter\", \"participant\", \"viewer\"];\n const states: readonly AdmissionState[] = [\n \"waiting_host\",\n \"knocking\",\n \"admitted\",\n \"denied\",\n \"expired\",\n \"withdrawn\",\n \"removed\",\n \"ended\",\n ];\n const userId = text(row, \"participant_user_id\");\n const identity = requireText(row, \"identity\", operation);\n return {\n id: requireText(row, \"id\", operation),\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n identity: asParticipantIdentity(identity),\n // A nameless attendee is a row nobody can read. The identity is a worse\n // name than a real one and an infinitely better one than a blank.\n displayName: text(row, \"display_name\") ?? identity,\n avatarUrl: text(row, \"avatar_url\"),\n userId: userId === null ? null : asUserId(userId),\n role: known.includes(role as ParticipantRole) ? (role as ParticipantRole) : \"participant\",\n admissionState: states.includes(admission as AdmissionState)\n ? (admission as AdmissionState)\n : \"admitted\",\n knockedAt: text(row, \"knocked_at\"),\n knockExpiresAt: text(row, \"knock_expires_at\"),\n // The list read selects `metadata->>knock_flag`; a Postgres Changes row carries `metadata`.\n knockFlag:\n text(row, \"knock_flag\") ??\n (typeof row[\"metadata\"] === \"object\" && row[\"metadata\"] !== null\n ? text(row[\"metadata\"] as Row, \"knock_flag\")\n : null),\n isAgent: bool(row, \"is_agent\", false),\n joinedAt: text(row, \"joined_at\"),\n leftAt: text(row, \"left_at\"),\n sessions: projectStays(row[\"sessions\"]),\n };\n }\n\n function requireOrg(organizationId: OrganizationId, operation: string): void {\n if (typeof organizationId !== \"string\" || organizationId.length === 0) {\n throw new MeetError(\n \"misconfigured\",\n `${operation}: no organization_id`,\n \"Every meet write carries an explicit organization_id (R5). The package refuses the \" +\n \"write rather than letting an unscoped row reach the database.\",\n );\n }\n }\n\n\n /**\n * S5 (CORE-DESIGN §2.3): the note-taker and intelligence state lives on the RUN\n * (`meet_sessions.note_taker`), never on the meeting row. Every reader of\n * `meeting.metadata.note_taker` (record view's wrap-up status, the note-taker line) reads\n * the meeting's CURRENT run through this one seam — the open run, else the latest. A\n * reader with no grant on runs (a guest) keeps the row as it is; their state arrives on\n * the room's `matrx.meet.note_taker` event.\n */\n async function withRunNoteTaker(meeting: MeetingRecord): Promise<MeetingRecord> {\n const response = await db\n .from<Row>(TABLES.sessions)\n .select(\"note_taker,started_at\")\n .eq(\"meeting_id\", meeting.id)\n .order(\"started_at\", { ascending: false })\n .limit(1);\n const run = response.error === null ? (response.data ?? [])[0] : undefined;\n const noteTaker = run?.[\"note_taker\"];\n if (typeof noteTaker !== \"object\" || noteTaker === null || Array.isArray(noteTaker)) {\n return meeting;\n }\n return {\n ...meeting,\n metadata: { ...(meeting.metadata ?? {}), note_taker: noteTaker } as MeetingRecord[\"metadata\"],\n };\n }\n\n return {\n schema: MEET_SCHEMA,\n tables: TABLES,\n projectInvite,\n projectMeeting,\n projectSegment,\n projectNote,\n projectRecording,\n projectAttendee,\n projectInvitee,\n\n async scheduleMeeting(input) {\n requireOrg(input.organizationId, \"meet.scheduleMeeting\");\n const row = await guard<Row>(\"meet.scheduleMeeting\", () =>\n db.rpc<Row>(RPCS.scheduleMeeting, {\n p_organization_id: input.organizationId,\n p_host_user_id: input.hostUserId,\n p_title: input.title,\n p_scheduled_for: input.scheduledFor,\n p_time_zone: input.timeZone,\n p_duration_minutes: input.durationMinutes ?? null,\n p_agenda: input.agenda ?? null,\n p_recurrence_rule: input.recurrenceRule ?? null,\n p_settings: scheduleSettingsPatch(input.settings ?? {}, input.appPanel, input.observers),\n }),\n );\n return projectMeeting(row);\n },\n\n async updateMeeting(meetingId, changes, options = {}) {\n const row = await guard<Row>(\"meet.updateMeeting\", () =>\n db.rpc<Row>(RPCS.updateMeeting, {\n p_meeting_id: meetingId,\n p_changes: changesPatch(changes),\n p_expected_version: options.expectedVersion ?? null,\n p_by_user_id: options.byUserId ?? null,\n }),\n );\n return projectMeeting(row);\n },\n\n async cancelMeeting(meetingId, reason, byUserId = null) {\n const row = await guard<Row>(\"meet.cancelMeeting\", () =>\n db.rpc<Row>(RPCS.cancelMeeting, {\n p_meeting_id: meetingId,\n p_reason: reason,\n p_by_user_id: byUserId,\n }),\n );\n return projectMeeting(row);\n },\n\n async archiveMeeting(meetingId, byUserId = null) {\n const row = await guard<Row>(\"meet.archiveMeeting\", () =>\n db.rpc<Row>(RPCS.archiveMeeting, { p_meeting_id: meetingId, p_by_user_id: byUserId }),\n );\n return projectMeeting(row);\n },\n\n async restoreMeeting(meetingId, byUserId = null) {\n const row = await guard<Row>(\"meet.restoreMeeting\", () =>\n db.rpc<Row>(RPCS.restoreMeeting, { p_meeting_id: meetingId, p_by_user_id: byUserId }),\n );\n return projectMeeting(row);\n },\n\n async setOccurrence(args) {\n const row = await guard<Row>(\"meet.setOccurrence\", () =>\n db.rpc<Row>(RPCS.setOccurrence, {\n p_meeting_id: args.meetingId,\n p_original_start: args.originalStart,\n p_action: args.action,\n p_new_start: args.newStart ?? null,\n p_new_duration_minutes: args.newDurationMinutes ?? null,\n p_reason: args.reason ?? null,\n p_by_user_id: args.byUserId ?? null,\n }),\n );\n return projectOverride(row);\n },\n\n async meetingOccurrences(meetingId, { from, to, limit = 200 }) {\n const rows = await guard<Row[]>(\"meet.meetingOccurrences\", () =>\n db.rpc<Row[]>(RPCS.meetingOccurrences, {\n p_meeting_id: meetingId,\n p_from: from,\n p_to: to,\n p_limit: limit,\n }),\n );\n return rows.map(projectOccurrence);\n },\n\n async occurrencesBetween({ from, to, organizationId = null, limit = 500 }) {\n const rows = await guard<Row[]>(\"meet.occurrencesBetween\", () =>\n db.rpc<Row[]>(RPCS.occurrencesBetween, {\n p_from: from,\n p_to: to,\n p_organization_id: organizationId,\n p_limit: limit,\n }),\n );\n return rows.map(projectUpcoming);\n },\n\n async invitees(meetingId) {\n const response = await db\n .from<Row>(TABLES.invitees)\n .select(INVITEE_COLUMNS)\n .eq(\"meeting_id\", meetingId)\n .is(\"deleted_at\", null)\n .order(\"created_at\", { ascending: true })\n .order(\"id\", { ascending: true });\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.invitees\");\n return (response.data ?? []).map(projectInvitee);\n },\n\n async addInvitees(meetingId, people, byUserId = null) {\n const rows = await guard<Row[]>(\"meet.addInvitees\", () =>\n db.rpc<Row[]>(RPCS.addInvitees, {\n p_meeting_id: meetingId,\n p_invitees: people.map((person) => ({\n ...(person.userId !== undefined ? { user_id: person.userId } : {}),\n ...(person.email !== undefined ? { email: person.email } : {}),\n ...(person.displayName !== undefined ? { display_name: person.displayName } : {}),\n ...(person.role !== undefined ? { role: person.role } : {}),\n })),\n p_by_user_id: byUserId,\n }),\n );\n return rows.map(projectInvitee);\n },\n\n async setInviteeRole(inviteeId, role, byUserId = null) {\n const row = await guard<Row>(\"meet.setInviteeRole\", () =>\n db.rpc<Row>(RPCS.setInviteeRole, {\n p_invitee_id: inviteeId,\n p_role: role,\n p_by_user_id: byUserId,\n }),\n );\n return projectInvitee(row);\n },\n\n async removeInvitee(inviteeId, byUserId = null) {\n const row = await guard<Row>(\"meet.removeInvitee\", () =>\n db.rpc<Row>(RPCS.removeInvitee, { p_invitee_id: inviteeId, p_by_user_id: byUserId }),\n );\n return projectInvitee(row);\n },\n\n async respond(meetingId, answer, note = null) {\n const row = await guard<Row>(\"meet.respond\", () =>\n db.rpc<Row>(RPCS.respond, {\n p_meeting_id: meetingId,\n p_response: answer,\n p_note: note,\n p_by_user_id: null,\n }),\n );\n return projectInvitee(row);\n },\n\n async invitationByToken(secret) {\n const body = await guard<unknown>(\"meet.invitationByToken\", () =>\n db.rpc<unknown>(RPCS.invitationByToken, { p_secret: secret }),\n );\n return projectRsvpInvitation(body);\n },\n\n async respondByToken(secret, answer, note = null) {\n const body = await guard<unknown>(\"meet.respondByToken\", () =>\n db.rpc<unknown>(RPCS.respondByToken, {\n p_secret: secret,\n p_response: answer,\n p_note: note,\n }),\n );\n return projectRsvpInvitation(body);\n },\n\n async attendees(meetingId) {\n const response = await db\n .from<Row>(TABLES.participants)\n .select(ATTENDEE_COLUMNS)\n .eq(\"meeting_id\", meetingId)\n .order(\"joined_at\", { ascending: true })\n .order(\"id\", { ascending: true });\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.attendees\");\n return (response.data ?? []).map(projectAttendee);\n },\n\n async createCallInvite(input) {\n requireOrg(input.organizationId, \"meet.createCallInvite\");\n const row = await guard<Row>(\"meet.createCallInvite\", () =>\n db.rpc<Row>(RPCS.createCallInvite, {\n p_room_name: input.roomName,\n p_mode: input.mode,\n p_caller_user_id: input.callerUserId,\n p_caller_name: input.callerName,\n p_caller_avatar_url: input.callerAvatarUrl,\n p_callee_ids: input.calleeUserIds,\n p_organization_id: input.organizationId,\n p_conversation_id: input.conversationId,\n p_expires_at: input.expiresAt,\n }),\n );\n return projectInvite(row);\n },\n\n async settleCallInvite(id, state, declineMessage) {\n const row = await guard<Row>(\"meet.settleCallInvite\", () =>\n db.rpc<Row>(RPCS.settleCallInvite, {\n p_invite_id: id,\n p_state: state,\n p_decline_message: declineMessage,\n }),\n );\n return projectInvite(row);\n },\n\n async pendingInvites(userId) {\n const rows = await guard<Row[]>(\"meet.pendingInvites\", () =>\n db.rpc<Row[]>(RPCS.pendingCallInvites, { p_user_id: userId }),\n );\n return rows.map(projectInvite);\n },\n\n async getOrCreateMeeting(input) {\n requireOrg(input.organizationId, \"meet.getOrCreateMeeting\");\n const row = await guard<Row>(\"meet.getOrCreateMeeting\", () =>\n db.rpc<Row>(RPCS.getOrCreateMeeting, {\n p_organization_id: input.organizationId,\n p_host_user_id: input.hostUserId,\n p_title: input.title,\n p_kind: input.kind,\n p_scheduled_for: input.scheduledFor ?? null,\n p_scheduled_duration_minutes: input.scheduledDurationMinutes ?? null,\n p_lobby_enabled: input.lobbyEnabled ?? true,\n p_recording_policy: input.recordingPolicy ?? \"host-controlled\",\n p_ai_enabled: input.aiEnabled ?? true,\n p_slug: input.slug ?? null,\n }),\n );\n return projectMeeting(row);\n },\n\n async meetingBySlug(slug) {\n // A DURABLE LINK RESOLVES BEFORE, DURING, AND AFTER (R3). This RPC is not\n // filtered by `ended_at is null` on purpose: a link that dies the moment\n // the meeting ends breaks every \"here are the notes\" follow-up.\n const [row, linkRun] = await Promise.all([\n guard<Row>(\"meet.meetingBySlug\", () => db.rpc<Row>(RPCS.meetingBySlug, { p_slug: slug })),\n readLinkRun(slug),\n ]);\n // AWAITED: spreading the un-awaited Promise dropped every field but `linkRun` (no roomName → the\n // room page crashed on `meet-room requires part roomName`).\n const record = await withRunNoteTaker(projectMeeting(row));\n return linkRun === undefined ? record : { ...record, linkRun };\n },\n\n async meeting(id) {\n const response = await db\n .from<Row>(TABLES.meetings)\n .select(MEETING_COLUMNS)\n .eq(\"id\", id)\n .single();\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.meeting\");\n if (response.data === null) throw invalidResponse(\"meet.meeting\", \"no such meeting\");\n return withRunNoteTaker(projectMeeting(response.data));\n },\n\n async listMeetings({ organizationId = null, limit = 25, before = null }) {\n let query = db\n .from<Row>(TABLES.meetings)\n .select(MEETING_COLUMNS);\n if (organizationId !== null) query = query.eq(\"organization_id\", organizationId);\n query = query\n .order(\"scheduled_for\", { ascending: false })\n .order(\"id\", { ascending: false })\n // ASK FOR ONE MORE THAN THE PAGE, so `hasMore` is a fact rather than a\n // guess from \"we got a full page\".\n .limit(limit + 1);\n if (before !== null) {\n query = query.or(\n `scheduled_for.lt.${before.scheduledFor},and(scheduled_for.eq.${before.scheduledFor},id.lt.${before.id})`,\n );\n }\n const response = await query;\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.listMeetings\");\n const rows = response.data ?? [];\n return {\n items: rows.slice(0, limit).map(projectMeeting),\n hasMore: rows.length > limit,\n };\n },\n\n async decideAdmission({ meetingId, identity, decision, byUserId }) {\n const fn = decision === \"admit\" ? RPCS.admit : decision === \"deny\" ? RPCS.deny : RPCS.undeny;\n const row = await guard<Row>(`meet.${decision}`, () =>\n db.rpc<Row>(fn, {\n p_meeting_id: meetingId,\n p_identity: identity,\n p_by_user_id: byUserId,\n }),\n );\n return projectAttendee(row);\n },\n\n async admitAll(meetingId, byUserId) {\n const response = await db.rpc<Row[]>(RPCS.admitAll, {\n p_meeting_id: meetingId,\n p_by_user_id: byUserId,\n });\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.admitAll\");\n const rows = Array.isArray(response.data) ? (response.data as Row[]) : [];\n return rows.map(projectAttendee);\n },\n\n async setLock(meetingId, locked, byUserId) {\n const row = await guard<Row>(\"meet.setLock\", () =>\n db.rpc<Row>(RPCS.setLock, {\n p_meeting_id: meetingId,\n p_locked: locked,\n p_by_user_id: byUserId,\n }),\n );\n return projectMeeting(row);\n },\n\n async meetingRule(meetingId, key) {\n const response = await db.rpc<unknown>(RPCS.meetingRule, { p_meeting_id: meetingId, p_key: key });\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.meetingRule\");\n return response.data;\n },\n\n async setMeetingRule(meetingId, key, value) {\n const response = await db.rpc<Row>(RPCS.setMeetingRule, { p_meeting_id: meetingId, p_key: key, p_value: value });\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.setMeetingRule\");\n const answer = (response.data ?? {}) as Row;\n if (answer[\"ok\"] !== true) {\n throw new MeetError(\n \"forbidden\",\n typeof answer[\"detail\"] === \"string\" ? answer[\"detail\"] : \"This meeting's rule was not changed.\",\n \"Only the meeting's host or a co-host sets its rules; an organization can lock a rule for everyone.\",\n answer,\n );\n }\n return answer[\"effective_value\"];\n },\n\n async transcript(meetingId, limit = 1000) {\n const response = await db\n .from<Row>(TABLES.transcripts)\n .select(SEGMENT_COLUMNS)\n .eq(\"meeting_id\", meetingId)\n .order(\"started_at\", { ascending: true })\n .order(\"id\", { ascending: true })\n .limit(limit);\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.transcript\");\n return (response.data ?? []).map(projectSegment);\n },\n\n async notes(meetingId) {\n const response = await db\n .from<Row>(TABLES.notes)\n .select(NOTE_COLUMNS)\n .eq(\"meeting_id\", meetingId)\n .order(\"created_at\", { ascending: true })\n .order(\"id\", { ascending: true });\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.notes\");\n return (response.data ?? []).map(projectNote);\n },\n\n async recordConsent({ meetingId, identity, acknowledgedAt }) {\n await guardVoid(\"meet.recordConsent\", () =>\n db.rpc<Row>(RPCS.recordConsent, {\n p_meeting_id: meetingId,\n p_identity: identity,\n p_acknowledged_at: acknowledgedAt,\n }),\n );\n },\n\n async recordings(meetingId) {\n const response = await db\n .from<Row>(TABLES.recordings)\n .select(RECORDING_COLUMNS)\n .eq(\"meeting_id\", meetingId)\n // Meet wave 3: an archived recording is off the record as well as the library.\n .is(\"deleted_at\", null)\n .order(\"started_at\", { ascending: false });\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.recordings\");\n return (response.data ?? []).map(projectRecording);\n },\n\n async myRecordings({ includeArchived = false, limit = 200 } = {}) {\n const rows = await guard<Row[]>(\"meet.myRecordings\", () =>\n db.rpc<Row[]>(RPCS.myRecordings, { p_include_archived: includeArchived, p_limit: limit }),\n );\n return rows.map(projectLibraryRecording);\n },\n\n async searchMeetings(query, limit = 60) {\n if (query.trim().length === 0) return [];\n const rows = await guard<Row[]>(\"meet.searchMeetings\", () =>\n db.rpc<Row[]>(RPCS.search, { p_query: query, p_limit: limit }),\n );\n return rows.map(projectSearchHit);\n },\n\n async renameRecording(recordingId, title) {\n const clean = title === null ? null : title.trim().slice(0, 200) || null;\n const response = await db\n .from<Row>(TABLES.recordings)\n .update({ title: clean })\n .eq(\"id\", recordingId)\n .select(\"id\");\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.renameRecording\");\n if ((response.data ?? []).length === 0) {\n throw normalizeMeetError(\n { message: \"permission denied: only the meeting's host or an editor can rename it\", code: \"42501\" },\n \"meet.renameRecording\",\n );\n }\n },\n\n async setRecordingArchived(recordingId, archived) {\n const response = await db\n .from<Row>(TABLES.recordings)\n .update({ deleted_at: archived ? new Date().toISOString() : null })\n .eq(\"id\", recordingId)\n .select(\"id\");\n if (response.error !== null) throw normalizeMeetError(response.error, \"meet.setRecordingArchived\");\n if ((response.data ?? []).length === 0) {\n throw normalizeMeetError(\n { message: \"permission denied: only the meeting's host or an editor can archive it\", code: \"42501\" },\n \"meet.setRecordingArchived\",\n );\n }\n },\n };\n}\n\n/** One row of the recordings library (`meet_my_recordings`). */\nexport interface LibraryRecording {\n readonly id: string;\n readonly meetingId: MeetingId;\n readonly meetingTitle: string;\n readonly meetingSlug: string;\n /** The reader's strongest role on the meeting. */\n readonly role: \"host\" | \"cohost\" | \"invitee\" | \"attendee\";\n readonly state: RecordingState;\n /** A name a host gave it; null = show the meeting title. */\n readonly title: string | null;\n readonly fileId: string | null;\n readonly startedAt: string | null;\n readonly endedAt: string | null;\n readonly durationMs: number | null;\n readonly sizeBytes: number | null;\n readonly failureReason: string | null;\n readonly archivedAt: string | null;\n readonly organizationId: OrganizationId;\n}\n\nconst LIBRARY_STATES: readonly RecordingState[] = [\n \"idle\",\n \"starting\",\n \"recording\",\n \"stopping\",\n \"processing\",\n \"available\",\n \"expired\",\n \"failed\",\n];\n\nexport function projectLibraryRecording(row: Row): LibraryRecording {\n const operation = \"meet.projectLibraryRecording\";\n const state = text(row, \"state\");\n const role = text(row, \"my_role\");\n return {\n id: requireText(row, \"recording_id\", operation),\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n meetingTitle: text(row, \"meeting_title\") ?? \"Meeting\",\n meetingSlug: requireText(row, \"meeting_slug\", operation),\n role: role === \"host\" || role === \"cohost\" || role === \"invitee\" ? role : \"attendee\",\n state: LIBRARY_STATES.includes(state as RecordingState) ? (state as RecordingState) : \"failed\",\n title: text(row, \"recording_title\"),\n fileId: text(row, \"file_id\"),\n startedAt: text(row, \"started_at\"),\n endedAt: text(row, \"ended_at\"),\n durationMs: num(row, \"duration_ms\"),\n sizeBytes: num(row, \"size_bytes\"),\n failureReason: text(row, \"failure_reason\"),\n archivedAt: text(row, \"deleted_at\"),\n organizationId: asOrganizationId(requireText(row, \"organization_id\", operation)),\n };\n}\n\n/** Where a search hit was found. */\nexport type MeetingSearchSource =\n | \"title\"\n | \"summary\"\n | \"decision\"\n | \"action_item\"\n | \"note\"\n | \"transcript\";\n\n/** One hit from `meet_search`. `snippet` marks matched words with ⟦ ⟧. */\nexport interface MeetingSearchHit {\n readonly meetingId: MeetingId;\n readonly meetingTitle: string;\n readonly meetingSlug: string;\n readonly meetingAt: string | null;\n readonly source: MeetingSearchSource;\n /** The note or transcript segment id (the meeting id for a title hit). */\n readonly refId: string;\n readonly speaker: string | null;\n readonly snippet: string;\n /** When it was said / written. */\n readonly at: string | null;\n readonly rank: number;\n}\n\nconst SEARCH_SOURCES: readonly MeetingSearchSource[] = [\n \"title\",\n \"summary\",\n \"decision\",\n \"action_item\",\n \"note\",\n \"transcript\",\n];\n\nexport function projectSearchHit(row: Row): MeetingSearchHit {\n const operation = \"meet.projectSearchHit\";\n const source = text(row, \"source\");\n return {\n meetingId: asMeetingId(requireText(row, \"meeting_id\", operation)),\n meetingTitle: text(row, \"meeting_title\") ?? \"Meeting\",\n meetingSlug: requireText(row, \"meeting_slug\", operation),\n meetingAt: text(row, \"meeting_at\"),\n source: SEARCH_SOURCES.includes(source as MeetingSearchSource)\n ? (source as MeetingSearchSource)\n : \"note\",\n refId: requireText(row, \"ref_id\", operation),\n speaker: text(row, \"speaker\"),\n snippet: text(row, \"snippet\") ?? \"\",\n at: text(row, \"said_at\"),\n rank: num(row, \"rank\") ?? 0,\n };\n}\n\n/** A snippet split into plain and matched runs, for highlighting without HTML. */\nexport function snippetRuns(snippet: string): readonly { readonly text: string; readonly hit: boolean }[] {\n const runs: { text: string; hit: boolean }[] = [];\n const pattern = /⟦([^⟧]*)⟧/g;\n let last = 0;\n for (const match of snippet.matchAll(pattern)) {\n const start = match.index ?? 0;\n if (start > last) runs.push({ text: snippet.slice(last, start), hit: false });\n runs.push({ text: match[1] ?? \"\", hit: true });\n last = start + match[0].length;\n }\n if (last < snippet.length) runs.push({ text: snippet.slice(last), hit: false });\n return runs;\n}\n\n/** `meet_link_state` → the typed run state; anything not understood is \"unknown\", never a guess. */\nexport function projectLinkRun(value: unknown): LinkRunState | undefined {\n if (typeof value !== \"object\" || value === null) return undefined;\n const row = value as Record<string, unknown>;\n const state = row[\"state\"];\n if (state !== \"open\" && state !== \"not_started\" && state !== \"ended\" && state !== \"cancelled\") return undefined;\n const reason = row[\"reason\"] === \"ended\" || row[\"reason\"] === \"cancelled\" ? row[\"reason\"] : null;\n const nextStart = typeof row[\"next_start\"] === \"string\" ? row[\"next_start\"] : null;\n return { state, reason, nextStart };\n}\n","/**\n * THE APP PANEL SLOT (HR-360 wave 2, Meet register MD-15).\n *\n * A meeting can carry one APP PANEL: a surface the HOST APP registered under a key, opened\n * beside the call on one record (`meet_meetings.metadata.app_panel = {key, record_id}`,\n * written by `meet_schedule_meeting`'s `app_panel` setting). The package knows only the key\n * and the record id; the host app's registered component reads the record through the\n * person's OWN doors, so someone who cannot read the record sees nothing of it.\n *\n * Everyone in the call looks at the same SECTION of the panel: one shared focus per run,\n * kept by the server and carried on the existing collab packet (`collab.focus`,\n * action `focus_set`) — never a second channel.\n */\n\nimport { settingsPatch } from \"./repository\";\nimport type { MeetingRecord, MeetingSettings } from \"./types\";\n\nexport interface MeetingAppPanel {\n /** The registered surface key, e.g. `hr.review_360`. */\n readonly key: string;\n /** The record the panel opens on. */\n readonly recordId: string;\n}\n\n/** The app panel this meeting carries, or null. Malformed values are never guessed at. */\nexport function appPanelOf(meeting: Pick<MeetingRecord, \"metadata\"> | null | undefined): MeetingAppPanel | null {\n const raw = meeting?.metadata?.[\"app_panel\"];\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) return null;\n const key = (raw as Record<string, unknown>)[\"key\"];\n const recordId = (raw as Record<string, unknown>)[\"record_id\"];\n if (typeof key !== \"string\" || key.length === 0) return null;\n if (typeof recordId !== \"string\" || recordId.length === 0) return null;\n return { key, recordId };\n}\n\n/**\n * HR-360 wave 3: the server held capture off because the meeting is about a Confidential record\n * (`metadata.capture_hold = \"confidential_record\"`, stamped by the meet_meetings trigger) and it\n * is still off. The room says so in one short line. Once the organization turns capture back on\n * (knob `meet.confidential_capture`), it is no longer held.\n */\nexport function captureHeldOf(\n meeting: Pick<MeetingRecord, \"metadata\" | \"aiEnabled\" | \"recordingPolicy\"> | null | undefined,\n): boolean {\n if (!meeting) return false;\n return (\n meeting.metadata?.[\"capture_hold\"] === \"confidential_record\" &&\n !meeting.aiEnabled &&\n meeting.recordingPolicy === \"disabled\"\n );\n}\n\n/** The `p_settings` of `meet_schedule_meeting`, with the panel and the named observers. */\nexport function scheduleSettingsPatch(\n settings: MeetingSettings,\n appPanel: MeetingAppPanel | undefined,\n observers: readonly string[] | undefined,\n): Record<string, unknown> {\n const patch = settingsPatch(settings);\n if (appPanel !== undefined) patch[\"app_panel\"] = { key: appPanel.key, record_id: appPanel.recordId };\n if (observers !== undefined && observers.length > 0) patch[\"observers\"] = [...observers];\n return patch;\n}\n\n/** The one shared section focus of the meeting's app panel (`collab.focus`). */\nexport interface MeetAppFocus {\n readonly panel: string;\n readonly section: string;\n readonly byName: string;\n readonly at: string;\n}\n\nexport function focusFromRow(value: unknown): MeetAppFocus | null {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return null;\n const row = value as Record<string, unknown>;\n if (typeof row[\"panel\"] !== \"string\") return null;\n return {\n panel: row[\"panel\"],\n section: typeof row[\"section\"] === \"string\" ? row[\"section\"] : \"\",\n byName: typeof row[\"by_name\"] === \"string\" ? row[\"by_name\"] : \"\",\n at: typeof row[\"at\"] === \"string\" ? row[\"at\"] : \"\",\n };\n}\n","/**\n * MEETING ROOM ITEMS — polls, Q&A, the shared whiteboard and breakout rooms (Meet wave 5).\n *\n * The server decides every one of them (`aidream/services/meet/collab.py`,\n * `breakouts.py`): who may create, launch, vote, ask, answer, draw, erase, open rooms or\n * move people — and what each viewer SEES (a draft poll is the host's alone; counts\n * reach everyone only once shared; anonymous votes and questions carry no identity;\n * a dismissed question stays with the hosts). This file only holds what the server\n * said and folds the room's `matrx.meet.collab` packets into it.\n *\n * Two reads, one truth: `GET /v1/meet/collab` once per join (a late joiner sees every\n * poll, question, stroke and the open breakout plan), then packets. Every item is keyed\n * by its row id, so the same item arriving twice is one item.\n */\n\nimport { readServerRefusal } from \"./ai\";\nimport type { MeetApi } from \"./api\";\nimport { MEET_ROUTES, MEETING_PASS_HEADER } from \"./tokens\";\nimport type { ParticipantIdentity } from \"./types\";\nimport { focusFromRow, type MeetAppFocus } from \"./app-panel\";\n\nexport interface MeetPollOption {\n readonly id: string;\n readonly text: string;\n /** Present when this viewer may see counts (a host, or results shared). */\n readonly votes?: number | undefined;\n /** Who picked it — hosts only, never for an anonymous poll. */\n readonly voters?: readonly string[] | undefined;\n}\n\nexport type MeetPollState = \"draft\" | \"live\" | \"closed\";\n\nexport interface MeetPoll {\n readonly id: string;\n readonly question: string;\n readonly options: readonly MeetPollOption[];\n readonly multiple: boolean;\n readonly anonymous: boolean;\n readonly state: MeetPollState;\n readonly resultsShared: boolean;\n readonly voterCount: number | null;\n /** This viewer's own answer — kept locally, since a room packet cannot know it. */\n readonly myVote: readonly string[];\n readonly createdByName: string;\n readonly createdAt: string | null;\n}\n\nexport type MeetQuestionState = \"open\" | \"answered\" | \"dismissed\";\n\nexport interface MeetQuestion {\n readonly id: string;\n readonly text: string;\n readonly askerName: string;\n readonly anonymous: boolean;\n readonly state: MeetQuestionState;\n readonly answer: string | null;\n readonly answeredByName: string | null;\n readonly upvotes: number;\n readonly upvotedByMe: boolean;\n readonly mine: boolean;\n readonly createdAt: string | null;\n}\n\nexport interface MeetStroke {\n readonly id: string;\n /** Points in the board's own 0..1 space. */\n readonly points: readonly (readonly [number, number])[];\n readonly color: string;\n readonly width: number;\n readonly tool: \"pen\" | \"highlighter\";\n readonly by: string;\n readonly byName: string;\n /** True while this browser's stroke has not come back from the server. */\n readonly pending?: boolean | undefined;\n}\n\nexport interface MeetBreakoutRoom {\n readonly id: string;\n readonly name: string;\n readonly identities: readonly string[];\n}\n\nexport interface MeetBreakoutBroadcast {\n readonly text: string;\n readonly by: string;\n readonly at: string;\n}\n\nexport interface MeetBreakoutPlan {\n readonly id: string;\n readonly state: \"open\" | \"closed\";\n readonly rooms: readonly MeetBreakoutRoom[];\n readonly allowChoose: boolean;\n readonly minutes: number;\n readonly openedAt: string | null;\n readonly endsAt: string | null;\n /** Set while the host's closing countdown runs: everyone returns to the main meeting at this time. */\n readonly closingAt: string | null;\n /** `invite` (Join / Later) or `auto` (a countdown, then moved) — the org's `meet.breakout_move`. */\n readonly moveMode: \"invite\" | \"auto\";\n readonly closedAt: string | null;\n readonly broadcasts: readonly MeetBreakoutBroadcast[];\n}\n\n/** The org's knobs for this meeting — which controls EXIST (absent, never dead). */\nexport interface MeetCollabSettings {\n readonly pollsEnabled: boolean;\n readonly pollMaxOptions: number;\n readonly qaEnabled: boolean;\n readonly qaAnonymousAllowed: boolean;\n readonly whiteboardEnabled: boolean;\n readonly breakoutsEnabled: boolean;\n readonly breakoutMaxRooms: number;\n readonly breakoutDefaultMinutes: number;\n readonly captionTranslationEnabled: boolean;\n /** Empty = the viewer's own language. */\n readonly captionTranslationLanguage: string;\n}\n\nexport type CollabPanel = \"polls\" | \"qa\" | \"whiteboard\" | \"breakouts\";\n\nexport interface CollabSnapshot {\n readonly loaded: boolean;\n /** Why the state could not be read, in words — or null. */\n readonly error: string | null;\n readonly settings: MeetCollabSettings | null;\n readonly polls: readonly MeetPoll[];\n readonly questions: readonly MeetQuestion[];\n readonly strokes: readonly MeetStroke[];\n readonly breakout: MeetBreakoutPlan | null;\n /** The newest message a host sent to every breakout room, for the banner. */\n readonly broadcast: MeetBreakoutBroadcast | null;\n /** A host moved THIS person (or everyone came back): where to go. `undefined` = nothing asked. */\n readonly moveRequest: { readonly roomId: string | null; readonly at: number } | null;\n /** Something asked a panel to open (a poll launched, a question arrived for a host). */\n readonly panelRequest: { readonly panel: CollabPanel; readonly at: number } | null;\n /** Caption translations for THIS viewer: line id → text, for `captionLanguage`. */\n readonly captionLanguage: string | null;\n readonly captionTranslations: Readonly<Record<string, string>>;\n readonly captionTranslationError: string | null;\n /**\n * HR-360 wave 2 (MD-15): the meeting's app panel's ONE shared section focus for this run,\n * as the server keeps it (`focus_set`); optional so a snapshot built before 0.11 compiles.\n */\n readonly focus?: MeetAppFocus | null;\n}\n\nexport const EMPTY_COLLAB: CollabSnapshot = {\n loaded: false,\n error: null,\n settings: null,\n polls: [],\n questions: [],\n strokes: [],\n breakout: null,\n broadcast: null,\n moveRequest: null,\n panelRequest: null,\n captionLanguage: null,\n captionTranslations: {},\n captionTranslationError: null,\n focus: null,\n};\n\ntype Row = Record<string, unknown>;\n\nfunction obj(value: unknown): Row | null {\n return typeof value === \"object\" && value !== null && !Array.isArray(value) ? (value as Row) : null;\n}\nfunction str(value: unknown): string | null {\n return typeof value === \"string\" ? value : null;\n}\nfunction num(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null;\n}\nfunction strings(value: unknown): string[] {\n return Array.isArray(value) ? value.filter((v): v is string => typeof v === \"string\") : [];\n}\n\nexport function pollFromRow(row: Row): MeetPoll | null {\n const id = str(row[\"id\"]);\n if (id === null) return null;\n const options: MeetPollOption[] = [];\n for (const raw of Array.isArray(row[\"options\"]) ? row[\"options\"] : []) {\n const option = obj(raw);\n const optionId = option === null ? null : str(option[\"id\"]);\n if (option === null || optionId === null) continue;\n const votes = num(option[\"votes\"]);\n options.push({\n id: optionId,\n text: str(option[\"text\"]) ?? \"\",\n ...(votes !== null ? { votes } : {}),\n ...(Array.isArray(option[\"voters\"]) ? { voters: strings(option[\"voters\"]) } : {}),\n });\n }\n const state = str(row[\"state\"]);\n return {\n id,\n question: str(row[\"question\"]) ?? \"\",\n options,\n multiple: row[\"multiple\"] === true,\n anonymous: row[\"anonymous\"] === true,\n state: state === \"live\" || state === \"closed\" ? state : \"draft\",\n resultsShared: row[\"results_shared\"] === true,\n voterCount: num(row[\"voter_count\"]),\n myVote: strings(row[\"my_vote\"]),\n createdByName: str(row[\"created_by_name\"]) ?? \"\",\n createdAt: str(row[\"created_at\"]),\n };\n}\n\nexport function questionFromRow(row: Row): MeetQuestion | null {\n const id = str(row[\"id\"]);\n if (id === null) return null;\n const state = str(row[\"state\"]);\n return {\n id,\n text: str(row[\"text\"]) ?? \"\",\n askerName: str(row[\"asker_name\"]) ?? \"\",\n anonymous: row[\"anonymous\"] === true,\n state: state === \"answered\" || state === \"dismissed\" ? state : \"open\",\n answer: str(row[\"answer\"]),\n answeredByName: str(row[\"answered_by_name\"]),\n upvotes: num(row[\"upvotes\"]) ?? 0,\n upvotedByMe: row[\"upvoted_by_me\"] === true,\n mine: row[\"mine\"] === true,\n createdAt: str(row[\"created_at\"]),\n };\n}\n\nexport function strokeFromRow(row: Row): MeetStroke | null {\n const id = str(row[\"id\"]);\n if (id === null || !Array.isArray(row[\"points\"])) return null;\n const points: (readonly [number, number])[] = [];\n for (const raw of row[\"points\"]) {\n if (Array.isArray(raw) && typeof raw[0] === \"number\" && typeof raw[1] === \"number\") {\n points.push([raw[0], raw[1]]);\n }\n }\n return {\n id,\n points,\n color: str(row[\"color\"]) ?? \"#111827\",\n width: num(row[\"width\"]) ?? 3,\n tool: row[\"tool\"] === \"highlighter\" ? \"highlighter\" : \"pen\",\n by: str(row[\"by\"]) ?? \"\",\n byName: str(row[\"by_name\"]) ?? \"\",\n };\n}\n\nfunction broadcastFrom(value: unknown): MeetBreakoutBroadcast | null {\n const row = obj(value);\n const text = row === null ? null : str(row[\"text\"]);\n if (row === null || text === null) return null;\n return { text, by: str(row[\"by\"]) ?? \"\", at: str(row[\"at\"]) ?? \"\" };\n}\n\nexport function planFromRow(row: Row): MeetBreakoutPlan | null {\n const id = str(row[\"id\"]);\n if (id === null) return null;\n const rooms: MeetBreakoutRoom[] = [];\n for (const raw of Array.isArray(row[\"rooms\"]) ? row[\"rooms\"] : []) {\n const room = obj(raw);\n const roomId = room === null ? null : str(room[\"id\"]);\n if (room === null || roomId === null) continue;\n rooms.push({ id: roomId, name: str(room[\"name\"]) ?? roomId, identities: strings(room[\"identities\"]) });\n }\n return {\n id,\n state: row[\"state\"] === \"open\" ? \"open\" : \"closed\",\n rooms,\n allowChoose: row[\"allow_choose\"] === true,\n minutes: num(row[\"minutes\"]) ?? 0,\n openedAt: str(row[\"opened_at\"]),\n endsAt: str(row[\"ends_at\"]),\n closingAt: str(row[\"closing_at\"]),\n moveMode: row[\"move_mode\"] === \"auto\" ? \"auto\" : \"invite\",\n closedAt: str(row[\"closed_at\"]),\n broadcasts: (Array.isArray(row[\"broadcasts\"]) ? row[\"broadcasts\"] : [])\n .map(broadcastFrom)\n .filter((b): b is MeetBreakoutBroadcast => b !== null),\n };\n}\n\nexport function settingsFromRow(row: Row | null): MeetCollabSettings | null {\n if (row === null) return null;\n return {\n pollsEnabled: row[\"polls_enabled\"] === true,\n pollMaxOptions: num(row[\"poll_max_options\"]) ?? 10,\n qaEnabled: row[\"qa_enabled\"] === true,\n qaAnonymousAllowed: row[\"qa_anonymous_allowed\"] === true,\n whiteboardEnabled: row[\"whiteboard_enabled\"] === true,\n breakoutsEnabled: row[\"breakouts_enabled\"] === true,\n breakoutMaxRooms: num(row[\"breakout_max_rooms\"]) ?? 20,\n breakoutDefaultMinutes: num(row[\"breakout_default_minutes\"]) ?? 15,\n captionTranslationEnabled: row[\"caption_translation_enabled\"] === true,\n captionTranslationLanguage: str(row[\"caption_translation_language\"]) ?? \"\",\n };\n}\n\n/** The server's `GET /collab` body → a loaded snapshot (keeps this viewer's caption state). */\nexport function collabFromState(body: Row, previous: CollabSnapshot): CollabSnapshot {\n const list = <T>(key: string, parse: (row: Row) => T | null): T[] =>\n (Array.isArray(body[key]) ? body[key] : [])\n .map((raw) => {\n const row = obj(raw);\n return row === null ? null : parse(row);\n })\n .filter((item): item is T => item !== null);\n const breakoutRow = obj(body[\"breakout\"]);\n return {\n ...previous,\n loaded: true,\n error: null,\n settings: settingsFromRow(obj(body[\"settings\"])),\n polls: list(\"polls\", pollFromRow),\n questions: list(\"questions\", questionFromRow),\n strokes: list(\"strokes\", strokeFromRow),\n breakout: breakoutRow === null ? null : planFromRow(breakoutRow),\n focus: focusFromRow(body[\"focus\"]),\n };\n}\n\nfunction upsert<T extends { id: string }>(items: readonly T[], next: T): T[] {\n const index = items.findIndex((item) => item.id === next.id);\n if (index === -1) return [...items, next];\n const copy = items.slice();\n copy[index] = next;\n return copy;\n}\n\n/**\n * Fold one server item into the snapshot. `fromRoom` = it came on the room broadcast,\n * which cannot know THIS viewer's vote, upvote or authorship — those are kept from what\n * the viewer's own reads and answers already said.\n */\nexport function withItem(collab: CollabSnapshot, row: Row, fromRoom = false): CollabSnapshot {\n const kind = str(row[\"kind\"]);\n if (kind === \"poll\") {\n const poll = pollFromRow(row);\n if (poll === null) return collab;\n // A ROOM packet cannot know this viewer's vote; keep the one we already hold.\n const known = collab.polls.find((p) => p.id === poll.id);\n let merged = fromRoom && known !== undefined ? { ...poll, myVote: known.myVote } : poll;\n // A room copy carries no counts until results are shared; a HOST already holding\n // counts (from their own read or the hosts' packet) keeps them until newer ones land.\n if (fromRoom && known !== undefined && !poll.options.some((o) => o.votes !== undefined)) {\n const counted = new Map(known.options.map((o) => [o.id, o]));\n merged = {\n ...merged,\n voterCount: known.voterCount,\n options: merged.options.map((o) => {\n const had = counted.get(o.id);\n return had === undefined ? o : { ...o, votes: had.votes, voters: had.voters };\n }),\n };\n }\n return { ...collab, polls: upsert(collab.polls, merged) };\n }\n if (kind === \"question\") {\n const question = questionFromRow(row);\n if (question === null) return collab;\n const known = collab.questions.find((q) => q.id === question.id);\n // `mine` / `upvoted_by_me` are per viewer; a room packet says false for everyone.\n const merged =\n fromRoom && known !== undefined\n ? { ...question, mine: known.mine, upvotedByMe: known.upvotedByMe }\n : question;\n return { ...collab, questions: upsert(collab.questions, merged) };\n }\n if (kind === \"stroke\") {\n const stroke = strokeFromRow(row);\n if (stroke === null) return collab;\n return { ...collab, strokes: upsert(collab.strokes, stroke) };\n }\n if (kind === \"breakout\") {\n const plan = planFromRow(row);\n if (plan === null) return collab;\n return { ...collab, breakout: plan };\n }\n return collab;\n}\n\nfunction without(collab: CollabSnapshot, ids: readonly string[]): CollabSnapshot {\n const gone = new Set(ids);\n return {\n ...collab,\n polls: collab.polls.filter((p) => !gone.has(p.id)),\n questions: collab.questions.filter((q) => !gone.has(q.id)),\n strokes: collab.strokes.filter((s) => !gone.has(s.id)),\n };\n}\n\nexport interface CollabPacketContext {\n readonly localIdentity: ParticipantIdentity | null;\n /** Hosts and co-hosts receive a second, fuller packet; they ignore the room copy. */\n readonly isHost: boolean;\n readonly now: number;\n}\n\n/**\n * One `matrx.meet.collab` packet → the next snapshot, or `null` when it must be dropped\n * (with the reason in `dropped`). Pure; the room-event dispatcher patches the store.\n */\nexport function applyCollabPacket(\n collab: CollabSnapshot,\n payload: Row,\n context: CollabPacketContext,\n): { next: CollabSnapshot | null; dropped?: string } {\n const item = obj(payload[\"item\"]);\n if (item !== null) {\n const kind = str(item[\"kind\"]);\n // Every copy is folded — the room's AND the hosts' fuller one. `withItem` keeps a\n // host's counts when a count-less room copy arrives, so neither copy can blank the\n // other, and a host still sees an item the server sent the room only (a new question).\n let next = withItem(collab, item, true);\n if (kind === \"poll\" && str(item[\"state\"]) === \"live\") {\n const before = collab.polls.find((p) => p.id === str(item[\"id\"]));\n if (before === undefined || before.state !== \"live\") {\n next = { ...next, panelRequest: { panel: \"polls\", at: context.now } };\n }\n }\n if (kind === \"breakout\") {\n const plan = planFromRow(item);\n if (plan !== null && plan.state === \"closed\") {\n next = { ...next, moveRequest: { roomId: null, at: context.now } };\n }\n }\n const move = obj(payload[\"move\"]);\n if (move !== null && str(move[\"identity\"]) === context.localIdentity) {\n next = { ...next, moveRequest: { roomId: str(move[\"room_id\"]), at: context.now } };\n }\n return { next };\n }\n if (payload[\"focus\"] !== undefined) {\n const focus = focusFromRow(payload[\"focus\"]);\n return focus === null ? { next: null, dropped: \"a focus packet without a panel\" } : { next: { ...collab, focus } };\n }\n if (Array.isArray(payload[\"removed\"])) {\n return { next: without(collab, strings(payload[\"removed\"])) };\n }\n if (Array.isArray(payload[\"hidden\"])) {\n // A dismissed question leaves everyone's list — but a host keeps it (their packet\n // carries the dismissed state).\n return context.isHost ? { next: null, dropped: \"hosts keep dismissed questions\" } : { next: without(collab, strings(payload[\"hidden\"])) };\n }\n if (payload[\"cleared\"] === \"whiteboard\") {\n return { next: { ...collab, strokes: [] } };\n }\n const broadcast = broadcastFrom(payload[\"broadcast\"]);\n if (broadcast !== null) {\n return { next: { ...collab, broadcast } };\n }\n return { next: null, dropped: \"a collab packet with nothing this version understands\" };\n}\n\n/** The breakout room a person is assigned to, or null. */\nexport function assignedRoom(plan: MeetBreakoutPlan | null, identity: string | null): MeetBreakoutRoom | null {\n if (plan === null || plan.state !== \"open\" || identity === null) return null;\n return plan.rooms.find((room) => room.identities.includes(identity)) ?? null;\n}\n\n/**\n * Split people into `rooms` rooms, round-robin in the order given — Zoom's \"assign\n * automatically\". Hosts are left out (they visit); pass them in `exclude`.\n */\nexport function autoAssign(\n identities: readonly string[],\n rooms: number,\n exclude: readonly string[] = [],\n): string[][] {\n const count = Math.max(1, Math.floor(rooms));\n const out: string[][] = Array.from({ length: count }, () => []);\n const skip = new Set(exclude);\n let index = 0;\n for (const identity of identities) {\n if (skip.has(identity)) continue;\n out[index % count]!.push(identity);\n index += 1;\n }\n return out;\n}\n\n/** Milliseconds until the plan's timer ends, or null when it has none. */\nexport function breakoutRemainingMs(plan: MeetBreakoutPlan | null, now: number): number | null {\n if (plan === null || plan.state !== \"open\") return null;\n const ends = [plan.endsAt, plan.closingAt]\n .map((stamp) => (stamp === null ? Number.NaN : Date.parse(stamp)))\n .filter((time) => !Number.isNaN(time));\n if (ends.length === 0) return null;\n return Math.max(0, Math.min(...ends) - now);\n}\n\n/** Meet shows a 30 second countdown before rooms end; the host's Close starts one at once. */\nexport const BREAKOUT_COUNTDOWN_MS = 30_000;\n\n/**\n * The closing countdown to show, in ms — or null when the rooms are not closing. Closing\n * means the host asked (`closingAt`) or the timer is within its last 30 seconds.\n */\nexport function breakoutClosingMs(plan: MeetBreakoutPlan | null, now: number): number | null {\n if (plan === null || plan.state !== \"open\") return null;\n const remaining = breakoutRemainingMs(plan, now);\n if (remaining === null) return null;\n return plan.closingAt !== null || remaining <= BREAKOUT_COUNTDOWN_MS ? remaining : null;\n}\n\n/** The language translated captions start in: the knob, else the viewer's own. */\nexport function defaultCaptionLanguage(\n settings: MeetCollabSettings | null,\n viewerLanguage: string | null | undefined,\n): string {\n const configured = settings?.captionTranslationLanguage.trim() ?? \"\";\n if (configured.length > 0) return configured;\n const own = (viewerLanguage ?? \"\").trim();\n return own.length > 0 ? own : \"en\";\n}\n\n/** Languages offered in the captions menu (tags the translator understands). */\nexport const CAPTION_LANGUAGES: readonly { readonly tag: string; readonly name: string }[] = [\n { tag: \"en\", name: \"English\" },\n { tag: \"es\", name: \"Spanish\" },\n { tag: \"fr\", name: \"French\" },\n { tag: \"de\", name: \"German\" },\n { tag: \"it\", name: \"Italian\" },\n { tag: \"pt\", name: \"Portuguese\" },\n { tag: \"nl\", name: \"Dutch\" },\n { tag: \"pl\", name: \"Polish\" },\n { tag: \"ru\", name: \"Russian\" },\n { tag: \"uk\", name: \"Ukrainian\" },\n { tag: \"tr\", name: \"Turkish\" },\n { tag: \"ar\", name: \"Arabic\" },\n { tag: \"he\", name: \"Hebrew\" },\n { tag: \"fa\", name: \"Persian\" },\n { tag: \"hi\", name: \"Hindi\" },\n { tag: \"bn\", name: \"Bengali\" },\n { tag: \"zh\", name: \"Chinese\" },\n { tag: \"ja\", name: \"Japanese\" },\n { tag: \"ko\", name: \"Korean\" },\n { tag: \"vi\", name: \"Vietnamese\" },\n { tag: \"th\", name: \"Thai\" },\n { tag: \"id\", name: \"Indonesian\" },\n { tag: \"sv\", name: \"Swedish\" },\n];\n\nexport interface MeetingActivities {\n /** Polls (with results), questions, whiteboard strokes — or null when refused. */\n readonly collab: CollabSnapshot | null;\n readonly refusal: { readonly message: string; readonly remedy: string } | null;\n}\n\n/**\n * THE MEETING'S POLLS, Q&A AND WHITEBOARD, AFTER IT (Meet wave 5) — for the record page.\n * The same `GET /v1/meet/collab` the room reads on join; a refusal is returned as data,\n * so the record still renders everything else.\n */\nexport async function loadMeetingActivities(\n api: MeetApi,\n meetingId: string,\n meetingPass: string | null = null,\n): Promise<MeetingActivities> {\n let response: Response;\n try {\n response = await api.request({\n path: `${MEET_ROUTES.collab}?meeting_id=${encodeURIComponent(meetingId)}`,\n method: \"GET\",\n operation: \"meet.loadMeetingActivities\",\n requireAuth: meetingPass === null,\n ...(meetingPass !== null ? { headers: { [MEETING_PASS_HEADER]: meetingPass } } : {}),\n });\n } catch (error) {\n return {\n collab: null,\n refusal: {\n message: `Polls, questions and the whiteboard could not be read (${(error as Error).message}).`,\n remedy: (error as { remedy?: string }).remedy ?? \"Reload the page.\",\n },\n };\n }\n if (!response.ok) {\n let body: unknown = null;\n try {\n body = await response.json();\n } catch {\n body = null;\n }\n const said = readServerRefusal(body);\n return {\n collab: null,\n refusal: {\n message: said?.reason ?? `Polls and questions could not be read (the server answered ${response.status}).`,\n remedy: said?.remedy || \"Reload the page.\",\n },\n };\n }\n return { collab: collabFromState((await response.json()) as Row, EMPTY_COLLAB), refusal: null };\n}\n","/**\n * WHO SAID THAT — one answer, every surface, both lanes (MRI-A16).\n *\n * 🚨 **A RAW LIVEKIT IDENTITY IS NEVER A PERSON'S NAME.** On production\n * 2026-09-09 the host's caption rail read *\"Ada Lovelace: I planted a marigold\n * in the courtyard\"* and the guest's — the same meeting, the same second —\n * read *\"guest:c8-spk-1\"*, because the host resolved the name from the durable\n * rows it can read and the guest had no fallback at all and printed the token\n * (MRI-C8). Two surfaces disagreeing about who is speaking is worse than either\n * one being ugly: a person reading the transcript cannot tell whether those are\n * one speaker or two.\n *\n * The cure is that nothing resolves a name for itself. Every source that\n * happens to KNOW a name teaches this book — the LiveKit roster, a durable\n * `meet_transcript_segments` row, a durable `meet_participants` row, a chat\n * message, our own identity — and every surface asks it. Three rules:\n *\n * 1. **A name is learned, never unlearned.** Someone who leaves the room keeps\n * the name they had; a caption that lands after them still says who spoke.\n * This is the \"last known name\" rule, and it is why the book is a store\n * field rather than a derivation over the current roster.\n * 2. **An identity is not a name.** `learn` refuses a \"name\" that is the\n * identity itself — which is exactly what the LiveKit roster hands back for\n * a participant whose token carried no name, and exactly how the token got\n * onto the screen in the first place.\n * 3. **When nobody knows, SAY nobody knows.** `displayNameFor` answers with an\n * honest placeholder — \"Unnamed guest\" / \"Unnamed participant\" — never a\n * token, and never an invented name. The placeholder is deliberately not\n * unique, so surfaces that must keep two unnamed people apart group by\n * IDENTITY rather than by the rendered name.\n */\n\nimport type { ParticipantIdentity } from \"./types\";\n\n/** Identity → the best display name anything has ever known for it. */\nexport type NameBook = Readonly<Record<string, string>>;\n\n/**\n * S7 (CORE-DESIGN §2.1): a LiveKit identity is `<person key>~<tab id>` — one CONNECTION per\n * browser tab. The person key (`user:<id>` · `guest:<device>` · `agent:<capability>:<meeting>`)\n * is what every tile, row and roster entry uses. THIS is the one helper that reads the shape;\n * `names.test.ts` fails on any identity prefix test elsewhere in the package.\n */\nexport const CONNECTION_SEPARATOR = \"~\";\n\n/** The person key of any identity (a connection identity or a person key). */\nexport function personOf(identity: ParticipantIdentity | string): ParticipantIdentity {\n const value = String(identity ?? \"\").trim();\n const cut = value.indexOf(CONNECTION_SEPARATOR);\n return (cut === -1 ? value : value.slice(0, cut)) as ParticipantIdentity;\n}\n\n/** Do two identities (connections or person keys) name the same person? */\nexport function samePerson(a: ParticipantIdentity | string | null | undefined, b: ParticipantIdentity | string | null | undefined): boolean {\n if (a == null || b == null) return false;\n return personOf(a) === personOf(b);\n}\n\n/** The account id behind an identity, or null for a guest or an agent. */\nexport function userIdOfIdentity(identity: ParticipantIdentity | string): string | null {\n const person = personOf(identity);\n return person.startsWith(\"user:\") ? person.slice(\"user:\".length) : null;\n}\n\n/** A guest's identity is minted `guest:<something>` by the token route. */\nexport function isGuestIdentity(identity: ParticipantIdentity | string): boolean {\n return personOf(identity).startsWith(\"guest:\");\n}\n\n/**\n * An AGENT's identity is minted `agent:<capability>:<meeting>` by the server\n * (`services/meet/worker.py`). An agent is never an anonymous \"participant\":\n * it is a product with a name, and a room that cannot say which product is\n * listening has failed the consent notice it is required to carry (D10).\n */\nexport function isAgentIdentity(identity: ParticipantIdentity | string): boolean {\n return personOf(identity).startsWith(\"agent:\");\n}\n\n/** The note-taker's product name — `services/meet/worker.py::AGENT_DISPLAY_NAME`. */\nexport const NOTE_TAKER_PRODUCT_NAME = \"Matrx note-taker\";\n/** Any other Matrx agent whose dispatch carried no name. */\nexport const AGENT_PRODUCT_NAME = \"Matrx agent\";\n/** Somebody in the room whose name nothing has ever known. */\nexport const UNNAMED_GUEST = \"Unnamed guest\";\n/** The same, for a participant whose identity is not a guest token. */\nexport const UNNAMED_PARTICIPANT = \"Unnamed participant\";\n\n/**\n * The strings THIS MODULE invents when nobody knows a name. They are answers,\n * never facts: `isRealName` refuses them, so a placeholder can never be learned\n * as somebody's name and can never outrank a real name that arrives later.\n *\n * 🚨 This is what makes the roster safe to route through the book. A roster\n * event projects \"Unnamed guest\" for a nameless participant; a durable\n * transcript row then teaches \"Ada Lovelace\"; without this rule the placeholder\n * — a perfectly ordinary-looking string — would win, and the screen would keep\n * saying \"Unnamed guest\" about somebody the meeting plainly knows.\n */\nconst PLACEHOLDER_NAMES: readonly string[] = [\n NOTE_TAKER_PRODUCT_NAME,\n AGENT_PRODUCT_NAME,\n UNNAMED_GUEST,\n UNNAMED_PARTICIPANT,\n];\n\n/** Is this string one of the placeholders this module produces? */\nexport function isPlaceholderName(name: string | null | undefined): boolean {\n return typeof name === \"string\" && PLACEHOLDER_NAMES.includes(name.trim());\n}\n\n/** What an identity is called when NOTHING knows a name for it. Never a token. */\nexport function placeholderNameFor(identity: ParticipantIdentity | string): string {\n const key = String(identity);\n if (isAgentIdentity(key)) {\n return key.startsWith(\"agent:note_taker:\") || key.startsWith(\"agent:note-taker\")\n ? NOTE_TAKER_PRODUCT_NAME\n : AGENT_PRODUCT_NAME;\n }\n return isGuestIdentity(key) ? UNNAMED_GUEST : UNNAMED_PARTICIPANT;\n}\n\n/**\n * Would this string be a real name for this identity? A name equal to the\n * identity is the roster's way of saying it has none.\n */\nexport function isRealName(\n identity: ParticipantIdentity | string,\n name: string | null | undefined,\n): boolean {\n if (typeof name !== \"string\") return false;\n const trimmed = name.trim();\n if (trimmed.length === 0) return false;\n if (trimmed === String(identity)) return false;\n // Our own answer is not new information. See PLACEHOLDER_NAMES.\n if (isPlaceholderName(trimmed)) return false;\n return true;\n}\n\n/**\n * Teach the book one identity's name. Returns the SAME book when nothing was\n * learned, so a store commit can be skipped rather than waking every subscriber\n * on every caption.\n */\nexport function learnName(\n book: NameBook,\n identity: ParticipantIdentity | string,\n name: string | null | undefined,\n): NameBook {\n const key = String(identity);\n if (key.length === 0) return book;\n if (!isRealName(key, name)) return book;\n const learned = (name as string).trim();\n if (book[key] === learned) return book;\n return { ...book, [key]: learned };\n}\n\n/** Teach the book several at once — a roster replace, or a durable read. */\nexport function learnNames(\n book: NameBook,\n entries: readonly {\n identity: ParticipantIdentity | string;\n displayName?: string | null | undefined;\n }[],\n): NameBook {\n let next = book;\n for (const entry of entries) next = learnName(next, entry.identity, entry.displayName);\n return next;\n}\n\n/**\n * The name a surface renders for `identity`. Never a raw token, never invented.\n *\n * `preferred` is what the arriving thing itself claimed (a caption's roster\n * lookup, a durable row's `speaker` column); it wins when it is a real name,\n * because it is the freshest, and teaches the book on its way past.\n */\nexport function displayNameFor(\n book: NameBook,\n identity: ParticipantIdentity | string,\n preferred?: string | null | undefined,\n): string {\n const key = String(identity);\n if (isRealName(key, preferred)) return (preferred as string).trim();\n const known = book[key];\n if (known !== undefined) return known;\n // NOBODY KNOWS, AND THAT IS WHAT IT SAYS. A person reading this can tell the\n // difference between \"someone whose name we never learned\" and a name.\n return placeholderNameFor(key);\n}\n","/**\n * RECORDING — LiveKit Egress, host-controlled, consent-enforced (R3/R5).\n *\n * 🚨 **CONSENT IS NOT OPTIONAL AND NOT A PROP.** There is no\n * `showRecordingIndicator={false}`. Recording a person without telling them is\n * illegal in a two-party-consent jurisdiction and is indefensible everywhere\n * else, so the indicator and the join-time notice are properties of the\n * package, not decisions a consumer gets to make. The only knob is the org's\n * `recordingPolicy` — disabled / host-controlled / always-on — which decides\n * whether recording may happen at all, never whether people are told.\n *\n * 🚨 **THE RECORDING BELONGS TO THE PLATFORM, NOT TO LIVEKIT.** The server\n * lands the artifact in the platform files system and returns a FILE ID. A\n * provider URL would be an orphan the day the room is garbage collected — the\n * reason `RecordingRecord.fileId` is not `url`.\n *\n * The client half is small on purpose: start/stop are aidream calls (the API\n * secret never comes near a browser), and the truth about a recording arrives\n * from the server's webhook through the room channel. This module never\n * *guesses* that a recording started.\n */\n\nimport type { MeetApi } from \"./api\";\nimport { MeetError } from \"./errors\";\nimport type { MeetStore } from \"./store\";\nimport { MEET_ROUTES } from \"./tokens\";\nimport type {\n MeetingId,\n MeetingRecord,\n RecordingState,\n RoomName,\n UserId,\n} from \"./types\";\n\nexport interface RecordingControllerOptions {\n /** The ONE org-aware aidream client (MRI-A16). Owns base url and headers. */\n api: MeetApi;\n store: MeetStore;\n /** Announce the new state to everyone in the room (the indicator is for all). */\n announce?: ((state: RecordingState, by: UserId | null) => void) | undefined;\n /** Persist that this participant saw the notice. Consent is RECORDED (R5). */\n acknowledgeConsent?: ((meetingId: MeetingId) => Promise<void>) | undefined;\n}\n\nexport interface RecordingController {\n /** True when the org policy and the actor's role allow starting a recording. */\n canStart(meeting: MeetingRecord | null, isHost: boolean): boolean;\n start(args: { meetingId: MeetingId; roomName: RoomName; by: UserId }): Promise<void>;\n stop(args: { meetingId: MeetingId; by: UserId }): Promise<void>;\n /** The join-time notice text. One sentence, one place, every surface. */\n consentNotice(meeting: MeetingRecord | null): string | null;\n acknowledge(meetingId: MeetingId): Promise<void>;\n}\n\nexport function createRecordingController(\n options: RecordingControllerOptions,\n): RecordingController {\n async function post(route: string, body: Record<string, unknown>): Promise<void> {\n let response: Response;\n try {\n response = await options.api.request({\n path: route,\n method: \"POST\",\n // A GUEST CAN NEVER START OR STOP A RECORDING (R5) — refused here,\n // before the wire, with the same sentence the hidden control implies.\n requireAuth: true,\n operation: \"meet.recording\",\n body,\n });\n } catch (error) {\n if ((error as { code?: string }).code === \"session-unavailable\") {\n throw new MeetError(\n \"forbidden\",\n \"meet.recording: recording requires an authenticated host\",\n \"A guest can never start or stop a recording (R5). The control is hidden for guests.\",\n );\n }\n throw error;\n }\n if (response.status === 403) {\n throw new MeetError(\n \"forbidden\",\n \"meet.recording: the server refused this recording action\",\n \"The actor is not a host of this meeting, or the org policy is `disabled`. The server \" +\n \"is the authority; do not re-decide it on the client.\",\n );\n }\n if (!response.ok) {\n throw new MeetError(\n \"transport\",\n `meet.recording: the server returned ${response.status}`,\n \"Egress could not be controlled. The recording state stays where it was rather than \" +\n \"showing a recording indicator for a recording that is not running.\",\n );\n }\n }\n\n return {\n canStart(meeting, isHost) {\n if (meeting === null) return false;\n if (meeting.recordingPolicy === \"disabled\") return false;\n // `always-on` is started by the SERVER when the room opens; a host cannot\n // start (or stop) it, which is the point of the policy.\n if (meeting.recordingPolicy === \"always-on\") return false;\n return isHost;\n },\n\n async start({ meetingId, roomName, by }) {\n // OPTIMISTIC ONLY AS FAR AS \"starting\", and marked `\"local\"` so the\n // durable row overrules it the moment it lands (D16). The indicator turns\n // solid when the SERVER says the egress is running — never on a client's\n // hope, and never STUCK on that hope either: `meet_recordings` is\n // subscribed and the room state carries it to the guests (S8).\n options.store.setRecording(\"starting\", by, \"local\");\n try {\n await post(MEET_ROUTES.recordingStart, {\n meeting_id: meetingId,\n room_name: roomName,\n });\n } catch (error) {\n options.store.setRecording(\"idle\", null, \"local\");\n throw error;\n }\n options.announce?.(\"starting\", by);\n },\n\n async stop({ meetingId, by }) {\n options.store.setRecording(\"stopping\", options.store.snapshot().recordingStartedBy, \"local\");\n try {\n await post(MEET_ROUTES.recordingStop, { meeting_id: meetingId });\n } catch (error) {\n options.store.setRecording(\n \"recording\",\n options.store.snapshot().recordingStartedBy,\n \"local\",\n );\n throw error;\n }\n options.announce?.(\"stopping\", by);\n },\n\n consentNotice(meeting) {\n if (meeting === null || meeting.recordingPolicy === \"disabled\") return null;\n if (meeting.recordingPolicy === \"always-on\") {\n return \"This meeting is recorded and transcribed automatically. By joining, you consent to being recorded.\";\n }\n return \"The host can record and transcribe this meeting. You will see a recording indicator whenever it is running.\";\n },\n\n async acknowledge(meetingId) {\n await options.acknowledgeConsent?.(meetingId);\n },\n };\n}\n\n/**\n * 🚨 IS A RECORDING IN FLIGHT RIGHT NOW? One definition (MRI-A16).\n *\n * The control used to ask `state === \"idle\"`, which was survivable only while\n * the store never learned any state but its own: the moment the DURABLE states\n * started arriving, a meeting whose first recording had finished sat on\n * `available` forever and the host could never start a second one — the Start\n * button simply stopped existing. `processing`, `available` and `failed` all\n * mean \"nothing is recording\"; only these three mean it is.\n */\nexport const RECORDING_IN_FLIGHT: readonly RecordingState[] = [\n \"starting\",\n \"recording\",\n \"stopping\",\n];\n\nexport function isRecordingInFlight(state: RecordingState): boolean {\n return RECORDING_IN_FLIGHT.includes(state);\n}\n\n/** The indicator's words. One definition, so no surface invents a softer one. */\nexport function recordingLabel(state: RecordingState): string | null {\n switch (state) {\n case \"starting\":\n return \"Starting recording…\";\n case \"recording\":\n return \"Recording\";\n case \"stopping\":\n return \"Stopping recording…\";\n case \"processing\":\n return \"Processing recording…\";\n case \"available\":\n return \"Recording ready\";\n case \"expired\":\n return \"Recording expired\";\n case \"failed\":\n return \"Recording failed\";\n default:\n return null;\n }\n}\n","/**\n * THE RULES A SCREEN REDRAWS FROM WHILE THE CALL IS RUNNING (P1: chat-restrictions,\n * mod-attendee-av-disabled, time-limit-warning, rec-consent-gate).\n *\n * The host can change a meeting's rules mid-call through the meeting's own rules door, which\n * never talks to the room. The server's state read answers the live values (`live_policy`), and\n * the room reads it on a short timer while the person is in the call — so a rule changed at\n * second 40 is on every screen within one poll. Pure functions here; the timer lives in\n * `meetings.ts`.\n */\n\nimport type { ParticipantRole } from \"./types\";\n\nexport type ChatMode = \"everyone\" | \"hosts_only\" | \"off\";\nexport type AvLock = \"off\" | \"mic\" | \"camera\" | \"both\";\nexport type RecordingConsent = \"notice\" | \"ok_or_leave\" | \"view_only_until_agree\";\n/** Who may share a screen (`share_who_can`) and how many may at once (`share_concurrency`). */\nexport type ShareWhoCan = \"everyone\" | \"host_only\" | \"org\" | \"presenters\";\nexport type ShareConcurrency = \"one_takeover\" | \"one_replace\" | \"multiple\";\n\n/** What remains when no host or co-host is in the room (`headless_powers`, CORE-DESIGN §4.2). */\nexport type HeadlessPowers = \"members_admit\" | \"none\" | \"cohosts_only\";\n\nexport interface LivePolicy {\n readonly headlessPowers: HeadlessPowers;\n readonly chatMode: ChatMode;\n readonly avLock: AvLock;\n /** 0 = no limit. */\n readonly timeLimitMinutes: number;\n readonly timeLimitWarningMinutes: number;\n readonly recordingConsent: RecordingConsent;\n readonly shareWhoCan: ShareWhoCan;\n readonly shareConcurrency: ShareConcurrency;\n}\n\nexport const DEFAULT_LIVE_POLICY: LivePolicy = {\n headlessPowers: \"members_admit\",\n chatMode: \"everyone\",\n avLock: \"off\",\n timeLimitMinutes: 0,\n timeLimitWarningMinutes: 5,\n recordingConsent: \"notice\",\n shareWhoCan: \"everyone\",\n shareConcurrency: \"one_takeover\",\n};\n\nfunction oneOf<T extends string>(value: unknown, allowed: readonly T[], fallback: T): T {\n return typeof value === \"string\" && (allowed as readonly string[]).includes(value) ? (value as T) : fallback;\n}\n\nfunction whole(value: unknown, fallback: number): number {\n const n = typeof value === \"number\" ? value : typeof value === \"string\" ? Number(value) : NaN;\n return Number.isFinite(n) && n >= 0 ? Math.floor(n) : fallback;\n}\n\n/** Reads the server's `live_policy` object; a key it did not send keeps its default. */\nexport function livePolicyFrom(raw: unknown): LivePolicy {\n const o = typeof raw === \"object\" && raw !== null ? (raw as Record<string, unknown>) : {};\n return {\n headlessPowers: oneOf(o[\"headless_powers\"], [\"members_admit\", \"none\", \"cohosts_only\"], DEFAULT_LIVE_POLICY.headlessPowers),\n chatMode: oneOf(o[\"chat_mode\"], [\"everyone\", \"hosts_only\", \"off\"], DEFAULT_LIVE_POLICY.chatMode),\n avLock: oneOf(o[\"attendee_av_lock\"], [\"off\", \"mic\", \"camera\", \"both\"], DEFAULT_LIVE_POLICY.avLock),\n timeLimitMinutes: whole(o[\"time_limit_minutes\"], 0),\n timeLimitWarningMinutes: whole(o[\"time_limit_warning_minutes\"], DEFAULT_LIVE_POLICY.timeLimitWarningMinutes),\n recordingConsent: oneOf(\n o[\"recording_consent\"],\n [\"notice\", \"ok_or_leave\", \"view_only_until_agree\"],\n DEFAULT_LIVE_POLICY.recordingConsent,\n ),\n shareWhoCan: oneOf(o[\"share_who_can\"], [\"everyone\", \"host_only\", \"org\", \"presenters\"], DEFAULT_LIVE_POLICY.shareWhoCan),\n shareConcurrency: oneOf(o[\"share_concurrency\"], [\"one_takeover\", \"one_replace\", \"multiple\"], DEFAULT_LIVE_POLICY.shareConcurrency),\n };\n}\n\nexport function samePolicy(a: LivePolicy, b: LivePolicy): boolean {\n return (\n a.headlessPowers === b.headlessPowers &&\n a.chatMode === b.chatMode &&\n a.avLock === b.avLock &&\n a.timeLimitMinutes === b.timeLimitMinutes &&\n a.timeLimitWarningMinutes === b.timeLimitWarningMinutes &&\n a.recordingConsent === b.recordingConsent &&\n a.shareWhoCan === b.shareWhoCan &&\n a.shareConcurrency === b.shareConcurrency\n );\n}\n\nconst MODERATORS: ReadonlySet<ParticipantRole> = new Set([\"host\", \"cohost\"]);\n\n/** Hosts and co-hosts are never locked. */\nexport function isLockedFor(policy: LivePolicy, role: ParticipantRole, kind: \"microphone\" | \"camera\"): boolean {\n if (MODERATORS.has(role)) return false;\n return policy.avLock === \"both\" || (kind === \"microphone\" ? policy.avLock === \"mic\" : policy.avLock === \"camera\");\n}\n\n/**\n * share-who-can: may this person share a screen under the meeting's current rule? Mirrors the\n * server's `sharing.may_share` (which is what actually decides); this only decides whether the\n * control is drawn. A word the package does not know leaves it open.\n */\nexport function canShareUnder(\n policy: LivePolicy,\n role: ParticipantRole,\n standing: \"member\" | \"invited\" | \"outsider\" | \"guest\" | null,\n): boolean {\n if (role === \"viewer\") return false;\n if (MODERATORS.has(role)) return true;\n switch (policy.shareWhoCan) {\n case \"host_only\":\n return false;\n case \"presenters\":\n return role === \"presenter\";\n case \"org\":\n return role === \"presenter\" || standing === \"member\";\n default:\n return true;\n }\n}\n\n/**\n * rec-consent-gate: the person has not yet agreed to being recorded, under a rule that makes them\n * wait (`ok_or_leave` shows a dialog, `view_only_until_agree` also takes the microphone and camera).\n */\nexport function consentPending(policy: LivePolicy, role: ParticipantRole, recordingOn: boolean, given: boolean): boolean {\n return recordingOn && !given && policy.recordingConsent !== \"notice\" && !MODERATORS.has(role);\n}\n\n/** May this role write in the chat under the meeting's current mode? */\nexport function canChat(policy: LivePolicy, role: ParticipantRole): boolean {\n if (policy.chatMode === \"everyone\") return true;\n return policy.chatMode === \"hosts_only\" && MODERATORS.has(role);\n}\n\n/** Whole minutes left before the time limit, or null with no limit / unknown start. */\nexport function minutesLeft(policy: LivePolicy, startedAtMs: number | null, nowMs: number): number | null {\n if (policy.timeLimitMinutes <= 0 || startedAtMs === null) return null;\n return Math.ceil((startedAtMs + policy.timeLimitMinutes * 60_000 - nowMs) / 60_000);\n}\n\n/**\n * May this person let waiting people in right now? A host or co-host always may. When NO host or\n * co-host is in the room, the meeting's `headless_powers` decides: `members_admit` lets any admitted\n * member (the server's door enforces the same rule); `none` and `cohosts_only` add nobody.\n * (meeting-without-host)\n */\nexport function mayAdmitNow(\n role: ParticipantRole | \"none\",\n participants: readonly { readonly role: ParticipantRole }[],\n policy: LivePolicy,\n): boolean {\n if (role === \"none\") return false;\n if (role === \"host\" || role === \"cohost\") return true;\n if (role === \"viewer\") return false;\n const hostIn = participants.some((p) => p.role === \"host\" || p.role === \"cohost\");\n return !hostIn && policy.headlessPowers === \"members_admit\";\n}\n","/**\n * LISTEN-ONLY ATTENDANCE — being IN the room is never contingent on a device.\n *\n * 0.3.1 and every version before it enabled the microphone as the last step of\n * `connect()`, inside the same `try` as the join. So a denied, absent, or busy\n * microphone did not produce a muted participant — it threw, the join was\n * failed with a device remedy, and the user was dropped back to `<PreJoin>`\n * having never entered the meeting. The whole in-room surface (tiles, captions,\n * live notes, Q&A, the note-taker consent notice) was unreachable to anyone\n * without a working mic, which is ordinary: a locked-down laptop, a conference\n * room dial-in, a phone on a call, a browser profile that denies capture by\n * policy, or a person who simply wants to listen. Every competitor allows it.\n *\n * THE CLASS, and how it is closed here:\n *\n * 1. **Connecting and publishing are two different operations.** The engine\n * reaches `connected` the moment the transport is up. Media is attempted\n * AFTER that, per track, and a rejection never unwinds the join.\n * 2. **A track that could not be acquired is STATE, not an exception.** The\n * reason and its remedy — the browser's own, translated by\n * `normalizeDeviceError` — ride in the snapshot so the UI says WHY the mic\n * is off instead of showing a mute button that silently does nothing.\n * 3. **Enabling later runs the SAME code path.** `setMicrophoneEnabled(true)`\n * from the control bar records exactly what the join attempt records, so a\n * person who plugs in a headset mid-meeting is one click from speaking and\n * a person whose second attempt also fails gets the same honest sentence.\n * A separate \"retry\" path is how the two drift.\n * 4. **The notice is one sentence, defined once.** Both the package's own room\n * chrome and any host that renders its own read `listenOnlyNotice()`, so\n * there is no second wording to keep in sync.\n */\n\nimport type { MeetError } from \"./errors\";\n\n/**\n * WHY a device is unusable, in the contract's words (CORE-DESIGN §3.2, the `mic` / `camera`\n * region's `unavailable:<reason>`). The media supervisor (`media-supervisor.ts`) decides it from\n * the browser's error AND the Permissions API, because the error alone cannot tell a person's\n * Block from an operating-system refusal or a stale device id.\n */\nexport type DeviceUnavailableReason =\n | \"denied\"\n | \"blocked_site\"\n | \"blocked_os\"\n | \"none\"\n | \"busy\"\n | \"failed\"\n | \"revoked\";\n\n/** Why a local track is not publishing, in the browser's terms plus a remedy. */\nexport interface MediaUnavailable {\n readonly code: string;\n readonly message: string;\n readonly remedy: string;\n /** The region reason (S6c). Absent on states recorded before the supervisor classified them. */\n readonly reason?: DeviceUnavailableReason | undefined;\n}\n\n/** What the browser's permission store says for one device kind. `unknown` = no Permissions API. */\nexport type PermissionReading = \"granted\" | \"denied\" | \"prompt\" | \"unknown\";\n\n/** A device change the person is told about (S6c): unplugged → switched, or nothing left. */\nexport interface DeviceNotice {\n readonly id: number;\n readonly kind: \"microphone\" | \"camera\";\n /** `switched` = the meeting moved to another device; `lost` = none of this kind is left. */\n readonly outcome: \"switched\" | \"lost\";\n /** The device that went away, by label (empty when the browser hid labels). */\n readonly from: string;\n /** The device that went away, by id, so Undo can go back to it if it is plugged in again. */\n readonly fromId?: string | undefined;\n /** The device now in use (`switched` only). */\n readonly to: string | null;\n}\n\n/** A device that just appeared — offered, never switched to silently (catalog `device-default-changed`). */\nexport interface DeviceOffer {\n readonly kind: \"microphone\" | \"camera\";\n readonly deviceId: string;\n readonly label: string;\n}\n\n/**\n * THE MEDIA REGIONS THE SUPERVISOR OWNS (CORE-DESIGN §3.2 `audio_output`, and the inputs the\n * `mic` / `camera` regions are derived from). None of it can change the call phase.\n */\nexport interface MediaRegionState {\n readonly permission: { readonly microphone: PermissionReading; readonly camera: PermissionReading };\n /** `blocked` = the browser's autoplay policy is holding remote audio until a gesture. */\n readonly audioOutput: \"ok\" | \"blocked\";\n readonly notice: DeviceNotice | null;\n readonly offer: DeviceOffer | null;\n /**\n * THE PRE-JOIN CHECK per kind (P1 `perm-prompt-pending`, `perm-blocked-site`, `device-none-found`,\n * `device-busy`): what the preview found BEFORE the room exists. `prompting` = the browser was\n * asked and has not answered (Join stays usable); `unavailable` carries the classified refusal.\n */\n readonly preview: { readonly microphone: DevicePreview; readonly camera: DevicePreview };\n}\n\n/** One kind's pre-join check. `idle` = nothing was tried (or the person turned it off before). */\nexport interface DevicePreview {\n readonly state: \"idle\" | \"prompting\" | \"on\" | \"off\" | \"unavailable\";\n readonly unavailable: MediaUnavailable | null;\n}\n\nexport const IDLE_PREVIEW: DevicePreview = { state: \"idle\", unavailable: null };\n\nexport const IDLE_MEDIA_REGION: MediaRegionState = {\n permission: { microphone: \"unknown\", camera: \"unknown\" },\n audioOutput: \"ok\",\n notice: null,\n offer: null,\n preview: { microphone: IDLE_PREVIEW, camera: IDLE_PREVIEW },\n};\n\nexport interface LocalTrackState {\n /** True only when the track is actually published. Never optimistic. */\n readonly enabled: boolean;\n /**\n * Set when acquisition was ATTEMPTED and refused. `null` covers both \"on\"\n * and \"deliberately off\" — a user who unchecked the mic in pre-join is not\n * looking at an error, they are looking at a choice they made.\n */\n readonly unavailable: MediaUnavailable | null;\n}\n\nexport interface LocalMediaState {\n readonly microphone: LocalTrackState;\n readonly camera: LocalTrackState;\n}\n\nexport const IDLE_TRACK: LocalTrackState = { enabled: false, unavailable: null };\n\nexport const IDLE_MEDIA: LocalMediaState = {\n microphone: IDLE_TRACK,\n camera: IDLE_TRACK,\n};\n\nexport function unavailableFrom(error: MeetError, reason?: DeviceUnavailableReason): MediaUnavailable {\n return reason === undefined\n ? { code: error.code, message: error.message, remedy: error.remedy }\n : { code: error.code, message: error.message, remedy: error.remedy, reason };\n}\n\n/**\n * THE SENTENCE. One definition, so the room chrome, a host's own chrome and the\n * tests cannot say three different things about the same fact.\n */\nexport const LISTEN_ONLY_NOTICE =\n \"You're attending without a microphone — others can't hear you\";\n\n/**\n * True when this participant is in the room with NO USABLE microphone — the\n * device was refused or is missing. A person who simply muted themselves has a\n * working microphone and is not \"attending without\" one, so a mute never\n * counts: `unavailable` is the only signal.\n */\nexport function isListenOnly(media: LocalMediaState): boolean {\n return !media.microphone.enabled && media.microphone.unavailable !== null;\n}\n\n/**\n * The notice to render while in the room, or `null` when the mic is live.\n * Returns the plain sentence; the reason and remedy, when there is one, are\n * rendered beside it from `media.microphone.unavailable`.\n */\nexport function listenOnlyNotice(media: LocalMediaState): string | null {\n return isListenOnly(media) ? LISTEN_ONLY_NOTICE : null;\n}\n","/**\n * THE ROOM SNAPSHOT — one immutable object the UI renders and nothing else.\n *\n * Deliberately NOT Redux, NOT a context full of `useState`, and NOT a bag of\n * refs: one store with `getSnapshot`/`subscribe` is exactly what\n * `useSyncExternalStore` wants, which is what makes tearing impossible when a\n * roster event and a speaker event land in the same tick.\n *\n * The rules encoded here:\n *\n * - **The roster is replaced wholesale, never patched.** The driver hands us\n * the full truth on every media event (see `room-driver.ts`); merging deltas\n * from six event sources is how a tile for someone who left stays on screen.\n * - **Ephemera EXPIRE.** Reactions and speaking flags are time-boxed in the\n * snapshot, so a dropped \"stop\" event cannot leave a permanent 🎉 or a\n * permanently-speaking ring. (Nothing here relies on receiving an end event.)\n * - **An interim transcript line is REPLACED in place by its final.** Appending\n * both is how a transcript ends up saying everything twice.\n */\n\nimport { displayNameFor, learnName, learnNames, type NameBook } from \"./names\";\nimport { isRecordingInFlight } from \"./recording\";\nimport { DEFAULT_LEAVE_POLICY, type LeavePolicy } from \"./leave\";\nimport { DEFAULT_LIVE_POLICY, type LivePolicy } from \"./live-policy\";\nimport { IDLE_MEDIA, IDLE_MEDIA_REGION, type LocalMediaState, type LocalTrackState, type MediaRegionState } from \"./attendance\";\nimport { ROLE_RANK } from \"./types\";\nimport { nextPhase, reasonOf, type MeetPhase, type PhaseEvent } from \"./phase\";\nimport type { ObserversVisibleTo } from \"./types\";\nimport type { NoteTakerRefusal } from \"./note-taker\";\nimport { EMPTY_COLLAB, type CollabSnapshot } from \"./collab\";\nimport { mergeCaption, mergeRecord } from \"./transcript\";\nimport type {\n AdmissionState,\n ConnectionQuality,\n LobbyEntry,\n MeetParticipant,\n MeetingNote,\n MeetingRecord,\n ParticipantIdentity,\n ParticipantRole,\n ParticipantStanding,\n RecordingSource,\n RecordingState,\n StageLayout,\n TranscriptSegment,\n UserId,\n WrapUpAnnouncement,\n RoomName,\n CallInvite,\n} from \"./types\";\n\nexport interface RoomChatMessage {\n readonly id: string;\n readonly identity: ParticipantIdentity;\n readonly displayName: string;\n readonly text: string;\n readonly at: string;\n /**\n * Set for a PRIVATE message: the one identity it was addressed to (from the\n * sender's side) or this participant (from the receiver's). Private messages\n * travel the data plane only and are never saved (Meet wave 2).\n */\n readonly to?: ParticipantIdentity | null | undefined;\n /** True once the server has SAVED it — a message everyone sees, late joiners too. */\n readonly saved?: boolean | undefined;\n /** A send the server refused, with its sentence. The message stays visible, marked. */\n readonly failed?: string | undefined;\n}\n\n/**\n * A HOST ACTED ON *ME* (Meet wave 2) — the server's own `matrx.meet.moderation`\n * packet, addressed to this participant. Rendered once, dismissible, in words.\n */\nexport interface ModerationNotice {\n readonly kind: \"muted\" | \"ask-unmute\" | \"removed\" | \"role\" | \"locked\" | \"capability\";\n readonly text: string;\n readonly at: number;\n}\n\n/** What this participant chose for their own media. Remembered per browser. */\nexport interface MediaEffects {\n readonly noiseSuppression: boolean;\n readonly background: \"none\" | \"blur\" | \"image\";\n readonly backgroundImage: string | null;\n}\n\nexport const DEFAULT_EFFECTS: MediaEffects = {\n noiseSuppression: true,\n background: \"none\",\n backgroundImage: null,\n};\n\nexport interface RoomReaction {\n readonly id: string;\n readonly identity: ParticipantIdentity;\n readonly emoji: string;\n readonly at: number;\n}\n\n/**\n * 🚨 WHAT THE SERVER GRANTED THIS JOINER, straight off the token response\n * (MRI-A11). It is placed in the snapshot the instant the token lands — before\n * the LiveKit roster exists at all — so a host's controls are correct on the\n * first frame of the room rather than one roster event later. The roster entry\n * for the local participant carries the SAME decision as a LiveKit attribute;\n * the two agree because one server statement produced both, and the UI reads\n * whichever has arrived.\n */\nexport interface MeetGrants {\n readonly role: ParticipantRole;\n /** Where this joiner stands with the meeting (server-minted, beside the role). */\n readonly standing?: ParticipantStanding | null;\n readonly canRecord: boolean;\n readonly canPublish: boolean;\n readonly canSubscribe: boolean;\n /** HR-360 wave 2 (MD-16): who sees the \"Observing\" line in People (knob). */\n readonly observersVisibleTo?: ObserversVisibleTo | undefined;\n}\n\n/** No token yet grants nothing. Never \"assume host until told otherwise\". */\nexport const NO_GRANTS: MeetGrants = {\n role: \"participant\",\n standing: null,\n canRecord: false,\n canPublish: false,\n canSubscribe: false,\n};\n\n/**\n * S4 — THIS PERSON'S WAIT, as the server's state read (`GET /v1/meet/state`) last said it\n * (CORE-DESIGN §2.6). Set while the phase is a waiting one (`waiting:*`, `denied`,\n * `knock_expired`); the waiting screens read it, never a broadcast. `admission` is the row's own word: `waiting_host` · `knocking` · `denied` ·\n * `expired` · `withdrawn` · `ended` (`admitted` is never held here — it is a join).\n */\nexport interface MeetWaiting {\n readonly admission: AdmissionState;\n /** Is anyone who may admit in the room? null until the first read. */\n readonly admittersPresent: boolean | null;\n /** May a denied person ask again (`reknock_after_deny`)? */\n readonly reknockAllowed: boolean;\n readonly knockExpiresAt: string | null;\n /** While a denial's cooldown runs: the epoch-ms moment the person may ask again; else null. */\n readonly reknockAt?: number | null;\n /** How often the core reads while waiting (`waiting_poll_seconds`). */\n readonly pollSeconds: number;\n /** The run's `live_version` at the last read. */\n readonly liveVersion: number;\n}\n\n/** The core's `connection` region (CORE-DESIGN §3.2) — never a phase. */\nexport interface LocalNetwork {\n readonly path: \"direct\" | \"restricted\" | \"unknown\";\n readonly quality: \"good\" | \"poor\" | \"unknown\";\n readonly videoPaused: boolean;\n}\n\nexport const UNKNOWN_LOCAL_NETWORK: LocalNetwork = { path: \"unknown\", quality: \"unknown\", videoPaused: false };\n\nexport type ConnectionRegion = \"stable\" | \"signal_reconnecting\" | \"reconnecting\" | \"offline\" | \"server_unreachable\";\n\n/**\n * The core's `share` region (CORE-DESIGN §3.2/§3.3 SA). A refused share is a situation the person\n * is in — a notice (`share-unavailable`), never a phase.\n */\nexport interface ShareRegion {\n readonly state: \"none\" | \"picking\" | \"presenting\" | \"refused\" | \"ended\";\n /**\n * Why a share was refused (this browser cannot share, the system blocks it, or it failed) or, for\n * `ended`, why the server took it down: someone else presented instead, or the host's rule no longer lets this person share.\n */\n readonly reason: \"unsupported\" | \"blocked\" | \"failed\" | \"replaced\" | \"not_allowed\" | null;\n /** `ended` / `replaced`: who presents now. */\n readonly by?: string;\n /** The fix, in the core's words (system setting, administrator policy, …), or null. */\n readonly remedy: string | null;\n readonly at: number;\n /** While presenting: whether the share carries sound (`share-audio`). Absent = not known. */\n readonly audio?: boolean;\n}\n\nexport const NO_SHARE: ShareRegion = { state: \"none\", reason: null, remedy: null, at: 0 };\n\nexport interface MeetSnapshot {\n /**\n * WHERE THIS TAB IS — the contract's `data-meet-phase` value, verbatim (CORE-DESIGN §3.1).\n * Written ONLY by `store.dispatch(event)` through the phase machine (`core/phase.ts`);\n * `patch` cannot touch it.\n */\n readonly phase: MeetPhase;\n /** `data-meet-reason` for a refused / terminal phase; null otherwise. */\n readonly reason: string | null;\n /** A notice that came WITH the phase (`sent-to-waiting`), or null. */\n readonly phaseNotice: string | null;\n /** The room this tab last joined (or is joining) — a different meeting mounting resets. */\n readonly roomName: RoomName | null;\n /** The `connection` region: reconnecting is here, never a phase. */\n readonly connection: ConnectionRegion;\n /**\n * Whether the reconnect banner shows: the region is honest at once, the banner only after the\n * drop outlasts `reconnect_banner_delay_ms` (the connection supervisor sets it, §3.3 CS).\n */\n readonly connectionBanner: boolean;\n /**\n * P1 connection-network — THIS tab's own path, as the core judged it from its WebRTC stats\n * (`network-health.ts`): `restricted` = relayed or TCP-only (`net-udp-blocked`), `quality` =\n * the self-assessment (`net-quality-self`), `videoPaused` = the camera was paused by the\n * network to keep the audio (`net-video-auto-off`). The room engine is the one writer.\n */\n readonly localNetwork: LocalNetwork;\n /** The `share` region. */\n readonly share: ShareRegion;\n /**\n * The 1:1 call this tab is placing or answering (CORE-DESIGN §3.6) — the core `calls` slice.\n * The ringing phases (`ringing_out` / `ringing_in` / `call_*`) describe it; null = no call.\n */\n readonly call: { readonly invite: CallInvite; readonly direction: \"outgoing\" | \"incoming\" } | null;\n /** S4: this person's wait (see `MeetWaiting`); null outside the waiting room. */\n readonly waiting: MeetWaiting | null;\n readonly meeting: MeetingRecord | null;\n readonly localIdentity: ParticipantIdentity | null;\n /**\n * S7: this person is already in this meeting on another tab or device, and the pre-join offers\n * the meeting's choice — `rule` is the `second_device` knob (`ask_switch_or_join` · Meet: Switch\n * here / Join here too; `ask_transfer_or_add` · Teams: Transfer / Add). Null otherwise.\n */\n readonly secondDevice: { readonly rule: string } | null;\n /** The server's word on what THIS joiner may do. See `MeetGrants`. */\n readonly grants: MeetGrants;\n readonly participants: readonly MeetParticipant[];\n readonly activeSpeakers: readonly ParticipantIdentity[];\n readonly pinned: ParticipantIdentity | null;\n readonly layout: StageLayout;\n readonly lobby: readonly LobbyEntry[];\n readonly locked: boolean;\n /**\n * 🚨 THE DURABLE `meet_recordings.state`, not a client's hope. Through 0.4.7\n * this only ever held what THIS browser optimistically patched — `starting`\n * on the click, `stopping` on the stop — so the indicator read \"Starting\n * recording…\" for the whole meeting while a perfectly healthy egress ran, and\n * \"Stopping recording…\" forever after (MRI-C8). The server's transitions\n * arrive on two transports now: the `meet_recordings` row for a member, and\n * the room's replicated state for a guest, who has no database.\n */\n readonly recording: RecordingState;\n readonly recordingStartedBy: UserId | null;\n /**\n * WHICH transport last spoke, so the two cannot undo each other (D16). The\n * database wins: `\"record\"` is never overwritten by `\"room\"` or `\"local\"`.\n */\n readonly recordingSource: RecordingSource;\n /**\n * WHICH recording this state is about. It is what tells a LATE packet for a\n * finished recording apart from the FIRST packet of the next one — without\n * it, \"the database wins\" either blocks a second recording forever or lets a\n * stale packet drag a finished one back to `recording`.\n */\n readonly recordingId: string | null;\n readonly chat: readonly RoomChatMessage[];\n /** Whether the SAVED chat has been read for this meeting (late joiners see history). */\n readonly chatLoaded: boolean;\n /** Why the saved chat could not be read on join, in words — or null. */\n readonly chatHistoryError: string | null;\n readonly reactions: readonly RoomReaction[];\n /**\n * The participant a HOST spotlighted for everyone, from the room's metadata\n * (server-set, delivered on join). A local pin outranks it on this screen only.\n */\n readonly spotlight: ParticipantIdentity | null;\n /**\n * S8 REPLICATED ROOM STATE (CORE-DESIGN §2.6 RS), from the room's metadata — the server's\n * truth, delivered on join and after every reconnect: who holds the run's host powers\n * (`actingHost`), who is presenting, and how many people wait to be let in.\n */\n readonly actingHost: ParticipantIdentity | null;\n readonly presenter: ParticipantIdentity | null;\n readonly waitingCount: number;\n /** S8: what the host's Leave offers in this meeting (from the token's `leave_policy`). */\n readonly leavePolicy: LeavePolicy;\n /** The meeting's rules as they are RIGHT NOW (chat mode, attendee lock, time limit), read live while in the call. */\n readonly livePolicy: LivePolicy;\n /** host-reclaim: this person is the meeting's owner and may take the host powers back (Zoom rules). */\n readonly canReclaim: boolean;\n /** rec-consent-gate: this person agreed to the recording that is running. */\n readonly recordingConsentGiven: boolean;\n /** When this run started (epoch ms), so every screen counts a time limit down together. */\n readonly sessionStartedAt: number | null;\n /** True from the moment a host muted THIS microphone until the person turns it on themselves. */\n readonly mutedByHost: boolean;\n /** The last thing a host did to THIS participant, in words. Dismissible. */\n readonly moderationNotice: ModerationNotice | null;\n /**\n * The server's answer to the last host action THIS participant took (\"Muted 3\n * people\", or the refusal). One place, so whichever surface acted — a tile's\n * menu, the People panel, More options — the sentence survives the menu closing.\n */\n readonly actionNotice: { readonly text: string; readonly failed: boolean; readonly at: number } | null;\n /**\n * \"Message privately\" asked for from ANY per-person menu — a tile's, the\n * People panel's — and answered by the one `ControlBar` that owns the chat\n * panel (0.7.8). Through 0.7.7 only the People panel could open a private\n * chat, so the Board's tile menu (and the Room's) had no such item while the\n * People panel did: two menus for one person that disagreed.\n */\n readonly privateChatRequest: { readonly to: ParticipantIdentity; readonly at: number } | null;\n readonly effects: MediaEffects;\n /**\n * Captions AND durable rows, reconciled into one list (D16). A record\n * replaces the caption it supersedes; a caption never overwrites a record.\n */\n readonly transcript: readonly TranscriptSegment[];\n readonly notes: readonly MeetingNote[];\n /**\n * 🚨 IDENTITY → THE BEST NAME ANYTHING HAS EVER KNOWN (MRI-A16). Learned from\n * every source that has one — the roster, a durable segment, a durable\n * attendance row, a chat message — and never unlearned, so a caption that\n * lands after somebody leaves still says who spoke. It exists because the\n * host and the guest rendered DIFFERENT names for the same speaker in the\n * same second, one of them a raw LiveKit token (MRI-C8). See `core/names.ts`.\n */\n readonly names: NameBook;\n /**\n * The wrap-up pass ANNOUNCED itself (MRI-A12). `null` means it has not been\n * announced in this room — which is not the same as \"no summary exists\", and\n * no surface may render it as such.\n */\n readonly wrapUp: WrapUpAnnouncement | null;\n /**\n * The server's last refusal to dispatch the note-taker, in ITS words (D7).\n * Rendered verbatim; never replaced by a spinner or a house sentence.\n */\n readonly noteTakerRefusal: NoteTakerRefusal | null;\n /** `captions-toggle`: THIS viewer wants captions. Local to the viewer; never sent to the room. */\n readonly captionsOn: boolean;\n /** Why asking for captions failed, in the server's words (`captions-unavailable`). */\n readonly captionsRefusal: NoteTakerRefusal | null;\n /** `captions_start_timeout_seconds` from the token's knobs; the \"Starting captions\" ceiling. */\n readonly captionsStartTimeoutSeconds: number;\n /**\n * What the LOCAL participant is actually publishing, and why not when it is\n * not. Attendance never depends on it (see `attendance.ts`): a refused\n * microphone is a state with a remedy, not a failed join.\n */\n readonly media: LocalMediaState;\n /**\n * S6c — the media supervisor's regions: permission per kind, autoplay (`audio_output`), the\n * last device change and an offered new device. Written only by `media-supervisor.ts`.\n */\n readonly mediaRegion: MediaRegionState;\n /** The last failure, with its remedy. Never null-and-hoping. */\n readonly failure: {\n readonly message: string;\n readonly remedy: string;\n /** The join gate's reason code, when the server named one (CORE-DESIGN §2.5). */\n readonly reason?: string | null;\n /**\n * When a refusal lifts by itself (a denial's cooldown): the epoch-ms moment the person may\n * ask again. Absent for every refusal that does not lift (CORE-DESIGN §11 `wr-denied-reknock`).\n */\n readonly retryAt?: number | null;\n /**\n * `join` = the person never got into the call this time (`net-cannot-connect`): a network or\n * server failure on the way IN is \"can't connect\", not \"lost your connection\".\n */\n readonly stage?: \"join\";\n } | null;\n /**\n * Meet wave 5: polls, Q&A, the shared whiteboard and the breakout plan — what the\n * server said, per viewer (`core/collab.ts`). Read once per join, then packets.\n */\n readonly collab: CollabSnapshot;\n /** The breakout room THIS participant is in, or null for the main room. */\n readonly breakoutRoomId: string | null;\n}\n\nconst EMPTY: MeetSnapshot = {\n phase: \"resolving\",\n reason: null,\n phaseNotice: null,\n roomName: null,\n connection: \"stable\",\n connectionBanner: false,\n localNetwork: UNKNOWN_LOCAL_NETWORK,\n share: NO_SHARE,\n call: null,\n waiting: null,\n meeting: null,\n localIdentity: null,\n secondDevice: null,\n grants: NO_GRANTS,\n participants: [],\n activeSpeakers: [],\n pinned: null,\n layout: \"grid\",\n lobby: [],\n locked: false,\n recording: \"idle\",\n recordingStartedBy: null,\n recordingSource: \"local\",\n recordingId: null,\n chat: [],\n chatLoaded: false,\n chatHistoryError: null,\n reactions: [],\n spotlight: null,\n actingHost: null,\n presenter: null,\n waitingCount: 0,\n leavePolicy: DEFAULT_LEAVE_POLICY,\n livePolicy: DEFAULT_LIVE_POLICY,\n canReclaim: false,\n recordingConsentGiven: false,\n sessionStartedAt: null,\n mutedByHost: false,\n moderationNotice: null,\n actionNotice: null,\n privateChatRequest: null,\n effects: DEFAULT_EFFECTS,\n transcript: [],\n notes: [],\n names: {},\n wrapUp: null,\n noteTakerRefusal: null,\n captionsOn: false,\n captionsRefusal: null,\n captionsStartTimeoutSeconds: 20,\n media: IDLE_MEDIA,\n mediaRegion: IDLE_MEDIA_REGION,\n failure: null,\n collab: EMPTY_COLLAB,\n breakoutRoomId: null,\n};\n\n/**\n * 🚨 THE ONE ANSWER TO \"WHAT ROLE DOES THIS CLIENT'S USER HOLD\" (MRI-A11).\n *\n * Two SERVER-minted carriers, one decision: the `/meet/token` response (in the\n * store from the moment the token lands, before any roster exists) and the\n * local participant's roster entry (whose role comes from the\n * `matrx.participant.role` LiveKit attribute). The stronger wins. Every host\n * power in this package — the UI that renders it AND the code that performs it\n * — asks THIS function, so a control can never render from one source and then\n * be refused by another. That split is exactly how a \"host control\" becomes a\n * button that throws.\n */\nexport function effectiveRole(snapshot: MeetSnapshot): ParticipantRole {\n const me = snapshot.participants.find(\n (participant) => participant.identity === snapshot.localIdentity,\n );\n const fromRoster = me?.role ?? \"participant\";\n const fromToken = snapshot.grants.role;\n return ROLE_RANK[fromRoster] >= ROLE_RANK[fromToken] ? fromRoster : fromToken;\n}\n\n/** This person's standing: the token's word, else what the roster carries for them. */\nexport function effectiveStanding(snapshot: MeetSnapshot): ParticipantStanding | null {\n if (snapshot.grants.standing != null) return snapshot.grants.standing;\n const me = snapshot.participants.find((p) => p.identity === snapshot.localIdentity);\n return me?.standing ?? null;\n}\n\n/** Reactions live this long. A dropped end event cannot outlive it. */\nexport const REACTION_TTL_MS = 5_000;\n/** In-room chat is EPHEMERAL by design (R3) — this is the visible backlog. */\nconst CHAT_LIMIT = 200;\n\n/** What `patch` accepts: the snapshot minus the machine-owned fields. */\nexport type MeetPatch = Partial<Omit<MeetSnapshot, \"phase\" | \"reason\" | \"phaseNotice\">>;\n\nexport interface MeetStore {\n snapshot(): MeetSnapshot;\n subscribe(listener: () => void): () => void;\n /** Everything EXCEPT the phase. The phase moves only through `dispatch`. */\n patch(next: MeetPatch): void;\n /**\n * THE ONE WRITER OF THE PHASE (CORE-DESIGN §3.1). Applies `event` through the machine's table;\n * returns false — and changes nothing — when the table does not allow it from the current\n * phase. `reason` overrides the phase's own reason (an exit's `removed`, `displaced`, …).\n */\n dispatch(event: PhaseEvent, detail?: { readonly reason?: string | null; readonly notice?: string | null }): boolean;\n setParticipants(participants: readonly MeetParticipant[]): void;\n setSpeaking(identities: readonly ParticipantIdentity[]): void;\n setQuality(identity: ParticipantIdentity, quality: ConnectionQuality): void;\n addChat(message: RoomChatMessage): void;\n /** The saved chat, read once per join. Merged with anything that arrived live. */\n loadChat(messages: readonly RoomChatMessage[]): void;\n /** Replace a pending message (by id) with what the server saved, or mark it failed. */\n settleChat(localId: string, next: RoomChatMessage | { failed: string }): void;\n addReaction(reaction: RoomReaction): void;\n /** Drops reactions past their TTL. Called by the UI's animation frame. */\n expireReactions(now: number): void;\n /**\n * The recording reached a state, and WHO says so. `\"local\"` is this browser's\n * optimistic `starting`/`stopping`; `\"room\"` is the server's packet on the\n * media plane; `\"record\"` is the durable row. Precedence is D16's: the\n * database's word stands until the database changes it.\n */\n setRecording(\n state: RecordingState,\n by: UserId | null,\n source: RecordingSource,\n recordingId?: string | null,\n ): void;\n upsertLobby(entry: LobbyEntry): void;\n setAdmission(identity: ParticipantIdentity, state: AdmissionState, by: UserId | null): void;\n /**\n * Teach the name book. Every source that knows a name calls this, and only\n * a REAL name is learned — an identity handed back as its own name is not one.\n */\n learn(entries: readonly { identity: ParticipantIdentity; displayName?: string | null }[]): void;\n /** A live caption from the media plane. A HINT (D16). */\n ingestCaption(segment: TranscriptSegment): void;\n /** A durable `meet_transcript_segments` row. THE TRANSCRIPT (D16). */\n ingestRecord(segment: TranscriptSegment): void;\n /** Durable notes are re-written in place — a re-run rewrites the same ids. */\n upsertNote(note: MeetingNote): void;\n /** The wrap-up pass finished. Announced on the media plane only (MRI-A12). */\n announceWrapUp(announcement: WrapUpAnnouncement): void;\n /** Record what a local track ACTUALLY did — published, or refused and why. */\n setLocalTrack(kind: \"microphone\" | \"camera\", state: LocalTrackState): void;\n reset(): void;\n}\n\nexport function createMeetStore(initial: Partial<MeetSnapshot> = {}): MeetStore {\n let state: MeetSnapshot = { ...EMPTY, ...initial };\n const listeners = new Set<() => void>();\n\n function commit(next: MeetSnapshot): void {\n if (next === state) return;\n state = next;\n for (const listener of listeners) listener();\n }\n\n return {\n snapshot: () => state,\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n patch(next) {\n if (\"phase\" in next || \"reason\" in next || \"phaseNotice\" in next) {\n // The type forbids it; this catches a cast. The phase has ONE writer.\n throw new TypeError(\"meet store: the phase moves only through store.dispatch(event)\");\n }\n commit({ ...state, ...next });\n },\n dispatch(event, detail) {\n const to = nextPhase(state.phase, event);\n if (to === null) return false;\n const reason = detail?.reason !== undefined ? detail.reason : reasonOf(to);\n const phaseNotice = detail?.notice ?? null;\n if (to === state.phase && reason === state.reason && phaseNotice === state.phaseNotice) return true;\n commit({ ...state, phase: to, reason, phaseNotice });\n return true;\n },\n setParticipants(participants) {\n // The roster TEACHES the book first — a participant whose token carried a\n // real name is where most names come from — and every roster entry is then\n // RESOLVED back through it, so a participant the roster cannot name still\n // gets the best name a durable row or a caption ever taught (MRI-A18).\n const names = learnNames(state.names, participants);\n const quality = new Map(state.participants.map((p) => [p.identity, p.quality]));\n commit({\n ...state,\n names,\n participants: participants.map((participant) => ({\n ...participant,\n // 🚨 THE ONE ROSTER RESOLUTION. Every surface that renders a person —\n // the tile, the People row, the header summary, every Pin/Mute label —\n // reads `participant.displayName`, so this is the single place a raw\n // identity could reach a screen, and it is the place that asks the\n // name book. `roster-names.test.ts` fails if a token ever gets past it.\n displayName: displayNameFor(names, participant.identity, participant.displayName),\n quality:\n participant.quality === \"unknown\"\n ? quality.get(participant.identity) ?? \"unknown\"\n : participant.quality,\n })),\n });\n },\n setSpeaking(identities) {\n const speaking = new Set(identities);\n commit({\n ...state,\n activeSpeakers: identities,\n participants: state.participants.map((participant) =>\n participant.speaking === speaking.has(participant.identity)\n ? participant\n : { ...participant, speaking: speaking.has(participant.identity) },\n ),\n });\n },\n setQuality(identity, quality) {\n commit({\n ...state,\n participants: state.participants.map((participant) =>\n participant.identity === identity ? { ...participant, quality } : participant,\n ),\n });\n },\n addChat(message) {\n if (state.chat.some((existing) => existing.id === message.id)) return;\n const chat = [...state.chat, message];\n // A chat message carries its sender's name — another source, same book.\n const names = learnName(state.names, message.identity, message.displayName);\n commit({ ...state, chat: chat.slice(-CHAT_LIMIT), names });\n },\n loadChat(messages) {\n const known = new Set(state.chat.map((message) => message.id));\n const merged = [...messages.filter((message) => !known.has(message.id)), ...state.chat].sort(\n (a, b) => (a.at < b.at ? -1 : a.at > b.at ? 1 : 0),\n );\n const names = learnNames(\n state.names,\n messages.map((message) => ({ identity: message.identity, displayName: message.displayName })),\n );\n commit({ ...state, chat: merged.slice(-CHAT_LIMIT), chatLoaded: true, chatHistoryError: null, names });\n },\n settleChat(localId, next) {\n const index = state.chat.findIndex((message) => message.id === localId);\n if (index === -1) return;\n const current = state.chat[index] as RoomChatMessage;\n let replacement: RoomChatMessage;\n if (\"failed\" in next && typeof next.failed === \"string\" && !(\"id\" in next)) {\n replacement = { ...current, failed: next.failed };\n } else {\n replacement = next as RoomChatMessage;\n }\n // The saved copy may ALREADY be here (the room packet beat the response):\n // then the pending one simply goes.\n const duplicate =\n replacement.id !== localId && state.chat.some((message) => message.id === replacement.id);\n const chat = duplicate\n ? state.chat.filter((message) => message.id !== localId)\n : state.chat.map((message, position) => (position === index ? replacement : message));\n commit({ ...state, chat });\n },\n addReaction(reaction) {\n commit({ ...state, reactions: [...state.reactions, reaction] });\n },\n expireReactions(now) {\n const live = state.reactions.filter((reaction) => now - reaction.at < REACTION_TTL_MS);\n if (live.length === state.reactions.length) return;\n commit({ ...state, reactions: live });\n },\n setRecording(next, by, source, recordingId = null) {\n // 🚨 THE DATABASE WINS (D16). A member receives every transition TWICE —\n // the `meet_recordings` row over Postgres Changes and the\n // room's replicated state — and whichever is slower\n // must not be able to drag the indicator backwards.\n //\n // The rule is about ONE recording, not about the meeting: a meeting can\n // be recorded again, and the second recording's first word is not a\n // contradiction of the first one's ending. So the durable state stands\n // against anything that is about the SAME recording, and against a\n // sourceless update while a recording is still in flight.\n // `record` always wins. The other two are refused when they would\n // contradict the durable word rather than continue past it:\n //\n // * `local` is this browser acting NOW — a host pressing Start again on a\n // meeting whose last recording is over. It is refused only while a\n // recording is genuinely in flight, where a stale optimistic patch\n // could drag a live indicator backwards.\n // * `room` carries the recording's id, so a LATE packet about the\n // recording that just finished is told apart from the FIRST packet of\n // the next one. Same recording: refused. Different recording: refused\n // only while the durable one is still in flight.\n if (state.recordingSource === \"record\" && source !== \"record\") {\n const aboutTheSameRecording =\n source === \"room\" &&\n (recordingId === null ||\n state.recordingId === null ||\n recordingId === state.recordingId);\n if (aboutTheSameRecording || isRecordingInFlight(state.recording)) return;\n }\n if (\n state.recording === next &&\n state.recordingStartedBy === by &&\n state.recordingSource === source &&\n state.recordingId === (recordingId ?? state.recordingId)\n ) {\n // Identity-stable: an unchanged state must not wake every subscriber.\n return;\n }\n commit({\n ...state,\n recording: next,\n recordingStartedBy: by,\n recordingSource: source,\n recordingId: recordingId ?? state.recordingId,\n });\n },\n upsertLobby(entry) {\n const index = state.lobby.findIndex((existing) => existing.identity === entry.identity);\n const lobby =\n index === -1\n ? [...state.lobby, entry]\n : state.lobby.map((existing, position) => (position === index ? entry : existing));\n commit({ ...state, lobby });\n },\n setAdmission(identity, admissionState, by) {\n commit({\n ...state,\n lobby: state.lobby.map((entry) =>\n entry.identity === identity\n ? {\n ...entry,\n state: admissionState,\n decidedAt: new Date().toISOString(),\n decidedBy: by,\n }\n : entry,\n ),\n });\n },\n learn(entries) {\n const names = learnNames(state.names, entries);\n // Identity-stable: learning nothing must not wake every subscriber, and\n // this is called on every roster event and every durable row.\n if (names === state.names) return;\n commit({ ...state, names });\n },\n ingestCaption(segment) {\n // A caption's roster lookup is a source of names like any other.\n const names = learnName(state.names, segment.identity, segment.speaker);\n const transcript = mergeCaption(state.transcript, segment);\n if (transcript === state.transcript && names === state.names) return;\n commit({ ...state, transcript, names });\n },\n ingestRecord(segment) {\n // 🚨 THE DURABLE ROW'S `speaker` IS THE BEST NAME THERE IS: the note-taker\n // wrote it from `meet_participants`, which is where \"Ada Lovelace\" lived\n // while the guest's screen was printing `guest:c8-spk-1`.\n const names = learnName(state.names, segment.identity, segment.speaker);\n const transcript = mergeRecord(state.transcript, segment);\n if (transcript === state.transcript && names === state.names) return;\n commit({ ...state, transcript, names });\n },\n upsertNote(note) {\n const index = state.notes.findIndex((existing) => existing.id === note.id);\n if (index === -1) {\n commit({ ...state, notes: [...state.notes, note] });\n return;\n }\n const existing = state.notes[index];\n // Identity-stable: an unchanged note must not wake `useSyncExternalStore`.\n // 🚨 `source` is part of \"unchanged\" and that is load-bearing, not\n // cosmetic: when the room copy and the database copy carry the same words,\n // dropping the second arrival would leave the note marked `\"room\"` forever\n // — and the precedence rule below would then let a LATER stale packet\n // overwrite a row the database had already confirmed.\n if (\n existing !== undefined &&\n existing.text === note.text &&\n existing.kind === note.kind &&\n existing.supersededBy === note.supersededBy &&\n existing.source === note.source\n ) {\n return;\n }\n // 🚨 THE DATABASE WINS (D16, MRI-A12). The identical row arrives on two\n // transports — a `meet_notes` row over Postgres Changes and a\n // `matrx.meet.note` packet on the media plane — and whichever is slower\n // must not be able to undo the other. A `\"room\"` copy never overwrites a\n // `\"record\"` one; a `\"record\"` copy always overwrites a `\"room\"` one.\n // Without this, a re-run that rewrote a note could flip a member's screen\n // back to the superseded wording purely on arrival order.\n if (existing !== undefined && existing.source === \"record\" && note.source === \"room\") {\n return;\n }\n commit({\n ...state,\n notes: state.notes.map((entry, position) => (position === index ? note : entry)),\n });\n },\n announceWrapUp(announcement) {\n const current = state.wrapUp;\n if (current !== null && current.at === announcement.at) return;\n commit({ ...state, wrapUp: announcement });\n },\n setLocalTrack(kind, next) {\n const current = state.media[kind];\n // Identity-stable: an unchanged track must not wake `useSyncExternalStore`.\n if (\n current.enabled === next.enabled &&\n current.unavailable?.code === next.unavailable?.code &&\n current.unavailable?.reason === next.unavailable?.reason &&\n current.unavailable?.message === next.unavailable?.message\n ) {\n return;\n }\n commit({\n ...state,\n media: { ...state.media, [kind]: next },\n // The person turned their own microphone on again: the host's mute is over.\n ...(kind === \"microphone\" && next.enabled && state.mutedByHost ? { mutedByHost: false } : {}),\n });\n },\n reset() {\n // The phase goes back through THE MACHINE like every other move (`teardown`: the engine has\n // already closed its connection); everything else returns to empty. A person's own media\n // choices outlive a room: they are theirs, per browser.\n const phase = nextPhase(state.phase, { type: \"reset\", teardown: true }) ?? EMPTY.phase;\n commit({ ...EMPTY, effects: state.effects, phase, reason: reasonOf(phase), phaseNotice: null });\n },\n };\n}\n","/**\n * THE PLATFORM'S OWN VOICE IN THE ROOM — and the ONLY live feed a guest has.\n *\n * 🚨 **Why this file exists.** Everything durable a meeting produces — a live\n * note, a decision, an action item, the note-taker's state, the ending — lands\n * in `communication.meet_*`, and a signed-in member learns about it over\n * Postgres Changes. A GUEST cannot: `anon` holds no SELECT grant on\n * `meet_notes`, `meet_transcript_segments` or `meet_participants`, and MRI-A10\n * settled that widening it is forbidden, because those are org-scoped tables and\n * a meeting link is not org membership. A guest's authority is the ROOM TOKEN.\n * So the platform publishes the same rows into the room, where the token is the\n * authorization, and this file turns those packets into exactly the same store\n * calls the database path makes.\n *\n * Three rules hold it together:\n *\n * 1. **Same row, same projection, same reducer.** A `matrx.meet.note` packet\n * carries a `meet_notes` row and is projected by `projectNote` — the very\n * function the Postgres Changes handler uses — then handed to\n * `store.upsertNote`. A guest and a member therefore converge on one code\n * path rather than on two implementations that agree until they don't.\n * 2. **Keyed by the row id, so both copies are one thing.** A member receives\n * every note TWICE. They render once because the id is the row's id on both\n * transports, and where the two could disagree the database wins\n * (`store.upsertNote`'s precedence rule, D16).\n * 3. **`matrx.meet.*` is the server's namespace and nothing else's.** LiveKit\n * does not validate a data packet's sender, so any participant could publish\n * any bytes under any topic. What makes these trustworthy is that publishing\n * them requires a `roomAdmin` grant, which only the server's own control\n * token carries — a participant token cannot claim it. The in-room control\n * plane participants DO publish (hand, reaction, chat) lives under `mx.*`\n * and is handled separately, precisely so the two can never be confused.\n */\n\nimport { applyCollabPacket } from \"./collab\";\nimport { projectNote } from \"./repository\";\nimport { effectiveRole, type MeetStore, type RoomChatMessage } from \"./store\";\nimport {\n asMeetingId,\n asParticipantIdentity,\n ROLE_RANK,\n type JsonObject,\n type MeetingRecord,\n type ParticipantRole,\n type RecordingState,\n type UserId,\n type WrapUpAnnouncement,\n} from \"./types\";\n\n/** Pinned against `RecordingState`; an unknown word is dropped, never shown. */\nconst RECORDING_STATES: readonly RecordingState[] = [\n \"idle\",\n \"starting\",\n \"recording\",\n \"stopping\",\n \"processing\",\n \"available\",\n \"expired\",\n \"failed\",\n];\n\n/**\n * The server's namespace. Pinned against `aidream/services/meet/livekit.py`'s\n * `ROOM_EVENT_PREFIX` — a rename on either side must break a named test here.\n */\nexport const ROOM_EVENT_PREFIX = \"matrx.meet.\";\n\nexport const ROOM_EVENTS = {\n /** One durable `meet_notes` row was written. Payload `{ note: <row> }`. */\n note: \"matrx.meet.note\",\n /** `meet_meetings.metadata.note_taker` changed. Payload `{ meeting_id, note_taker }`. */\n noteTaker: \"matrx.meet.note_taker\",\n /** A wrap-up pass finished. Payload `{ meeting_id, note_ids, segment_count }`. */\n wrapUp: \"matrx.meet.wrap_up\",\n /** The meeting ended for everyone. Payload `{ meeting_id, ended_at, trigger }`. */\n ended: \"matrx.meet.ended\",\n /**\n * A chat message to everyone was SAVED (Meet wave 2). Payload\n * `{ message: { id, meeting_id, sender_identity, sender_name, body, created_at } }`.\n */\n chat: \"matrx.meet.chat\",\n /**\n * A host or co-host acted (Meet wave 2). Payload `{ meeting_id, kind, … }`:\n * `muted {source, by}` · `ask-unmute {by}` · `removed {by}` — addressed to the\n * person concerned; `role {identity, role}` — to everyone.\n */\n moderation: \"matrx.meet.moderation\",\n /**\n * A meeting ROOM ITEM changed (Meet wave 5): a poll, a Q&A question, a whiteboard\n * stroke or the breakout plan. Payload `{ meeting_id, item }` · `{ removed: [ids] }` ·\n * `{ hidden: [ids] }` · `{ cleared: \"whiteboard\" }` · `{ broadcast }` · `{ item, move }`.\n * A host's fuller copy of a poll or question carries `audience: \"hosts\"`.\n */\n collab: \"matrx.meet.collab\",\n} as const;\n\nexport type RoomEventTopic = (typeof ROOM_EVENTS)[keyof typeof ROOM_EVENTS];\n\nexport function isRoomEventTopic(topic: string): boolean {\n return topic.startsWith(ROOM_EVENT_PREFIX);\n}\n\ntype Payload = Record<string, unknown>;\n\nfunction object(value: unknown): Payload | null {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Payload)\n : null;\n}\n\nfunction text(value: unknown): string | null {\n return typeof value === \"string\" && value.length > 0 ? value : null;\n}\n\n/**\n * What happened to one packet. Returned rather than logged from inside, so the\n * room engine can report a drop with its own diagnostic channel and a test can\n * assert on it — a packet that is silently ignored is indistinguishable from\n * one that never arrived, which is the failure this whole item is about.\n */\nexport interface RoomEventOutcome {\n readonly handled: boolean;\n /** Present when `handled` is false. Says what was wrong, in a sentence. */\n readonly ignored?: string;\n}\n\nconst HANDLED: RoomEventOutcome = { handled: true };\n\nfunction ignored(reason: string): RoomEventOutcome {\n return { handled: false, ignored: reason };\n}\n\nexport interface ApplyRoomEventArgs {\n readonly store: MeetStore;\n readonly topic: string;\n /** The packet's already-parsed JSON body. */\n readonly payload: unknown;\n readonly now?: (() => number) | undefined;\n}\n\n/**\n * Apply one `matrx.meet.*` packet to the store. Never throws.\n *\n * A malformed packet is DROPPED with a reason, never partially applied: half a\n * note on a guest's screen is worse than no note, because the guest cannot tell\n * which half they are missing.\n */\nexport function applyRoomEvent(args: ApplyRoomEventArgs): RoomEventOutcome {\n const { store, topic } = args;\n if (!isRoomEventTopic(topic)) {\n return ignored(`${topic} is not a Meet room event`);\n }\n const payload = object(args.payload);\n if (payload === null) {\n return ignored(`${topic} carried no object payload`);\n }\n const now = args.now ?? (() => Date.now());\n\n switch (topic) {\n case ROOM_EVENTS.note:\n return applyNote(store, payload);\n case ROOM_EVENTS.noteTaker:\n return applyNoteTaker(store, payload);\n case ROOM_EVENTS.wrapUp:\n return applyWrapUp(store, payload, now);\n case ROOM_EVENTS.ended:\n return applyEnded(store, payload);\n case ROOM_EVENTS.chat:\n return applyChat(store, payload);\n case ROOM_EVENTS.moderation:\n return applyModeration(store, payload, now);\n case ROOM_EVENTS.collab:\n return applyCollab(store, payload, now);\n default:\n // A NEWER SERVER, not a bug. Ignoring an unknown event under our own\n // prefix is what lets the platform ship a new one without breaking every\n // client that has not been redeployed.\n return ignored(`${topic} is not known to this version of @ai-matrx/meet`);\n }\n}\n\nfunction applyCollab(store: MeetStore, payload: Payload, now: () => number): RoomEventOutcome {\n const snapshot = store.snapshot();\n const meeting = snapshot.meeting;\n if (meeting !== null && text(payload[\"meeting_id\"]) !== null && payload[\"meeting_id\"] !== meeting.id) {\n return ignored(\"a collab event for a different meeting than this room is showing\");\n }\n const result = applyCollabPacket(snapshot.collab, payload, {\n localIdentity: snapshot.localIdentity,\n isHost: ROLE_RANK[effectiveRole(snapshot)] >= ROLE_RANK.cohost,\n now: now(),\n });\n if (result.next === null) return ignored(result.dropped ?? \"a collab packet that changed nothing\");\n store.patch({ collab: result.next });\n return HANDLED;\n}\n\nfunction applyNote(store: MeetStore, payload: Payload): RoomEventOutcome {\n const row = object(payload[\"note\"]);\n if (row === null) return ignored(\"a note event with no `note` row\");\n if (text(row[\"id\"]) === null || text(row[\"meeting_id\"]) === null) {\n // Without the row id there is no dedupe key, and appending a note that can\n // never be matched to its database copy is exactly the double-render bug.\n return ignored(\"a note row with no id or meeting_id\");\n }\n const meeting = store.snapshot().meeting;\n if (meeting !== null && row[\"meeting_id\"] !== meeting.id) {\n return ignored(\"a note for a different meeting than this room is showing\");\n }\n try {\n // THE SAME PROJECTION THE DATABASE PATH USES — one definition of what a\n // note row means — re-stamped as the room copy so the store's precedence\n // rule can keep the database's word on top.\n store.upsertNote({ ...projectNote(row), source: \"room\" });\n } catch (error) {\n return ignored(`a note row this client could not read: ${String(error)}`);\n }\n return HANDLED;\n}\n\nfunction applyNoteTaker(store: MeetStore, payload: Payload): RoomEventOutcome {\n const noteTaker = object(payload[\"note_taker\"]);\n if (noteTaker === null) return ignored(\"a note-taker event with no state\");\n const meeting = store.snapshot().meeting;\n if (meeting === null) {\n // An ad-hoc call room has no meeting record to hang the state on, and\n // there is no note-taker in one. Stated, not silent.\n return ignored(\"a note-taker state with no meeting record to attach it to\");\n }\n const meetingId = text(payload[\"meeting_id\"]);\n if (meetingId !== null && meetingId !== meeting.id) {\n return ignored(\"a note-taker state for a different meeting\");\n }\n // MERGED, NOT REPLACED. The packet carries only the `note_taker` object — the\n // two keys a surface renders — because the rest of the row names people and\n // organizations a guest is not authorized to learn. Everything else on the\n // meeting record stays exactly as the join backfill left it.\n const next: MeetingRecord = {\n ...meeting,\n metadata: { ...meeting.metadata, note_taker: noteTaker as JsonObject },\n };\n store.patch({ meeting: next });\n return HANDLED;\n}\n\nfunction applyWrapUp(\n store: MeetStore,\n payload: Payload,\n now: () => number,\n): RoomEventOutcome {\n const meetingId = text(payload[\"meeting_id\"]);\n if (meetingId === null) return ignored(\"a wrap-up event with no meeting id\");\n const meeting = store.snapshot().meeting;\n if (meeting !== null && meetingId !== meeting.id) {\n return ignored(\"a wrap-up for a different meeting\");\n }\n const rawIds = payload[\"note_ids\"];\n const noteIds = Array.isArray(rawIds)\n ? rawIds.filter((value): value is string => typeof value === \"string\")\n : [];\n const segmentCount = payload[\"segment_count\"];\n const announcement: WrapUpAnnouncement = {\n meetingId: asMeetingId(meetingId),\n noteIds,\n segmentCount: typeof segmentCount === \"number\" ? segmentCount : 0,\n at: new Date(now()).toISOString(),\n };\n store.announceWrapUp(announcement);\n return HANDLED;\n}\n\n/**\n * S8: a recording's state arrives as REPLICATED ROOM STATE (the room's metadata, written only by\n * aidream's `room_state.publish_room_state`), which LiveKit delivers on join and after every\n * reconnect — never a one-shot packet a late joiner misses. Called by the engine's room-metadata\n * handler with the metadata's `recording` object and `meeting_id`.\n */\nexport function applyRecordingState(store: MeetStore, payload: Payload): RoomEventOutcome {\n const row = object(payload[\"recording\"]);\n if (row === null) return ignored(\"room state carries no recording\");\n const meetingId = text(payload[\"meeting_id\"]);\n const meeting = store.snapshot().meeting;\n if (meeting !== null && meetingId !== null && meetingId !== meeting.id) {\n return ignored(\"a recording state for a different meeting than this room is showing\");\n }\n const state = text(row[\"state\"]);\n if (state === null || !RECORDING_STATES.includes(state as RecordingState)) {\n // An UNKNOWN state is dropped rather than rendered: a recording indicator\n // that says a word nobody defined is worse than one that says nothing.\n return ignored(`a recording state this version does not know: ${String(row[\"state\"])}`);\n }\n const by = text(row[\"started_by\"]);\n // `\"room\"` — the durable row outranks this the moment a member's\n // subscription delivers it (D16, `store.setRecording`).\n store.setRecording(\n state as RecordingState,\n by === null ? null : (by as UserId),\n \"room\",\n text(row[\"id\"]),\n );\n return HANDLED;\n}\n\nfunction applyEnded(store: MeetStore, payload: Payload): RoomEventOutcome {\n const meetingId = text(payload[\"meeting_id\"]);\n const meeting = store.snapshot().meeting;\n if (meeting === null) return ignored(\"an ended event with no meeting record\");\n if (meetingId !== null && meetingId !== meeting.id) {\n return ignored(\"an ended event for a different meeting\");\n }\n // 🚨 THE LAST THING A GUEST HEARS. Ending a meeting deletes the LiveKit room,\n // so the server publishes this in the moment BEFORE the delete — and a client\n // that ignored it would keep saying the meeting is live while everyone is\n // disconnected. The `left` phase that follows says nothing about WHY.\n const endedAt = text(payload[\"ended_at\"]) ?? new Date().toISOString();\n if (meeting.endedAt === endedAt) return HANDLED;\n store.patch({ meeting: { ...meeting, endedAt } });\n return HANDLED;\n}\n\n/** One saved chat row as the room sees it. Shared with the history read. */\nexport function chatFromRow(row: Payload): RoomChatMessage | null {\n const id = text(row[\"id\"]);\n const identity = text(row[\"sender_identity\"]);\n const body = text(row[\"body\"]);\n if (id === null || identity === null || body === null) return null;\n return {\n id,\n identity: asParticipantIdentity(identity),\n displayName: text(row[\"sender_name\"]) ?? \"Participant\",\n text: body,\n at: text(row[\"created_at\"]) ?? new Date().toISOString(),\n // `false` only from a server whose chat table has not landed: the room heard\n // it, nobody will see it later — and the line says so.\n saved: row[\"saved\"] !== false,\n };\n}\n\nfunction applyChat(store: MeetStore, payload: Payload): RoomEventOutcome {\n const row = object(payload[\"message\"]);\n if (row === null) return ignored(\"a chat event with no `message` row\");\n const meeting = store.snapshot().meeting;\n if (meeting !== null && text(row[\"meeting_id\"]) !== null && row[\"meeting_id\"] !== meeting.id) {\n return ignored(\"a chat message for a different meeting\");\n }\n const message = chatFromRow(row);\n if (message === null) return ignored(\"a chat row with no id, sender or body\");\n store.addChat(message);\n return HANDLED;\n}\n\n/** Sentences for what a host did to THIS participant. */\nfunction moderationText(kind: string, payload: Payload): string | null {\n const by = text(payload[\"by\"]) ?? \"The host\";\n switch (kind) {\n case \"muted\": {\n // Meet names the person who did it (\"<name> muted you\"); the role word only stands in when the server sent no name.\n const source = text(payload[\"source\"]) ?? \"microphone\";\n return source === \"microphone\"\n ? `${by} muted you.`\n : source === \"camera\"\n ? `${by} turned off your camera.`\n : `${by} stopped your screen share.`;\n }\n case \"ask-unmute\":\n return `${by} is asking you to unmute.`;\n case \"removed\":\n return `${by} removed you from this meeting.`;\n default:\n return null;\n }\n}\n\nfunction applyModeration(store: MeetStore, payload: Payload, now: () => number): RoomEventOutcome {\n const snapshot = store.snapshot();\n const meetingId = text(payload[\"meeting_id\"]);\n if (snapshot.meeting !== null && meetingId !== null && meetingId !== snapshot.meeting.id) {\n return ignored(\"a moderation event for a different meeting\");\n }\n const kind = text(payload[\"kind\"]);\n switch (kind) {\n case \"role\": {\n const identity = text(payload[\"identity\"]);\n const role = text(payload[\"role\"]);\n if (identity === null || role === null || !ROLES.includes(role as ParticipantRole)) {\n return ignored(\"a role event with no identity or an unknown role\");\n }\n // THE SERVER CHANGED ITS OWN DECISION. The token's role was minted at join;\n // a co-host made or unmade, or a host handed over, must re-grant or\n // UN-grant this screen's powers now — `effectiveRole` takes the stronger of\n // token and roster, so a demotion that left the token alone would leave the\n // old host holding host controls the server refuses.\n if (identity === snapshot.localIdentity) {\n store.patch({\n grants: { ...snapshot.grants, role: role as ParticipantRole },\n moderationNotice: {\n kind: \"role\",\n text:\n role === \"host\"\n ? \"You are now the host.\"\n : role === \"cohost\"\n ? \"You are now a co-host.\"\n : role === \"presenter\"\n ? \"You are now a presenter.\"\n : snapshot.grants.role === \"presenter\"\n ? \"You are now a participant: you can no longer present.\"\n : \"You are no longer a co-host.\",\n at: now(),\n },\n });\n }\n return HANDLED;\n }\n case \"share-ended\": {\n // share-takeover / share-who-can: the SERVER took this person's share down and says why.\n const reason = text(payload[\"reason\"]) === \"not_allowed\" ? \"not_allowed\" : \"replaced\";\n const by = text(payload[\"by\"]);\n store.patch({\n share: { state: \"ended\", reason, remedy: null, at: now(), ...(by !== null ? { by } : {}) },\n });\n return HANDLED;\n }\n case \"muted\":\n case \"ask-unmute\":\n case \"removed\": {\n const sentence = moderationText(kind, payload);\n if (sentence === null) return ignored(\"a moderation event with no sentence\");\n store.patch({\n moderationNotice: { kind, text: sentence, at: now() },\n ...(kind === \"muted\" && (text(payload[\"source\"]) ?? \"microphone\") === \"microphone\" ? { mutedByHost: true } : {}),\n });\n return HANDLED;\n }\n default:\n return ignored(`a moderation kind this version does not know: ${String(kind)}`);\n }\n}\n\nconst ROLES: readonly ParticipantRole[] = [\"host\", \"cohost\", \"presenter\", \"participant\", \"viewer\"];\n","/**\n * DEVICE MANAGEMENT — the part users blame the product for.\n *\n * Every rule here exists because it is a support ticket somewhere:\n *\n * - **Labels are empty until permission is granted.** `enumerateDevices()`\n * happily returns three cameras with blank labels before the prompt. A picker\n * rendering \"Camera 1 / Camera 2\" is the classic symptom; we report\n * `labelsHidden` so the UI can say *why* instead.\n * - **A remembered device can vanish.** Unplug a headset between meetings and a\n * stored `deviceId` produces `OverconstrainedError`. The manager falls back to\n * the system default AND SAYS SO — a silent fallback is how someone joins on\n * the laptop mic while believing they are on their headset.\n * - **Selections persist per KIND, per device.** Injected storage (C10:\n * persistence-injected — this package's core purpose is not storage), with a\n * memory fallback that announces itself rather than pretending to be durable.\n * - **Enumeration is repeated on `devicechange`.** A picker that lists what was\n * plugged in at mount is wrong within seconds of a headset arriving.\n */\n\nimport { MeetError, normalizeDeviceError } from \"./errors\";\n\nexport type DeviceKind = \"audioinput\" | \"videoinput\" | \"audiooutput\";\n\nexport interface MeetDevice {\n readonly deviceId: string;\n readonly kind: DeviceKind;\n readonly label: string;\n}\n\nexport interface DeviceSnapshot {\n readonly devices: Readonly<Record<DeviceKind, readonly MeetDevice[]>>;\n readonly selected: Readonly<Record<DeviceKind, string | null>>;\n /** True when the browser is hiding labels because permission was not granted. */\n readonly labelsHidden: boolean;\n readonly permission: \"unknown\" | \"granted\" | \"denied\";\n /** Set when a remembered device was gone and we fell back. Shown, not swallowed. */\n readonly fallbackNotice: string | null;\n}\n\nexport interface DeviceStorage {\n read(): Record<string, string>;\n write(value: Record<string, string>): void;\n readonly durable: boolean;\n}\n\nexport interface MediaDevicesLike {\n enumerateDevices(): Promise<\n readonly { deviceId: string; kind: string; label: string }[]\n >;\n getUserMedia(constraints: { audio?: unknown; video?: unknown }): Promise<{\n getTracks(): readonly { stop(): void }[];\n }>;\n addEventListener?: ((type: \"devicechange\", handler: () => void) => void) | undefined;\n removeEventListener?: ((type: \"devicechange\", handler: () => void) => void) | undefined;\n}\n\nexport interface DeviceManagerOptions {\n media: MediaDevicesLike;\n storage?: DeviceStorage | undefined;\n onChange?: ((snapshot: DeviceSnapshot) => void) | undefined;\n}\n\nexport interface DeviceManager {\n /**\n * 🚨 THE SAME REFERENCE until something actually changes. This is a\n * `useSyncExternalStore` `getSnapshot` source (see `useDeviceSnapshot`), and\n * React compares snapshots by IDENTITY: a fresh object literal per call makes\n * every render report a change, which React reports as *\"The result of\n * getSnapshot should be cached\"* and then kills the tree with *\"Maximum\n * update depth exceeded\"*. `createMeetStore()` has always held this contract;\n * 0.2.0 shipped without it here and every room crashed at `<PreJoin>`.\n */\n snapshot(): DeviceSnapshot;\n /** Enumerate without prompting. Labels may be hidden; that is reported. */\n refresh(): Promise<DeviceSnapshot>;\n /** Prompt, then enumerate with real labels. The pre-join screen calls this. */\n requestPermission(kinds: { audio: boolean; video: boolean }): Promise<DeviceSnapshot>;\n select(kind: DeviceKind, deviceId: string): DeviceSnapshot;\n /** The id to actually use — the remembered one, or the default when it is gone. */\n resolve(kind: DeviceKind): string | null;\n subscribe(listener: (snapshot: DeviceSnapshot) => void): () => void;\n dispose(): void;\n}\n\nconst KINDS: readonly DeviceKind[] = [\"audioinput\", \"videoinput\", \"audiooutput\"];\n\nfunction isDeviceKind(value: string): value is DeviceKind {\n return value === \"audioinput\" || value === \"videoinput\" || value === \"audiooutput\";\n}\n\n/** localStorage when it exists; memory otherwise — and it says which. */\nexport function createWebDeviceStorage(key = \"mx.meet.devices\"): DeviceStorage {\n const store = (globalThis as { localStorage?: Storage }).localStorage;\n if (store === undefined) return createMemoryDeviceStorage();\n return {\n durable: true,\n read() {\n try {\n const raw = store.getItem(key);\n if (raw === null) return {};\n const parsed: unknown = JSON.parse(raw);\n return typeof parsed === \"object\" && parsed !== null\n ? (parsed as Record<string, string>)\n : {};\n } catch {\n return {};\n }\n },\n write(value) {\n try {\n store.setItem(key, JSON.stringify(value));\n } catch {\n // A full or blocked storage must not break joining a call.\n }\n },\n };\n}\n\nexport function createMemoryDeviceStorage(): DeviceStorage {\n let value: Record<string, string> = {};\n return {\n durable: false,\n read: () => value,\n write: (next) => {\n value = next;\n },\n };\n}\n\nexport function createDeviceManager(options: DeviceManagerOptions): DeviceManager {\n const storage = options.storage ?? createMemoryDeviceStorage();\n const listeners = new Set<(snapshot: DeviceSnapshot) => void>();\n const stored = storage.read();\n\n let devices: Record<DeviceKind, readonly MeetDevice[]> = {\n audioinput: [],\n videoinput: [],\n audiooutput: [],\n };\n let selected: Record<DeviceKind, string | null> = {\n audioinput: stored[\"audioinput\"] ?? null,\n videoinput: stored[\"videoinput\"] ?? null,\n audiooutput: stored[\"audiooutput\"] ?? null,\n };\n let labelsHidden = true;\n let permission: DeviceSnapshot[\"permission\"] = \"unknown\";\n let fallbackNotice: string | null = null;\n\n // 🚨 THE MEMOIZED SNAPSHOT. `current` IS the snapshot; readers get this exact\n // object until `commit()` replaces it, which happens only when one of the\n // five fields above actually moved. See the `DeviceManager.snapshot` doc\n // comment for the crash this prevents.\n let current: DeviceSnapshot = {\n devices,\n selected,\n labelsHidden,\n permission,\n fallbackNotice,\n };\n\n function snapshot(): DeviceSnapshot {\n return current;\n }\n\n /** Rebuild the snapshot from the current state. Call after ANY mutation. */\n function commit(): DeviceSnapshot {\n current = { devices, selected, labelsHidden, permission, fallbackNotice };\n return current;\n }\n\n function emit(): DeviceSnapshot {\n const next = commit();\n options.onChange?.(next);\n for (const listener of listeners) listener(next);\n return next;\n }\n\n async function enumerate(): Promise<DeviceSnapshot> {\n let raw: readonly { deviceId: string; kind: string; label: string }[];\n try {\n raw = await options.media.enumerateDevices();\n } catch (error) {\n throw normalizeDeviceError(error, \"meet.enumerateDevices\");\n }\n const grouped: Record<DeviceKind, MeetDevice[]> = {\n audioinput: [],\n videoinput: [],\n audiooutput: [],\n };\n let anyLabel = false;\n for (const device of raw) {\n if (!isDeviceKind(device.kind)) continue;\n if (device.label.length > 0) anyLabel = true;\n grouped[device.kind].push({\n deviceId: device.deviceId,\n kind: device.kind,\n label: device.label,\n });\n }\n devices = grouped;\n labelsHidden = !anyLabel && raw.length > 0;\n\n // THE VANISHED-DEVICE FALLBACK. A remembered id that is no longer present\n // is dropped to the system default, and the notice is part of the snapshot\n // so the UI can say \"your headset is unplugged — using the built-in mic\".\n const lost: string[] = [];\n for (const kind of KINDS) {\n const current = selected[kind];\n if (current === null) continue;\n if (!grouped[kind].some((device) => device.deviceId === current)) {\n selected = { ...selected, [kind]: null };\n lost.push(kind);\n }\n }\n fallbackNotice =\n lost.length === 0\n ? null\n : `A remembered ${lost.join(\" and \")} device is no longer connected — using the ` +\n \"system default. Pick another in the device menu.\";\n return emit();\n }\n\n const onDeviceChange = (): void => {\n void enumerate();\n };\n options.media.addEventListener?.(\"devicechange\", onDeviceChange);\n\n return {\n snapshot,\n refresh: enumerate,\n async requestPermission(kinds) {\n if (!kinds.audio && !kinds.video) {\n throw new MeetError(\n \"misconfigured\",\n \"meet.requestPermission: asked for neither audio nor video\",\n \"Request at least one kind. Prompting for nothing silently resolves and leaves \" +\n \"labels hidden, which looks like a broken picker.\",\n );\n }\n try {\n const stream = await options.media.getUserMedia({\n ...(kinds.audio ? { audio: true } : {}),\n ...(kinds.video ? { video: true } : {}),\n });\n // 🚨 STOP THE PROBE STREAM. Holding it keeps the camera light on and,\n // on Windows, makes the real join fail with NotReadableError — the\n // device is busy with our own probe.\n for (const track of stream.getTracks()) track.stop();\n permission = \"granted\";\n } catch (error) {\n permission = \"denied\";\n emit();\n throw normalizeDeviceError(error, \"meet.requestPermission\");\n }\n return enumerate();\n },\n select(kind, deviceId) {\n selected = { ...selected, [kind]: deviceId };\n fallbackNotice = null;\n storage.write({\n ...storage.read(),\n [kind]: deviceId,\n });\n return emit();\n },\n resolve(kind) {\n const current = selected[kind];\n if (current !== null && devices[kind].some((device) => device.deviceId === current)) {\n return current;\n }\n return devices[kind][0]?.deviceId ?? null;\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n dispose() {\n options.media.removeEventListener?.(\"devicechange\", onDeviceChange);\n listeners.clear();\n },\n };\n}\n","/**\n * PRESENTATION HELPERS specific to a CALL. Pure, tested, and shared by every\n * surface — so a roster line reads the same in the meeting, in the history\n * list, and in the notification.\n *\n * The GENERIC formatters this file used to define — `formatDuration`,\n * `getInitials`, `avatarPaletteIndex` — moved to `@ai-matrx/kit/format` on\n * 2026-09-07 (duplication census H1). They were byte-for-byte duplicated in\n * `@ai-matrx/messaging`, and because two packages claimed them, ~65 host\n * twins across six repos had no correct owner to point at: no surface should\n * take a dependency on the video-calling package to print a duration. Nothing\n * is re-exported from here — import `@ai-matrx/kit/format` directly.\n */\n\nimport type { CallState, MeetParticipant } from \"./types\";\n\n/** \"Ana, Ben and 3 others\" — the roster line, never a raw array join. */\nexport function participantSummary(participants: readonly MeetParticipant[]): string {\n const names = participants.filter((p) => !p.isLocal).map((p) => p.displayName);\n if (names.length === 0) return \"Just you\";\n if (names.length === 1) return names[0] ?? \"\";\n if (names.length === 2) return `${names[0]} and ${names[1]}`;\n return `${names[0]}, ${names[1]} and ${names.length - 2} ${names.length - 2 === 1 ? \"other\" : \"others\"}`;\n}\n\n/** What a settled call says in history. One sentence per terminal state. */\nexport function callStateLabel(state: CallState, outgoing: boolean): string {\n switch (state) {\n case \"dialing\":\n case \"ringing\":\n return outgoing ? \"Calling…\" : \"Incoming call\";\n case \"connecting\":\n return \"Connecting…\";\n case \"active\":\n return \"In call\";\n case \"declined\":\n return outgoing ? \"Declined\" : \"You declined\";\n case \"missed\":\n return outgoing ? \"No answer\" : \"Missed call\";\n case \"busy\":\n return outgoing ? \"On another call\" : \"You were busy\";\n case \"cancelled\":\n return outgoing ? \"Cancelled\" : \"Cancelled by caller\";\n case \"ended\":\n return \"Call ended\";\n case \"failed\":\n return \"Call failed\";\n default:\n return \"Call\";\n }\n}\n\n/** \"2 waiting\" for the lobby badge — plural handled once, not per surface. */\nexport function countLabel(count: number, singular: string, plural = `${singular}s`): string {\n return `${count} ${count === 1 ? singular : plural}`;\n}\n","/**\n * THE MESSAGING SEAM — a call invitation is an ACTIONABLE MESSAGE (D8).\n *\n * `@ai-matrx/messaging` deliberately knows nothing about meetings; its action\n * registry exists so packages like this one can add a kind without messaging\n * learning about them. This file is that registration, and it is the whole\n * coupling between the two packages.\n *\n * Why an invitation is a message and not only a ring: a ring is EPHEMERAL. If\n * you were away from your desk, a broadcast that expired is gone, and \"someone\n * tried to reach me\" is exactly the information you most needed. The message is\n * the durable half — it is still in the thread tomorrow, with the outcome\n * stamped on it.\n *\n * 🚨 **THE PAYLOAD IS NOT AUTHORIZATION.** The chip's payload says what was\n * ASKED, never what is ALLOWED. The handler re-resolves the invite through the\n * repository (an auth-checked RPC) before it joins anything. Trusting the\n * payload would turn any message anyone can send into a room grant.\n *\n * Idempotence is the registry's, not ours: two tabs and a double tap settle\n * ONCE and share one receipt.\n */\n\nimport type { ActionHandler, ActionReceipt } from \"@ai-matrx/messaging\";\nimport type { CallCenter } from \"./calls\";\nimport { MeetError } from \"./errors\";\nimport type { MeetRepository } from \"./repository\";\nimport { asCallId, asUserId, type CallInvite, type CallMode } from \"./types\";\n\nexport const CALL_INVITE_KIND = \"meet.call-invite\";\nexport const CALL_INVITE_VERSION = 1;\n\nexport interface CallInvitePayload {\n readonly invite_id: string;\n readonly room_name: string;\n readonly mode: CallMode;\n readonly caller_name: string;\n readonly expires_at: string;\n}\n\n/** The `action_data` a caller attaches to the invitation message. */\nexport function callInviteAction(invite: CallInvite): {\n readonly kind: string;\n readonly version: number;\n readonly payload: CallInvitePayload;\n} {\n return {\n kind: CALL_INVITE_KIND,\n version: CALL_INVITE_VERSION,\n payload: {\n invite_id: invite.id,\n room_name: invite.roomName,\n mode: invite.mode,\n caller_name: invite.callerName,\n expires_at: invite.expiresAt,\n },\n };\n}\n\n/** The message body. Readable on a surface that cannot render the chips. */\nexport function callInviteText(invite: CallInvite): string {\n return invite.mode === \"video\"\n ? `${invite.callerName} started a video call`\n : `${invite.callerName} started an audio call`;\n}\n\nexport interface CallInviteHandlerOptions {\n calls: CallCenter;\n repository: MeetRepository;\n /** Called after a successful accept, with the room to enter. */\n onJoin: (invite: CallInvite) => void | Promise<void>;\n now?: (() => number) | undefined;\n}\n\nexport function createCallInviteHandler(\n options: CallInviteHandlerOptions,\n): ActionHandler<CallInvitePayload> {\n const now = options.now ?? (() => Date.now());\n\n return {\n kind: CALL_INVITE_KIND,\n versions: [CALL_INVITE_VERSION],\n choices: (payload) => {\n // An EXPIRED invite renders no chips. Offering \"Join\" for a call that\n // ended twenty minutes ago is a dead end with a friendly face.\n if (Date.parse(payload.expires_at) < now()) return [];\n return [\n { id: \"join\", label: payload.mode === \"video\" ? \"Join video\" : \"Join call\", icon: payload.mode === \"video\" ? \"video\" : \"phone\", tone: \"primary\" },\n { id: \"decline\", label: \"Decline\", tone: \"neutral\" },\n ];\n },\n summarize: (payload) =>\n payload.mode === \"video\"\n ? `${payload.caller_name} started a video call`\n : `${payload.caller_name} started an audio call`,\n execute: async (payload, context): Promise<ActionReceipt> => {\n const settledAt = new Date(now()).toISOString();\n const inviteId = asCallId(payload.invite_id);\n\n // RE-RESOLVE. The payload told us what to look for; the database tells us\n // whether it is still true and whether this actor may act on it.\n // `context.actorId` is messaging's branded UserId; ours is a distinct\n // brand over the same string. Re-branding through the constructor is the\n // honest crossing — a cast between two brands would compile today and\n // hide a genuine mismatch the day either package changes its identity.\n const pending = await options.repository.pendingInvites(asUserId(String(context.actorId)));\n const live = pending.find((invite) => invite.id === inviteId) ?? null;\n\n if (live === null) {\n return {\n kind: CALL_INVITE_KIND,\n messageId: context.messageId,\n actorId: context.actorId,\n outcome: \"unavailable\",\n label: \"This call has ended\",\n settledAt,\n detail:\n \"The invite is no longer ringing — it was answered elsewhere, cancelled, or it \" +\n \"timed out. The chips are replaced by the outcome rather than left clickable.\",\n };\n }\n\n if (context.choice === \"decline\") {\n await options.calls.decline(inviteId);\n return {\n kind: CALL_INVITE_KIND,\n messageId: context.messageId,\n actorId: context.actorId,\n outcome: \"declined\",\n label: \"Declined\",\n settledAt,\n };\n }\n\n const accepted = await options.calls.accept(inviteId, live.mode);\n await options.onJoin(accepted);\n return {\n kind: CALL_INVITE_KIND,\n messageId: context.messageId,\n actorId: context.actorId,\n outcome: \"applied\",\n label: \"Joined\",\n settledAt,\n };\n },\n };\n}\n\n/**\n * A meeting invitation — the scheduled twin. Deliberately NOT the same kind: a\n * scheduled meeting's chip must never say \"Join call\" three days early, and a\n * receiver that only understands calls must render NOTHING for it rather than a\n * button it cannot honor.\n */\nexport const MEETING_INVITE_KIND = \"meet.meeting-invite\";\nexport const MEETING_INVITE_VERSION = 1;\n\nexport interface MeetingInvitePayload {\n readonly meeting_id: string;\n readonly slug: string;\n readonly title: string;\n readonly scheduled_for: string | null;\n readonly link: string;\n}\n\nexport function meetingInviteAction(payload: MeetingInvitePayload): {\n readonly kind: string;\n readonly version: number;\n readonly payload: MeetingInvitePayload;\n} {\n return { kind: MEETING_INVITE_KIND, version: MEETING_INVITE_VERSION, payload };\n}\n\nexport interface MeetingInviteHandlerOptions {\n onOpen: (payload: MeetingInvitePayload) => void | Promise<void>;\n now?: (() => number) | undefined;\n}\n\nexport function createMeetingInviteHandler(\n options: MeetingInviteHandlerOptions,\n): ActionHandler<MeetingInvitePayload> {\n const now = options.now ?? (() => Date.now());\n return {\n kind: MEETING_INVITE_KIND,\n versions: [MEETING_INVITE_VERSION],\n // A DURABLE LINK NEVER EXPIRES (R3) — the chip stays useful after the\n // meeting too, because that is when people go looking for the notes.\n choices: () => [{ id: \"open\", label: \"Open meeting\", tone: \"primary\" }],\n summarize: (payload) =>\n payload.scheduled_for === null\n ? payload.title\n : `${payload.title} — ${payload.scheduled_for}`,\n execute: async (payload, context): Promise<ActionReceipt> => {\n if (payload.slug.length === 0) {\n throw new MeetError(\n \"invalid-response\",\n \"meet.meeting-invite: the payload carries no slug\",\n \"A meeting invitation without a durable slug cannot be opened. Fix the sender; \" +\n \"never synthesize a link on the receiving side.\",\n );\n }\n await options.onOpen(payload);\n return {\n kind: MEETING_INVITE_KIND,\n messageId: context.messageId,\n actorId: context.actorId,\n outcome: \"applied\",\n label: \"Opened\",\n settledAt: new Date(now()).toISOString(),\n };\n },\n };\n}\n","/**\n * THE STAGE — who is big, who is small, and who is not rendered at all.\n *\n * This is pure arithmetic on purpose: layout is the part of a meeting UI that\n * everyone re-derives inside a component and gets subtly wrong, and it is\n * trivially testable once it stops living in JSX.\n *\n * R5 SCALE HONESTY lives here. A 200-person meeting does not render 200 video\n * tiles — every browser dies, and LiveKit's adaptive stream cannot save a\n * layout that asked for 200 subscriptions. The grid PAGES, and the ordering is\n * deliberate rather than \"whatever the roster map iterated\":\n *\n * 1. a screen share (or an explicit pin) owns the stage;\n * 2. then the active speakers, most recent first;\n * 3. then anyone with a hand raised — they are asking to be seen;\n * 4. then cameras on before cameras off (an avatar tile is cheap);\n * 5. then stable by join time, so tiles do not shuffle every time someone\n * breathes. Shuffling tiles is the single most disorienting thing a\n * meeting UI can do.\n */\n\nimport { isObserverRole, type MeetParticipant, type ParticipantIdentity, type StageLayout } from \"./types\";\n\nexport interface StageArgs {\n readonly participants: readonly MeetParticipant[];\n readonly layout: StageLayout;\n readonly pinned: ParticipantIdentity | null;\n readonly activeSpeakers: readonly ParticipantIdentity[];\n /**\n * The participant a HOST spotlighted for everyone (Meet wave 2). A local pin\n * outranks it on this screen; a screen share outranks both.\n */\n readonly spotlight?: ParticipantIdentity | null | undefined;\n /** How many tiles this viewport can honestly render. Default 12. */\n readonly pageSize?: number;\n readonly page?: number;\n}\n\nexport interface Stage {\n /** The large tile, when the layout has one. */\n readonly feature: MeetParticipant | null;\n /** What actually renders as a tile, already paged. */\n readonly tiles: readonly MeetParticipant[];\n /** Everyone not on this page. Rendered as \"+N others\", never dropped silently. */\n readonly overflow: number;\n readonly page: number;\n readonly pageCount: number;\n /** True when a screen share, not a person, is the reason for the big tile. */\n readonly presenting: boolean;\n}\n\nexport const DEFAULT_PAGE_SIZE = 12;\n\nexport function orderParticipants(\n participants: readonly MeetParticipant[],\n activeSpeakers: readonly ParticipantIdentity[],\n): readonly MeetParticipant[] {\n const speakerRank = new Map<ParticipantIdentity, number>();\n activeSpeakers.forEach((identity, index) => speakerRank.set(identity, index));\n\n return [...participants].sort((a, b) => {\n const speakerA = speakerRank.get(a.identity) ?? Number.MAX_SAFE_INTEGER;\n const speakerB = speakerRank.get(b.identity) ?? Number.MAX_SAFE_INTEGER;\n if (speakerA !== speakerB) return speakerA - speakerB;\n if (a.handRaised !== b.handRaised) return a.handRaised ? -1 : 1;\n if (a.cameraEnabled !== b.cameraEnabled) return a.cameraEnabled ? -1 : 1;\n // STABLE from here down. Join time, then identity — never Math.random,\n // never map order, both of which reshuffle the grid on every event.\n if (a.joinedAt !== b.joinedAt) return a.joinedAt < b.joinedAt ? -1 : 1;\n return a.identity < b.identity ? -1 : 1;\n });\n}\n\nexport function computeStage(args: StageArgs): Stage {\n const pageSize = Math.max(1, args.pageSize ?? DEFAULT_PAGE_SIZE);\n // A SILENT OBSERVER (MD-16) watches; it is never a tile — not on anyone's stage, its own included.\n const ordered = orderParticipants(\n args.participants.filter((participant) => !isObserverRole(participant.role)),\n args.activeSpeakers,\n );\n\n const sharer = ordered.find((participant) => participant.screenSharing) ?? null;\n const pinnedParticipant =\n args.pinned === null\n ? null\n : ordered.find((participant) => participant.identity === args.pinned) ?? null;\n\n // A SCREEN SHARE OUTRANKS A PIN, deliberately: someone presenting is the\n // reason everyone is looking, and a stale pin hiding a live demo is the\n // complaint this rule exists to prevent.\n const spotlit =\n args.spotlight == null\n ? null\n : ordered.find((participant) => participant.identity === args.spotlight) ?? null;\n\n const feature =\n sharer !== null\n ? sharer\n : pinnedParticipant !== null && args.layout !== \"grid\"\n ? pinnedParticipant\n : spotlit !== null\n ? spotlit\n : args.layout === \"grid\"\n ? null\n : ordered[0] ?? null;\n\n const rest =\n feature === null\n ? ordered\n : ordered.filter((participant) => participant.identity !== feature.identity);\n\n const pageCount = Math.max(1, Math.ceil(rest.length / pageSize));\n const page = Math.min(Math.max(0, args.page ?? 0), pageCount - 1);\n const tiles = rest.slice(page * pageSize, page * pageSize + pageSize);\n\n return {\n feature,\n tiles,\n overflow: rest.length - tiles.length,\n page,\n pageCount,\n presenting: sharer !== null,\n };\n}\n\n/** Column count for a grid of N tiles — the shape that keeps tiles squarest. */\nexport function gridColumns(count: number): number {\n if (count <= 1) return 1;\n if (count <= 4) return 2;\n if (count <= 9) return 3;\n if (count <= 16) return 4;\n return 5;\n}\n","/**\n * CG — WHAT THIS BROWSER CAN DO, probed before anything else (CORE-DESIGN §11 `browser-unsupported`,\n * `share-unsupported-mobile`). Pure over an injected scope so the rules are the tests; the web\n * reading is `detectCapabilities()` with no argument. Never touches LiveKit.\n */\n\nexport type UnsupportedReason = \"no_webrtc\";\n\nexport interface MeetCapabilities {\n /** False only when a call cannot run here at all (no WebRTC: an in-app webview, an old browser). */\n readonly canJoin: boolean;\n readonly reason: UnsupportedReason | null;\n /** The in-app browser this looks like (Instagram, Facebook, LinkedIn…), or null. */\n readonly inAppBrowser: string | null;\n /** A phone or tablet: no screen sharing from the browser there. */\n readonly mobile: boolean;\n /** `getDisplayMedia` exists AND this is not a phone/tablet. */\n readonly canShareScreen: boolean;\n}\n\ninterface ScopeLike {\n readonly RTCPeerConnection?: unknown;\n readonly webkitRTCPeerConnection?: unknown;\n readonly navigator?: {\n readonly userAgent?: string;\n readonly maxTouchPoints?: number;\n readonly mediaDevices?: { readonly getDisplayMedia?: unknown };\n };\n}\n\nconst IN_APP: readonly [RegExp, string][] = [\n [/Instagram/i, \"Instagram\"],\n [/FBAN|FBAV|FB_IAB/i, \"Facebook\"],\n [/LinkedInApp/i, \"LinkedIn\"],\n [/MicroMessenger/i, \"WeChat\"],\n [/Snapchat/i, \"Snapchat\"],\n [/\\bLine\\//i, \"LINE\"],\n [/Twitter|TwitterAndroid/i, \"X\"],\n [/TikTok|musical_ly|BytedanceWebview/i, \"TikTok\"],\n];\n\nexport function isMobileUserAgent(userAgent: string, maxTouchPoints = 0): boolean {\n if (/iPhone|iPod|Android|Mobile|iPad/i.test(userAgent)) return true;\n // iPadOS reports a Mac user agent; touch points give it away.\n return /Macintosh/.test(userAgent) && maxTouchPoints > 1;\n}\n\nexport function detectCapabilities(scope: ScopeLike | null = typeof globalThis === \"undefined\" ? null : (globalThis as ScopeLike)): MeetCapabilities {\n // Outside a browser (a server render, a test with no scope) nothing is known to be missing.\n if (scope === null || scope.navigator === undefined) {\n return { canJoin: true, reason: null, inAppBrowser: null, mobile: false, canShareScreen: false };\n }\n const ua = scope.navigator.userAgent ?? \"\";\n const webrtc = typeof scope.RTCPeerConnection === \"function\" || typeof scope.webkitRTCPeerConnection === \"function\";\n const mobile = isMobileUserAgent(ua, scope.navigator.maxTouchPoints ?? 0);\n const inAppBrowser = IN_APP.find(([pattern]) => pattern.test(ua))?.[1] ?? null;\n return {\n canJoin: webrtc,\n reason: webrtc ? null : \"no_webrtc\",\n inAppBrowser,\n mobile,\n canShareScreen: !mobile && typeof scope.navigator.mediaDevices?.getDisplayMedia === \"function\",\n };\n}\n\n/** The words for a browser that cannot join: what to open instead. Short lines (layout, not prose). */\nexport function unsupportedWords(capabilities: MeetCapabilities): { readonly title: string; readonly body: string } {\n if (capabilities.inAppBrowser !== null) {\n return {\n title: `${capabilities.inAppBrowser}'s browser can't join calls`,\n body: \"Open this link in Chrome, Safari, Edge or Firefox\",\n };\n }\n return { title: \"Meet is not compatible with your browser yet\", body: \"Open this link in Chrome, Safari, Edge or Firefox\" };\n}\n","/**\n * THE MEDIA SEAM — the ONE interface the room engine talks to.\n *\n * D1/D2: LiveKit Cloud is the media infrastructure, hardcoded. This interface\n * is NOT a provider abstraction and is not here so a second SFU can be swapped\n * in — it exists for two reasons that are both about correctness:\n *\n * 1. **The engine must be provable without a browser.** Everything expensive in\n * a calling product (ring timeouts, reconnection, admission, escalation,\n * speaker tracking) is logic, and logic proven only through a real SFU is\n * logic that is never proven. `testing/fake-driver.ts` drives every one of\n * those paths in a node test.\n * 2. **The SDK stays out of the initial chunk.** `livekit-client` is reached\n * only through a dynamic `import()` inside the default driver, so importing\n * this package during SSR — or on a page with a call BUTTON but no call —\n * costs nothing and touches no browser API.\n *\n * The default implementation ships (`createLiveKitDriver`) and is what\n * `<MeetProvider>` uses when a host injects nothing (THE ALL-INCLUSIVE LAW).\n */\n\nimport type { NetworkReading } from \"./network-health\";\nimport type { ConnectionQuality } from \"./types\";\n\n/** A participant as the media layer sees them, before roles or profiles. */\nexport interface DriverParticipant {\n readonly identity: string;\n readonly name: string;\n /** Opaque JSON string the token minted for this participant. */\n readonly metadata: string | null;\n /**\n * The participant's LiveKit attributes. Unlike `metadata` these are set by\n * the participant itself at run time, which is how the note-taker announces\n * that a consent notice is owed (D10).\n */\n readonly attributes: Readonly<Record<string, string>>;\n readonly isLocal: boolean;\n readonly micEnabled: boolean;\n readonly cameraEnabled: boolean;\n readonly screenSharing: boolean;\n}\n\nexport type DriverConnectionState =\n | \"connecting\"\n | \"connected\"\n /** Only the signal connection is being re-established; media may still flow. */\n | \"signal_reconnecting\"\n | \"reconnecting\"\n | \"disconnected\";\n\nexport type DriverTrackKind = \"camera\" | \"microphone\" | \"screen\";\n\n/**\n * ONE LIVE CAPTION off the `lk.transcription` text stream.\n *\n * `speakerIdentity` is `lk.transcribed_participant_identity` — WHO IS BEING\n * TRANSCRIBED, not who published the stream (the note-taker publishes every\n * one of them). The SFU-reported publisher identity below is independently\n * checked against the meeting's note-taker; captions remain hints and durable\n * segments remain the record (D16).\n */\nexport interface DriverTranscription {\n /** `lk.segment_id`. Interim and final share it; that is what makes replacement possible. */\n readonly segmentId: string;\n readonly speakerIdentity: string;\n /**\n * The LiveKit participant that opened this text stream. Unlike\n * `speakerIdentity`, this comes from the SFU and is the authority for who\n * published a caption.\n */\n readonly publisherIdentity: string;\n readonly text: string;\n /** `lk.transcription_final`. */\n readonly final: boolean;\n /** `lk.transcribed_track_id`, when the sender supplied it. */\n readonly trackId: string | null;\n readonly at: string;\n}\n\nexport interface RoomDriverHandlers {\n /** The FULL roster, every time. A delta protocol here drifts; a snapshot cannot. */\n participants(participants: readonly DriverParticipant[]): void;\n activeSpeakers(identities: readonly string[]): void;\n quality(identity: string, quality: ConnectionQuality): void;\n connectionState(state: DriverConnectionState): void;\n /** In-room data messages: hand raise, reactions, in-room chat, AI events. */\n data(topic: string, payload: string, fromIdentity: string | null): void;\n /** A live caption. Interim and final both arrive here (D16). */\n transcription(event: DriverTranscription): void;\n /**\n * A remote participant left the room, with LiveKit's reason by NAME (`CLIENT_INITIATED`, …) or\n * null when none was given. Fired BEFORE the roster update that drops them, so an unannounced\n * departure can be held as a `reconnecting` tile (S6b). Optional for older host drivers.\n */\n participantLeft?(identity: string, reason: string | null): void;\n /** The room ended for this client. `reason` is shown to the user verbatim. */\n disconnected(reason: string | null): void;\n /**\n * The ROOM's metadata — set only by the server (`UpdateRoomMetadata`), and\n * delivered to every joiner ON JOIN, which is why the spotlight rides it: a\n * data packet reaches only the people already in the room. Called once after\n * connect and on every change. Optional so an older host driver still works.\n */\n roomMetadata?(metadata: string): void;\n /**\n * S6c — MEDIA. Can remote audio play (`false` = the browser's autoplay policy is holding it\n * until a gesture)? Fired on every change and once right after connect. Optional for older drivers.\n */\n audioPlayback?(canPlay: boolean): void;\n /** S6c — the device list changed (plugged / unplugged), AFTER the SDK re-picked defaults. */\n devicesChanged?(): void;\n /** S6c — the SDK could not acquire or keep a local device. `kind` when the SDK knows it. */\n mediaDeviceError?(error: unknown, kind: \"audioinput\" | \"videoinput\" | null): void;\n /**\n * P1 connection-network — one sample of THIS tab's WebRTC path (round trip, loss, relay/TCP),\n * read from the peer connections' stats every couple of seconds while connected. The rules\n * that turn samples into what people see are `network-health.ts`. Optional for older drivers.\n */\n networkReading?(reading: NetworkReading): void;\n}\n\n/** A camera background effect. `image` replaces the background with `imageUrl`. */\nexport type VideoEffect =\n | { readonly kind: \"none\" }\n | { readonly kind: \"blur\"; readonly radius?: number }\n | { readonly kind: \"image\"; readonly imageUrl: string };\n\n/** Microphone processing the browser applies before anything is sent. */\nexport interface AudioProcessing {\n readonly noiseSuppression: boolean;\n readonly echoCancellation: boolean;\n readonly autoGainControl: boolean;\n}\n\nexport interface MeetRoomDriver {\n connect(serverUrl: string, token: string): Promise<void>;\n disconnect(): Promise<void>;\n setMicrophoneEnabled(enabled: boolean): Promise<void>;\n setCameraEnabled(enabled: boolean): Promise<void>;\n setScreenShareEnabled(enabled: boolean): Promise<void>;\n /**\n * Reliable, topic-addressed data. The in-room control plane. `to` addresses\n * named identities only (a private chat message); absent = everyone.\n */\n publishData(topic: string, payload: string, to?: readonly string[]): Promise<void>;\n /**\n * Apply microphone processing to the live track and to every future one.\n * Optional: a driver without it makes the control ABSENT, never dead.\n */\n setAudioProcessing?(processing: AudioProcessing): Promise<void>;\n /**\n * Apply a background effect to the live camera and to every future one.\n * Rejects with a sentence when this browser cannot run it.\n */\n setVideoEffect?(effect: VideoEffect): Promise<void>;\n /** Whether this browser can run background effects at all. */\n supportsVideoEffects?(): boolean;\n /** Whether this browser can share a screen at all (iOS cannot). Absent = assume yes. */\n supportsScreenShare?(): boolean;\n /** The screen share being published carries sound (`share-audio`). */\n screenShareHasAudio?(): boolean;\n /**\n * Attach a participant's track to a media element. Returns the detach\n * function — returning it (rather than exposing `detach`) is what makes a\n * leaked attachment impossible from the React side: the effect's cleanup IS\n * the return value.\n */\n attach(identity: string, kind: DriverTrackKind, element: HTMLMediaElement): () => void;\n /** The device the driver actually selected, so the UI never lies about it. */\n activeDevices(): Readonly<Record<\"audioinput\" | \"videoinput\" | \"audiooutput\", string | null>>;\n switchDevice(kind: \"audioinput\" | \"videoinput\" | \"audiooutput\", deviceId: string): Promise<void>;\n /**\n * S6c — resume remote audio the autoplay policy blocked. MUST run inside the person's click\n * (the gesture is what the browser checks). Optional: a driver without it never blocks.\n */\n startAudio?(): Promise<void>;\n /**\n * S7 — a \"join here too\" companion plays no remote audio (the person's other device does; two\n * speakers in one room echo). `false` removes every attached remote audio element and keeps new\n * ones from attaching. Optional: a driver without it always plays.\n */\n setRemoteAudioPlayback?(enabled: boolean): void;\n}\n\nexport type RoomDriverFactory = (\n handlers: RoomDriverHandlers,\n) => MeetRoomDriver | Promise<MeetRoomDriver>;\n\n/** Topics for the in-room control plane. One place, so senders cannot drift. */\nexport const DATA_TOPICS = {\n reaction: \"mx.reaction\",\n chat: \"mx.chat\",\n} as const;\n\n/**\n * 🚨 `mx.ai.note` and `mx.ai.transcript` WERE declared here and emitted by\n * nothing. They are gone (MRI-A12): the platform's own events are published by\n * the SERVER under `matrx.meet.*` — see `core/room-events.ts` — because a packet\n * a participant could publish is a packet a guest could forge, and these carry\n * durable rows. A participant topic for the same thing would be a second,\n * unauthenticated way to say it.\n */\n\nexport type DataTopic = (typeof DATA_TOPICS)[keyof typeof DATA_TOPICS];\n\n/**\n * THE LIVEKIT TRANSCRIPTION CONTRACT — not ours, so it is written down once\n * rather than spelled inline. The topic and these four attribute names are the\n * SDK's own (`lk.*`); the worker publishes to exactly them (MRI-A2) and a\n * rename on either side must break here, loudly, in one place.\n */\nexport const TRANSCRIPTION_TOPIC = \"lk.transcription\";\n\nexport const TRANSCRIPTION_ATTRIBUTES = {\n segmentId: \"lk.segment_id\",\n trackId: \"lk.transcribed_track_id\",\n final: \"lk.transcription_final\",\n /**\n * WHO IS BEING TRANSCRIBED. The LiveKit JS client reports the PUBLISHER of a\n * text stream, and the publisher is always the note-taker — so a caption\n * carries the speaker in this attribute and falls back to the publisher only\n * when it is absent (which attributes the line to the agent, visibly, rather\n * than to the wrong person, silently).\n */\n speakerIdentity: \"lk.transcribed_participant_identity\",\n} as const;\n","/**\n * NETWORK HEALTH (CORE-DESIGN §3.3 CS, P1 connection-network) — the pure rules behind what each\n * person sees when a network degrades. No socket, no timer, no browser global: the driver hands\n * in readings, the engine hands in the clock, and every rule here is provable in a node test.\n *\n * Why the core judges quality itself and does not only repeat LiveKit's `ConnectionQuality`:\n * LiveKit scores a TCP/TURN path with 800 ms of round trip as \"good\" (no packets are lost on\n * TCP — they are just late), and it keeps a participant whose cable was pulled \"good\" for 25 s\n * and more. Google Meet documents media quality degrading at RTT ≥ 300 ms; that is the bar here.\n *\n * Four rules:\n * 1. **Self-assessment** (`createSelfAssessor`): this tab's own path, from its WebRTC stats —\n * round trip, loss — with hysteresis so the reading does not flap.\n * 2. **The path** (`pathOf`): media relayed over TURN or carried over TCP is a restricted network\n * (`net-udp-blocked`): the call works, quality may be reduced.\n * 3. **Liveness** (`createLivenessTracker`): every client says \"alive\" on a light data heartbeat\n * that carries its own reading and whether its video is paused. A person who goes silent past\n * `staleAfterMs` is shown `reconnecting` to everyone else (`net-others-see-drop`) — long before\n * the media server gives up on them. A client that never sent a heartbeat (an agent, an older\n * client) is never judged by it.\n * 4. **Video auto-off** (`createVideoAutoOff`): a poor reading that lasts\n * `video_auto_off_after_poor_seconds` pauses this person's camera so their audio survives\n * (`net-video-auto-off`); a person who turns it back on is not paused again until the path has\n * recovered once.\n */\n\nimport type { ConnectionQuality } from \"./types\";\n\n/* ------------------------------------------------------------------ readings */\n\n/** One sample of this tab's WebRTC path, as the driver read it from `getStats()`. */\nexport interface NetworkReading {\n /** Selected candidate pair's current round trip (ms), or null when not known. */\n readonly rttMs: number | null;\n /** Fraction of packets lost since the previous sample (0..1), or null. */\n readonly lossRatio: number | null;\n /** The selected local candidate is a TURN relay. */\n readonly relayed: boolean | null;\n /** The selected local candidate carries media over TCP. */\n readonly tcp: boolean | null;\n}\n\nexport type NetworkPath = \"direct\" | \"restricted\" | \"unknown\";\n\nexport function pathOf(reading: NetworkReading): NetworkPath {\n if (reading.relayed === null && reading.tcp === null) return \"unknown\";\n return reading.relayed === true || reading.tcp === true ? \"restricted\" : \"direct\";\n}\n\n/** Google Meet's documented bar: media quality degrades at RTT ≥ 300 ms. */\nexport const POOR_RTT_MS = 300;\n/** Recovery needs clear daylight under the bar, so the reading does not flap at 299/301. */\nexport const RECOVERED_RTT_MS = 220;\nexport const POOR_LOSS = 0.08;\nexport const RECOVERED_LOSS = 0.03;\n\nexport type SelfQuality = \"good\" | \"poor\" | \"unknown\";\n\nexport interface SelfAssessor {\n /** Fold in one sample; returns the reading after it. */\n push(reading: NetworkReading): SelfQuality;\n current(): SelfQuality;\n reset(): void;\n}\n\n/**\n * Poor after `poorSamples` consecutive bad samples; good again after `goodSamples` consecutive\n * clearly-good ones. A sample with nothing measured changes nothing.\n */\nexport function createSelfAssessor(options: { poorSamples?: number; goodSamples?: number } = {}): SelfAssessor {\n const poorSamples = options.poorSamples ?? 2;\n const goodSamples = options.goodSamples ?? 3;\n let quality: SelfQuality = \"unknown\";\n let badRun = 0;\n let goodRun = 0;\n return {\n push(reading) {\n const { rttMs, lossRatio } = reading;\n if (rttMs === null && lossRatio === null) return quality;\n const bad = (rttMs !== null && rttMs >= POOR_RTT_MS) || (lossRatio !== null && lossRatio >= POOR_LOSS);\n const clear =\n (rttMs === null || rttMs < RECOVERED_RTT_MS) && (lossRatio === null || lossRatio < RECOVERED_LOSS);\n badRun = bad ? badRun + 1 : 0;\n goodRun = clear ? goodRun + 1 : 0;\n if (badRun >= poorSamples) quality = \"poor\";\n else if (quality !== \"poor\" && !bad) quality = \"good\";\n else if (quality === \"poor\" && goodRun >= goodSamples) quality = \"good\";\n return quality;\n },\n current: () => quality,\n reset() {\n quality = \"unknown\";\n badRun = 0;\n goodRun = 0;\n },\n };\n}\n\n/* ------------------------------------------------------------------ combining */\n\nconst RANK: Record<ConnectionQuality, number> = {\n lost: 0,\n poor: 1,\n good: 2,\n excellent: 3,\n unknown: 4,\n};\n\n/** The worse of two readings; `unknown` never wins over a real one. */\nexport function worstQuality(a: ConnectionQuality, b: ConnectionQuality | SelfQuality | null | undefined): ConnectionQuality {\n if (b === null || b === undefined || b === \"unknown\") return a;\n if (a === \"unknown\") return b;\n return RANK[b] < RANK[a] ? b : a;\n}\n\n/* ------------------------------------------------------------------ heartbeat */\n\n/** The participant data topic the heartbeat rides (lossy-tolerant, tiny). */\nexport const ALIVE_TOPIC = \"mx.alive\";\n\nexport interface AliveBeat {\n /** The sender's own path reading. */\n readonly q: SelfQuality;\n /** The sender's camera is paused by the network (auto-off), not by their choice. */\n readonly vp: boolean;\n}\n\nexport function encodeBeat(beat: AliveBeat): string {\n return JSON.stringify({ q: beat.q, vp: beat.vp });\n}\n\n/** A deliberate Leave, said on the heartbeat so nobody holds a `reconnecting` tile for it. */\nexport function encodeBye(): string {\n return JSON.stringify({ bye: true });\n}\n\nexport function isBye(payload: string): boolean {\n try {\n return (JSON.parse(payload) as { bye?: unknown }).bye === true;\n } catch {\n return false;\n }\n}\n\nexport function decodeBeat(payload: string): AliveBeat | null {\n try {\n const raw = JSON.parse(payload) as { q?: unknown; vp?: unknown };\n const q = raw.q === \"good\" || raw.q === \"poor\" ? raw.q : \"unknown\";\n return { q, vp: raw.vp === true };\n } catch {\n return null;\n }\n}\n\n/**\n * How often to beat. 2 s up to 25 people, then slower in proportion, so a 500-person room costs\n * every client about the same packets per second as a 25-person one.\n */\nexport function heartbeatIntervalMs(roomSize: number): number {\n return 2000 * Math.max(1, Math.ceil(Math.max(1, roomSize) / 25));\n}\n\n/** Silent this long = shown reconnecting: three missed beats and a second of slack (7 s small). */\nexport function staleAfterMs(roomSize: number): number {\n return heartbeatIntervalMs(roomSize) * 3 + 1000;\n}\n\nexport interface LivenessTracker {\n /** A beat (or any packet) arrived from `who`. */\n heard(who: string, beat: AliveBeat | null): void;\n /** People who have beaten at least once and are now silent past `staleAfterMs(roomSize)`. */\n stale(present: readonly string[], roomSize: number): Set<string>;\n /** The last reading `who` sent about their own path, or null. */\n reported(who: string): AliveBeat | null;\n /** This tab was itself cut off: everyone gets a fresh start from now (no false ghosts). */\n restartClock(): void;\n forget(who: string): void;\n clear(): void;\n}\n\nexport function createLivenessTracker(now: () => number): LivenessTracker {\n const lastHeard = new Map<string, number>();\n const beats = new Map<string, AliveBeat>();\n return {\n heard(who, beat) {\n lastHeard.set(who, now());\n if (beat !== null) beats.set(who, beat);\n },\n stale(present, roomSize) {\n const limit = staleAfterMs(roomSize);\n const at = now();\n const out = new Set<string>();\n for (const who of present) {\n const heard = lastHeard.get(who);\n if (heard !== undefined && at - heard > limit) out.add(who);\n }\n return out;\n },\n reported: (who) => beats.get(who) ?? null,\n restartClock() {\n const at = now();\n for (const who of lastHeard.keys()) lastHeard.set(who, at);\n },\n forget(who) {\n lastHeard.delete(who);\n beats.delete(who);\n },\n clear() {\n lastHeard.clear();\n beats.clear();\n },\n };\n}\n\n/* ------------------------------------------------------------------ video auto-off */\n\nexport interface VideoAutoOff {\n /**\n * One step with the current reading and whether the camera is on. Returns `\"pause\"` exactly\n * once when the camera should be paused for the network.\n */\n step(quality: SelfQuality, cameraOn: boolean): \"pause\" | null;\n /** The person turned their video back on themselves: no auto-pause until the path recovers. */\n retried(): void;\n reset(): void;\n}\n\n/** `afterMs` ≤ 0 = the knob is off: never pause. */\nexport function createVideoAutoOff(afterMs: () => number, now: () => number): VideoAutoOff {\n let poorSince: number | null = null;\n let armed = true;\n return {\n step(quality, cameraOn) {\n if (quality === \"good\") {\n poorSince = null;\n armed = true;\n return null;\n }\n if (quality !== \"poor\" || !cameraOn) {\n poorSince = quality === \"poor\" ? poorSince : null;\n return null;\n }\n const at = now();\n if (poorSince === null) poorSince = at;\n const limit = afterMs();\n if (!armed || limit <= 0 || at - poorSince < limit) return null;\n armed = false;\n return \"pause\";\n },\n retried() {\n armed = false;\n },\n reset() {\n poorSince = null;\n armed = true;\n },\n };\n}\n\n/* ------------------------------------------------------------------ stats → reading */\n\n/** The few `RTCStats` fields the reading needs (structural: no DOM types in the core). */\nexport interface StatsEntry {\n readonly id: string;\n readonly type: string;\n readonly [field: string]: unknown;\n}\n\n/**\n * One reading from a peer connection's `getStats()` entries: the SELECTED candidate pair (the\n * transport's `selectedCandidatePairId`, else the nominated succeeded pair), its round trip, its\n * local candidate's type and protocol, and the worst `fractionLost` the far end reported.\n */\nexport function readingFromStats(entries: Iterable<StatsEntry>): NetworkReading {\n const byId = new Map<string, StatsEntry>();\n for (const entry of entries) byId.set(entry.id, entry);\n let pair: StatsEntry | undefined;\n for (const entry of byId.values()) {\n if (entry.type === \"transport\" && typeof entry[\"selectedCandidatePairId\"] === \"string\") {\n pair = byId.get(entry[\"selectedCandidatePairId\"] as string);\n if (pair !== undefined) break;\n }\n }\n if (pair === undefined) {\n for (const entry of byId.values()) {\n if (entry.type === \"candidate-pair\" && entry[\"nominated\"] === true && entry[\"state\"] === \"succeeded\") {\n pair = entry;\n break;\n }\n }\n }\n let lossRatio: number | null = null;\n for (const entry of byId.values()) {\n if (entry.type === \"remote-inbound-rtp\" && typeof entry[\"fractionLost\"] === \"number\") {\n lossRatio = Math.max(lossRatio ?? 0, entry[\"fractionLost\"] as number);\n }\n }\n if (pair === undefined) return { rttMs: null, lossRatio, relayed: null, tcp: null };\n const rtt = pair[\"currentRoundTripTime\"];\n const local = typeof pair[\"localCandidateId\"] === \"string\" ? byId.get(pair[\"localCandidateId\"] as string) : undefined;\n const candidateType = local?.[\"candidateType\"];\n const protocol = local?.[\"protocol\"];\n const relayProtocol = local?.[\"relayProtocol\"];\n return {\n rttMs: typeof rtt === \"number\" ? Math.round(rtt * 1000) : null,\n lossRatio,\n relayed: typeof candidateType === \"string\" ? candidateType === \"relay\" : null,\n tcp:\n typeof protocol === \"string\" || typeof relayProtocol === \"string\"\n ? protocol === \"tcp\" || relayProtocol === \"tcp\" || relayProtocol === \"tls\"\n : null,\n };\n}\n\n/** Two transports (publisher, subscriber) → one reading: the worse round trip and loss, either path restricted. */\nexport function mergeReadings(readings: readonly NetworkReading[]): NetworkReading {\n const max = (values: (number | null)[]): number | null => {\n const known = values.filter((v): v is number => v !== null);\n return known.length === 0 ? null : Math.max(...known);\n };\n const any = (values: (boolean | null)[]): boolean | null =>\n values.every((v) => v === null) ? null : values.some((v) => v === true);\n return {\n rttMs: max(readings.map((r) => r.rttMs)),\n lossRatio: max(readings.map((r) => r.lossRatio)),\n relayed: any(readings.map((r) => r.relayed)),\n tcp: any(readings.map((r) => r.tcp)),\n };\n}\n","/**\n * THE ONE PLACE `livekit-client` IS TOUCHED.\n *\n * Everything above this file talks to `MeetRoomDriver`. That containment is\n * what lets the engine's whole behavior — ring timeouts, admission, escalation,\n * reconnection — be proven in a node test, and it is why an SDK upgrade is a\n * single-file review rather than a package-wide audit.\n *\n * 🚨 **THE IMPORT IS DYNAMIC, DELIBERATELY.** `import type` is erased, and the\n * runtime `await import(\"livekit-client\")` happens inside `connect()`. So:\n * importing `@ai-matrx/meet` during SSR, or on a page that renders a call\n * BUTTON but never a call, pulls in none of the media SDK and touches no\n * browser API (independence contract §4). A static import here would put ~400KB\n * of WebRTC into every consumer's first load and break RSC rendering.\n *\n * The mapping helpers below are exported for their own tests: an SDK enum\n * renamed upstream must fail a named test, not silently become \"unknown\" on\n * every tile.\n */\n\nimport type {\n ConnectionQuality as LiveKitQuality,\n Participant,\n Room,\n RoomOptions,\n} from \"livekit-client\";\nimport { detectCapabilities } from \"./capabilities\";\nimport { MeetError, normalizeDeviceError, normalizeScreenShareError } from \"./errors\";\nimport {\n TRANSCRIPTION_ATTRIBUTES,\n TRANSCRIPTION_TOPIC,\n type AudioProcessing,\n type DriverConnectionState,\n type DriverParticipant,\n type DriverTrackKind,\n type MeetRoomDriver,\n type RoomDriverHandlers,\n type VideoEffect,\n} from \"./room-driver\";\nimport type { ConnectionQuality } from \"./types\";\nimport { createReconnectPolicy } from \"./connection-supervisor\";\nimport { mergeReadings, readingFromStats, type NetworkReading, type StatsEntry } from \"./network-health\";\n\n/**\n * LiveKit's quality enum → ours. Mapped explicitly rather than cast: the SDK\n * has five states and our UI has five sentences, and an unmapped one must\n * degrade to `unknown` (an honest \"we don't know\") rather than to `excellent`\n * (a lie that hides a failing call).\n */\nexport function mapQuality(quality: LiveKitQuality | string): ConnectionQuality {\n switch (quality) {\n case \"excellent\":\n return \"excellent\";\n case \"good\":\n return \"good\";\n case \"poor\":\n return \"poor\";\n case \"lost\":\n return \"lost\";\n default:\n return \"unknown\";\n }\n}\n\nexport function mapConnectionState(state: string): DriverConnectionState {\n switch (state) {\n case \"connected\":\n return \"connected\";\n case \"connecting\":\n return \"connecting\";\n case \"reconnecting\":\n return \"reconnecting\";\n // Only the signal socket is down (S6b): its own region value, so the supervisor can tell a\n // signal blip from a full media reconnect.\n case \"signalReconnecting\":\n return \"signal_reconnecting\";\n default:\n return \"disconnected\";\n }\n}\n\n/**\n * A LiveKit participant → our snapshot.\n *\n * 🚨 `name` IS REPORTED EXACTLY AS LIVEKIT GAVE IT, empty string and all\n * (MRI-A18). Through 0.4.12 it fell back to the identity here, which meant a\n * driver — the thing furthest from any screen — invented a \"name\" that\n * `core/names.ts` then had to recognize and refuse. A driver reports; it does\n * not name. The one fallback lives in `projectParticipant`, and it is the name\n * book's honest placeholder rather than a token.\n */\nexport function snapshotParticipant(\n participant: Pick<\n Participant,\n \"identity\" | \"name\" | \"metadata\" | \"isMicrophoneEnabled\" | \"isCameraEnabled\" | \"isScreenShareEnabled\"\n > & { attributes?: Readonly<Record<string, string>> | undefined },\n isLocal: boolean,\n): DriverParticipant {\n const name = participant.name;\n return {\n identity: participant.identity,\n name: name ?? \"\",\n metadata: participant.metadata ?? null,\n // ATTRIBUTES ARE PART OF THE SNAPSHOT, not read ad hoc later: the note-taker\n // announces the consent notice here (D10) and the roster is the only place\n // this package looks at a participant.\n attributes: participant.attributes ?? {},\n isLocal,\n micEnabled: participant.isMicrophoneEnabled,\n cameraEnabled: participant.isCameraEnabled,\n screenSharing: participant.isScreenShareEnabled,\n };\n}\n\nexport interface LiveKitDriverOptions {\n /** Adaptive stream + dynacast default ON — R5 scale honesty, not a knob. */\n roomOptions?: RoomOptions | undefined;\n}\n\n/** The browser defaults every call product ships with. */\nexport const DEFAULT_AUDIO_PROCESSING: AudioProcessing = {\n noiseSuppression: true,\n echoCancellation: true,\n autoGainControl: true,\n};\n\n/**\n * Background effects run a segmentation model on the GPU (MediaPipe via\n * `@livekit/track-processors`). A browser without `OffscreenCanvas` or WebGL2\n * cannot — the control is then ABSENT, never a toggle that silently fails.\n */\nexport function videoEffectsSupported(): boolean {\n const scope = globalThis as unknown as {\n OffscreenCanvas?: unknown;\n document?: { createElement(tag: string): { getContext(kind: string): unknown } };\n };\n if (typeof scope.OffscreenCanvas === \"undefined\" || scope.document === undefined) return false;\n try {\n return scope.document.createElement(\"canvas\").getContext(\"webgl2\") !== null;\n } catch {\n return false;\n }\n}\n\n/**\n * Put the chosen effect on the local camera. The processor package is imported\n * lazily, exactly like `livekit-client` itself, so a meeting nobody blurs never\n * downloads a segmentation model.\n */\nasync function applyVideoEffect(target: Room, effect: VideoEffect): Promise<void> {\n const { Track } = await import(\"livekit-client\");\n const track = target.localParticipant.getTrackPublication(Track.Source.Camera)?.track as\n | {\n setProcessor(processor: unknown): Promise<void>;\n stopProcessor(): Promise<void>;\n }\n | undefined;\n if (track === undefined) return; // camera off: applied when it turns on.\n try {\n if (effect.kind === \"none\") {\n await track.stopProcessor();\n return;\n }\n const processors = await import(\"@livekit/track-processors\");\n const processor =\n effect.kind === \"blur\"\n ? processors.BackgroundBlur(effect.radius ?? 12)\n : processors.VirtualBackground(effect.imageUrl);\n await track.setProcessor(processor);\n } catch (error) {\n throw new MeetError(\n \"media-unsupported\",\n \"The background effect could not start on this camera\",\n \"Background effects need a browser with WebGL2 (current Chrome, Edge, Firefox or Safari). \" +\n \"Your video continues without the effect.\",\n error,\n );\n }\n}\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\nexport function createLiveKitDriver(\n handlers: RoomDriverHandlers,\n options: LiveKitDriverOptions = {},\n): MeetRoomDriver {\n let room: Room | null = null;\n /**\n * The roster projection for the CURRENT room, kept out here so `connect()`\n * can fire it once the transport is up. See the call site for why.\n */\n let projectRoster: (() => void) | null = null;\n /** What the person chose, kept so a re-enabled track gets it too. */\n let audioCapture: AudioProcessing = DEFAULT_AUDIO_PROCESSING;\n let videoEffect: VideoEffect = { kind: \"none\" };\n\n /**\n * P1 connection-network: sample this tab's path every 2 s while connected. `engine.pcManager`\n * is the SDK's own handle on the two peer connections; a reading is skipped (never invented)\n * while a transport is missing or reconnecting.\n */\n let statsTimer: ReturnType<typeof setInterval> | null = null;\n function stopStatsPoll(): void {\n if (statsTimer !== null) clearInterval(statsTimer);\n statsTimer = null;\n }\n function startStatsPoll(target: Room): void {\n stopStatsPoll();\n if (handlers.networkReading === undefined) return;\n const sample = async (): Promise<void> => {\n if (room !== target) return;\n const manager = (target.engine as unknown as {\n pcManager?: { publisher?: { getStats(): Promise<RTCStatsReport> | undefined }; subscriber?: { getStats(): Promise<RTCStatsReport> | undefined } };\n }).pcManager;\n if (manager === undefined) return;\n const readings: NetworkReading[] = [];\n for (const transport of [manager.publisher, manager.subscriber]) {\n try {\n const report = await transport?.getStats();\n if (report !== undefined) readings.push(readingFromStats(report.values() as Iterable<StatsEntry>));\n } catch {\n // A transport mid-restart has no stats; the next sample reads it.\n }\n }\n if (readings.length > 0 && room === target) handlers.networkReading?.(mergeReadings(readings));\n };\n statsTimer = setInterval(() => void sample(), 2000);\n }\n\n async function load(): Promise<Room> {\n if (room !== null) return room;\n let module: typeof import(\"livekit-client\");\n try {\n module = await import(\"livekit-client\");\n } catch (error) {\n throw new MeetError(\n \"misconfigured\",\n \"livekit-client could not be loaded\",\n \"`livekit-client` is a dependency of @ai-matrx/meet and is imported lazily at \" +\n \"connect time. If this failed, the bundler excluded it — remove it from any \" +\n \"`external`/`serverExternalPackages` list, or pass your own driver factory.\",\n error,\n );\n }\n const { Room: LiveKitRoom, RoomEvent, Track } = module;\n const created = new LiveKitRoom({\n // R5: large meetings degrade gracefully instead of melting a laptop.\n adaptiveStream: true,\n dynacast: true,\n // S6b: OUR retry schedule — jittered from attempt 0 (the SDK's default has no jitter on its\n // first two attempts), give-up limits in one place (`connection-supervisor.ts`).\n reconnectPolicy: createReconnectPolicy(),\n ...options.roomOptions,\n });\n /** LiveKit's numeric `DisconnectReason` → its NAME; a number means nothing outside this file. */\n const reasonName = (reason: unknown): string | null =>\n reason === undefined || reason === null\n ? null\n : ((module.DisconnectReason as unknown as Record<number, string> | undefined)?.[reason as number] ?? String(reason));\n\n const roster = (): void => {\n const local = snapshotParticipant(created.localParticipant, true);\n const remotes = [...created.remoteParticipants.values()].map((participant) =>\n snapshotParticipant(participant, false),\n );\n // The FULL roster every time. Every one of these six events changes what\n // a tile must render, and a delta protocol across six sources drifts.\n handlers.participants([local, ...remotes]);\n };\n\n projectRoster = roster;\n\n // 🚨 A REMOTE MICROPHONE IS PLAYED HERE, OR NOWHERE. `livekit-client` never\n // plays a remote audio track by itself (`webAudioMix` is off by default): a\n // subscribed track makes sound only once `track.attach()` gives it a media\n // element. Until 0.7.10 nothing attached audio, so every meeting received\n // everyone's audio packets and played none of them (production\n // re-verification, 2026-09-29). Audio is NOT tied to tiles on purpose: the\n // stage paginates and the Board strip overflows, and a person on page two\n // must still be heard.\n created\n .on(RoomEvent.TrackSubscribed, (track) => {\n if (track.kind !== Track.Kind.Audio || typeof document === \"undefined\") return;\n if (!remoteAudioPlayback) return;\n const element = track.attach();\n element.setAttribute(\"data-mx-meet-audio\", \"\");\n audioSink().appendChild(element);\n })\n .on(RoomEvent.TrackUnsubscribed, (track) => {\n if (track.kind !== Track.Kind.Audio) return;\n for (const element of track.detach()) element.remove();\n });\n\n created\n .on(RoomEvent.ParticipantConnected, roster)\n // WHY they left goes first (S6b): an unannounced drop is held as a `reconnecting` tile,\n // and the roster that follows must already know it.\n .on(RoomEvent.ParticipantDisconnected, (participant, reason) => {\n handlers.participantLeft?.(participant.identity, reasonName(reason));\n roster();\n })\n .on(RoomEvent.TrackSubscribed, roster)\n .on(RoomEvent.TrackUnsubscribed, roster)\n .on(RoomEvent.TrackMuted, roster)\n .on(RoomEvent.TrackUnmuted, roster)\n .on(RoomEvent.LocalTrackPublished, (publication) => {\n // `share-stopped-by-browser`: the browser's own \"Stop sharing\" ends the share's track;\n // unpublish it (screen and its sound) so everyone's stage and this tab's controls follow.\n if (publication.source === Track.Source.ScreenShare) {\n const ended = (): void => {\n void created.localParticipant.setScreenShareEnabled(false).catch(() => undefined);\n };\n publication.track?.mediaStreamTrack.addEventListener(\"ended\", ended, { once: true });\n }\n roster();\n })\n .on(RoomEvent.LocalTrackUnpublished, roster)\n // A SERVER DECISION CHANGED MID-MEETING (Meet wave 2): a co-host made or\n // unmade, the host handed over, a rename. The server re-stamps the\n // roster through `UpdateParticipant`; these three events carry it here.\n .on(RoomEvent.ParticipantNameChanged, roster)\n .on(RoomEvent.ParticipantAttributesChanged, roster)\n .on(RoomEvent.ParticipantMetadataChanged, roster)\n .on(RoomEvent.RoomMetadataChanged, (metadata) => {\n handlers.roomMetadata?.(metadata ?? \"\");\n })\n // S6c MEDIA (the media supervisor's inputs). Autoplay: LiveKit flips this when an\n // attached remote audio element's play() is refused, and back when startAudio() (inside\n // a click) or a later play succeeds.\n .on(RoomEvent.AudioPlaybackStatusChanged, (canPlay: boolean) => {\n handlers.audioPlayback?.(canPlay);\n })\n .on(RoomEvent.MediaDevicesChanged, () => {\n handlers.devicesChanged?.();\n })\n .on(RoomEvent.MediaDevicesError, (error: Error, kind?: MediaDeviceKind) => {\n handlers.mediaDeviceError?.(error, kind === \"audioinput\" || kind === \"videoinput\" ? kind : null);\n })\n .on(RoomEvent.ActiveSpeakersChanged, (speakers) => {\n handlers.activeSpeakers(speakers.map((speaker) => speaker.identity));\n })\n .on(RoomEvent.ConnectionQualityChanged, (quality, participant) => {\n handlers.quality(participant.identity, mapQuality(quality));\n })\n .on(RoomEvent.ConnectionStateChanged, (state) => {\n handlers.connectionState(mapConnectionState(state));\n })\n .on(RoomEvent.DataReceived, (payload, participant, _kind, topic) => {\n handlers.data(\n topic ?? \"\",\n decoder.decode(payload),\n participant?.identity ?? null,\n );\n })\n .on(RoomEvent.Disconnected, (reason) => {\n stopStatsPoll();\n audioContainer?.remove();\n audioContainer = null;\n // §3.4 ER reads LiveKit's reason by NAME (`DUPLICATE_IDENTITY`, `PARTICIPANT_REMOVED`, …):\n // the enum is numeric on the wire, and a number means nothing outside this file.\n handlers.disconnected(reasonName(reason));\n });\n\n // LIVE CAPTIONS (D16). The agent publishes one text stream per utterance on\n // `lk.transcription`, re-sending the same `lk.segment_id` as the words firm\n // up, and the FINAL flag says which one is the last. The chunks are read as\n // they arrive — waiting for `readAll()` would make captions appear only\n // once the sentence was over, which is not a caption.\n //\n // 🚨 REGISTERED BEFORE `connect()`, deliberately: LiveKit buffers data\n // packets that arrive before the room is connected and replays them, but a\n // handler registered after the first stream header lands misses that\n // utterance outright — the first thing anyone says in the meeting.\n try {\n created.registerTextStreamHandler(TRANSCRIPTION_TOPIC, (reader, participantInfo) => {\n const attributes: Record<string, string> = { ...(reader.info.attributes ?? {}) };\n const segmentId =\n attributes[TRANSCRIPTION_ATTRIBUTES.segmentId] ?? reader.info.id;\n const trackId = attributes[TRANSCRIPTION_ATTRIBUTES.trackId] ?? null;\n const final = attributes[TRANSCRIPTION_ATTRIBUTES.final] === \"true\";\n // `lk.transcribed_participant_identity` is WHO IS BEING TRANSCRIBED.\n // The SFU's publisher identity stays the note-taker, so using it here\n // would attribute the whole meeting to the agent. Do not use the\n // sender-identity override: LiveKit accepts it from an agent publisher,\n // but aidream deliberately does not set it.\n const speakerIdentity =\n attributes[TRANSCRIPTION_ATTRIBUTES.speakerIdentity] ?? participantInfo.identity;\n const at = new Date(reader.info.timestamp || Date.now()).toISOString();\n void (async () => {\n let text = \"\";\n try {\n for await (const chunk of reader) {\n text += chunk;\n // Emit as it grows: a caption that only appears when the sentence\n // ends is a transcript, not a caption.\n handlers.transcription({\n segmentId,\n speakerIdentity,\n publisherIdentity: participantInfo.identity,\n text,\n final,\n trackId,\n at,\n });\n }\n } catch (error) {\n // A caption stream that dies is a caption we do not have. It is\n // never fatal to the meeting and never silent.\n handlers.data(\n TRANSCRIPTION_TOPIC,\n JSON.stringify({ error: String((error as Error)?.message ?? error) }),\n participantInfo.identity,\n );\n }\n })();\n });\n } catch {\n // Registering twice throws in the SDK. `load()` is single-flight through\n // `room`, so this can only happen on a re-entrant remount — in which case\n // the existing handler is already delivering captions.\n }\n\n // Kept for `attach`, which needs the SDK's Source enum at call time.\n sources = {\n camera: Track.Source.Camera,\n microphone: Track.Source.Microphone,\n screen: Track.Source.ScreenShare,\n };\n trackEvents = {\n subscribed: RoomEvent.TrackSubscribed,\n localPublished: RoomEvent.LocalTrackPublished,\n };\n room = created;\n return created;\n }\n\n let sources: Record<DriverTrackKind, string> | null = null;\n /** The SDK's event names `attach` listens on, captured with `sources`. */\n let trackEvents: { subscribed: string; localPublished: string } | null = null;\n\n /** The hidden container every remote audio element of THIS room lives in. */\n let audioContainer: HTMLElement | null = null;\n /** S7: false for a companion connection — no remote audio element is ever attached. */\n let remoteAudioPlayback = true;\n function audioSink(): HTMLElement {\n if (audioContainer !== null && audioContainer.isConnected) return audioContainer;\n const container = document.createElement(\"div\");\n container.setAttribute(\"data-mx-meet-audio-sink\", \"\");\n container.setAttribute(\"aria-hidden\", \"true\");\n container.style.display = \"none\";\n document.body.appendChild(container);\n audioContainer = container;\n return container;\n }\n\n function requireRoom(operation: string): Room {\n if (room === null) {\n throw new MeetError(\n \"transport\",\n `${operation}: the room is not connected`,\n \"Call connect() first. The engine never reaches this — it gates every media call \" +\n \"on its own phase — so seeing it means a consumer drove the driver directly.\",\n );\n }\n return room;\n }\n\n return {\n async connect(serverUrl, token) {\n const target = await load();\n try {\n await target.connect(serverUrl, token);\n } catch (error) {\n // A connect failure is either media permission (user-actionable) or\n // transport. Both must reach the UI with a sentence, never a spinner.\n throw normalizeDeviceError(error, \"meet.connect\");\n }\n // 🚨 PROJECT THE ROSTER ONCE, HERE. Every other roster source is an\n // EVENT, and a participant who publishes nothing into an empty room\n // generates none of them: no `LocalTrackPublished`, no\n // `ParticipantConnected`, no track events. Until 0.3.2 the join always\n // published a microphone, so `LocalTrackPublished` fired and hid this —\n // and the moment listen-only attendance became possible, a listen-only\n // host was absent from their own People panel, had no tile, and got NO\n // host controls at all (the note-taker could not be started), because\n // host powers are read off the local participant's roster entry.\n projectRoster?.();\n // The room's metadata arrives WITH the join (the spotlight lives there),\n // and `RoomMetadataChanged` fires only for later changes.\n handlers.roomMetadata?.(target.metadata ?? \"\");\n // S6c: the autoplay state as of the join — `AudioPlaybackStatusChanged` fires only on change.\n handlers.audioPlayback?.(target.canPlaybackAudio);\n startStatsPoll(target);\n },\n async disconnect() {\n if (room === null) return;\n const target = room;\n room = null;\n try {\n target.unregisterTextStreamHandler(TRANSCRIPTION_TOPIC);\n } catch {\n // Already gone. Teardown is idempotent by contract.\n }\n projectRoster = null;\n stopStatsPoll();\n audioContainer?.remove();\n audioContainer = null;\n await target.disconnect();\n },\n async setMicrophoneEnabled(enabled) {\n try {\n await requireRoom(\"setMicrophoneEnabled\").localParticipant.setMicrophoneEnabled(\n enabled,\n audioCapture,\n );\n } catch (error) {\n throw normalizeDeviceError(error, \"meet.setMicrophoneEnabled\");\n }\n },\n async setCameraEnabled(enabled) {\n try {\n const target = requireRoom(\"setCameraEnabled\");\n await target.localParticipant.setCameraEnabled(enabled);\n // A REMEMBERED EFFECT FOLLOWS THE CAMERA. Turning the camera back on must\n // not quietly show the room behind someone who chose to blur it.\n if (enabled && videoEffect.kind !== \"none\") await applyVideoEffect(target, videoEffect);\n } catch (error) {\n throw normalizeDeviceError(error, \"meet.setCameraEnabled\");\n }\n },\n async setScreenShareEnabled(enabled) {\n try {\n // Ask for the tab's sound too (`share-audio`): Chrome then offers \"Also share tab audio\".\n await requireRoom(\"setScreenShareEnabled\").localParticipant.setScreenShareEnabled(\n enabled,\n enabled ? { audio: true, systemAudio: \"include\" } : undefined,\n );\n } catch (error) {\n throw normalizeScreenShareError(error, \"meet.setScreenShareEnabled\");\n }\n },\n async publishData(topic, payload, to) {\n await requireRoom(\"publishData\").localParticipant.publishData(\n // `reliable` is not optional for a control plane: a dropped\n // \"hand lowered\" leaves a hand up on every other screen forever.\n encoder.encode(payload) as Uint8Array<ArrayBuffer>,\n to === undefined || to.length === 0\n ? { reliable: true, topic }\n : { reliable: true, topic, destinationIdentities: [...to] },\n );\n },\n async setAudioProcessing(processing) {\n audioCapture = { ...audioCapture, ...processing };\n const target = room;\n if (target === null) return;\n const { Track } = await import(\"livekit-client\");\n const publication = target.localParticipant.getTrackPublication(Track.Source.Microphone);\n const track = publication?.track;\n if (track === undefined || publication?.isMuted === true) return;\n try {\n // The browser's own processing, applied to the LIVE track: a restart\n // with new constraints, the same device, no renegotiation.\n await (track as unknown as { restartTrack(options: object): Promise<void> }).restartTrack({\n ...audioCapture,\n ...(target.getActiveDevice(\"audioinput\") !== undefined\n ? { deviceId: target.getActiveDevice(\"audioinput\") }\n : {}),\n });\n } catch (error) {\n throw normalizeDeviceError(error, \"meet.setAudioProcessing\");\n }\n },\n async setVideoEffect(effect) {\n videoEffect = effect;\n const target = room;\n if (target === null) return;\n await applyVideoEffect(target, effect);\n },\n supportsVideoEffects() {\n return videoEffectsSupported();\n },\n screenShareHasAudio() {\n const target = room;\n if (target === null) return false;\n // `Track.Source.ScreenShareAudio`, by value: the SDK enum is not in scope outside connect().\n return target.localParticipant.getTrackPublications().some((p) => String(p.source) === \"screen_share_audio\" && p.track !== undefined);\n },\n supportsScreenShare() {\n // iOS Safari and most phones have no `getDisplayMedia` at all.\n // A phone browser that exposes it still cannot present (`share-unsupported-mobile`).\n return detectCapabilities().canShareScreen;\n },\n attach(identity, kind, element) {\n const target = requireRoom(\"attach\");\n const source = sources?.[kind];\n if (source === undefined) return () => undefined;\n // 🚨 THE TRACK MAY NOT EXIST YET, AND MAY BE REPLACED LATER. A tile\n // renders as soon as the roster says the camera is on — which is known\n // from the PUBLICATION, before the track is SUBSCRIBED — and the tile's\n // element stays mounted, so its ref never runs again. Until 0.7.10 an\n // attach that found no track returned a no-op for good: a person\n // admitted from the waiting room was a black tile for everyone already\n // inside (production re-verification, 2026-09-29). So the attach keeps\n // listening for this participant's track and (re)attaches whenever it\n // arrives or is republished; the returned cleanup stops listening too.\n let current: { detach(element: HTMLMediaElement): unknown } | null = null;\n const sync = (): void => {\n const participant =\n target.localParticipant.identity === identity\n ? target.localParticipant\n : target.remoteParticipants.get(identity);\n const track = participant?.getTrackPublication(source as never)?.track;\n if (track === undefined || track === current) return;\n current?.detach(element);\n track.attach(element);\n current = track;\n };\n const onTrack = (...args: unknown[]): void => {\n // TrackSubscribed(track, publication, participant) /\n // LocalTrackPublished(publication, participant) — the participant is\n // always the last argument.\n const who = args[args.length - 1] as { identity?: string } | undefined;\n if (who?.identity === identity) sync();\n };\n sync();\n const events = trackEvents;\n if (events !== null) {\n target.on(events.subscribed as never, onTrack as never);\n target.on(events.localPublished as never, onTrack as never);\n }\n // The DETACH IS THE RETURN VALUE — a React effect's cleanup cannot forget\n // to call something it was handed.\n return () => {\n if (events !== null) {\n target.off(events.subscribed as never, onTrack as never);\n target.off(events.localPublished as never, onTrack as never);\n }\n current?.detach(element);\n current = null;\n };\n },\n activeDevices() {\n const target = room;\n if (target === null) {\n return { audioinput: null, videoinput: null, audiooutput: null };\n }\n return {\n audioinput: target.getActiveDevice(\"audioinput\") ?? null,\n videoinput: target.getActiveDevice(\"videoinput\") ?? null,\n audiooutput: target.getActiveDevice(\"audiooutput\") ?? null,\n };\n },\n async switchDevice(kind, deviceId) {\n try {\n await requireRoom(\"switchDevice\").switchActiveDevice(kind, deviceId);\n } catch (error) {\n throw normalizeDeviceError(error, \"meet.switchDevice\");\n }\n },\n setRemoteAudioPlayback(enabled) {\n remoteAudioPlayback = enabled;\n if (enabled || audioContainer === null) return;\n for (const element of Array.from(audioContainer.querySelectorAll(\"[data-mx-meet-audio]\"))) {\n if (element instanceof HTMLMediaElement) element.srcObject = null;\n element.remove();\n }\n },\n async startAudio() {\n // S6c: inside the person's click. LiveKit plays every attached remote audio element and\n // resumes its AudioContext; success flips `AudioPlaybackStatusChanged` back to true.\n const target = room;\n if (target === null) return;\n await target.startAudio();\n },\n };\n}\n\n/**\n * THE PRE-JOIN CAMERA PREVIEW (Meet wave 2) — the same SDK track and the same\n * background processor the room will use, so what someone sees before joining\n * is exactly what the meeting will see. Lives here because this file is the one\n * place `livekit-client` is touched.\n */\nexport interface CameraPreview {\n attach(element: HTMLVideoElement): void;\n setEffect(effect: VideoEffect): Promise<void>;\n stop(): void;\n}\n\nexport async function createCameraPreview(\n deviceId: string | null,\n effect: VideoEffect,\n): Promise<CameraPreview> {\n let module: typeof import(\"livekit-client\");\n try {\n module = await import(\"livekit-client\");\n } catch (error) {\n throw new MeetError(\n \"misconfigured\",\n \"livekit-client could not be loaded for the camera preview\",\n \"The bundler excluded `livekit-client`; remove it from any `external` list.\",\n error,\n );\n }\n let track: Awaited<ReturnType<typeof module.createLocalVideoTrack>>;\n try {\n track = await module.createLocalVideoTrack(deviceId === null ? {} : { deviceId });\n } catch (error) {\n throw normalizeDeviceError(error, \"meet.cameraPreview\");\n }\n const target = track as unknown as {\n setProcessor(processor: unknown): Promise<void>;\n stopProcessor(): Promise<void>;\n };\n async function apply(next: VideoEffect): Promise<void> {\n if (next.kind === \"none\") {\n await target.stopProcessor();\n return;\n }\n const processors = await import(\"@livekit/track-processors\");\n await target.setProcessor(\n next.kind === \"blur\"\n ? processors.BackgroundBlur(next.radius ?? 12)\n : processors.VirtualBackground(next.imageUrl),\n );\n }\n if (effect.kind !== \"none\" && videoEffectsSupported()) {\n try {\n await apply(effect);\n } catch {\n // The preview still shows the camera; the settings say the effect failed.\n }\n }\n return {\n attach(element) {\n track.attach(element);\n },\n setEffect: apply,\n stop() {\n track.stop();\n track.detach();\n },\n };\n}\n","/**\n * MEETINGS — the lobby, the host powers, and the durable link.\n *\n * A meeting is a room plus scheduling, lobby and host metadata (D3). Everything\n * here is about the moments OUTSIDE the media session, which is exactly why it\n * cannot ride LiveKit's data plane: a guest waiting in the lobby is not in the\n * LiveKit room yet, by construction. The lobby therefore lives on the meet-room\n * realtime channel.\n *\n * 🚨 THE THREE THINGS THAT MAKE GUEST JOIN SAFE (D6)\n *\n * 1. **The link is an identity, not a session.** A durable slug resolves before,\n * during, and after the scheduled time, and a recurring meeting keeps ONE\n * link. Nothing here mints a per-occurrence URL.\n * 2. **A guest's capability is the TOKEN, and nothing else.** They are scoped to\n * exactly one room. There is no guest row this client writes that could grant\n * anything, and no client-side \"is this person allowed\" branch.\n * 3. **Admission is a SERVER decision, recorded.** `admit` is an RPC; the\n * broadcast that tells the knocker is a NOTIFICATION of that decision, never\n * the decision itself. Two hosts clicking admit is the ordinary case, and the\n * RPC is what makes it idempotent.\n *\n * Host powers are checked against the role the SERVER minted into the token\n * (see `room.ts`), and the UI hides what an actor cannot do rather than\n * rendering a dead button (no dead ends).\n */\n\nimport { isWaitingPhase, type ExitServerWord } from \"./phase\";\nimport type { ChannelHandle, RealtimeManager } from \"@ai-matrx/realtime\";\nimport { MEET_EVENTS, meetingTopic, roomTopic } from \"./channels\";\nimport type { MeetWaiting } from \"./store\";\nimport type { MeetApi } from \"./api\";\nimport { MeetError } from \"./errors\";\nimport { readServerRefusal } from \"./ai\";\nimport type { MeetRepository } from \"./repository\";\nimport { effectiveRole, type MeetStore } from \"./store\";\nimport { MEET_ROUTES, MEETING_PASS_HEADER } from \"./tokens\";\nimport { livePolicyFrom, mayAdmitNow, samePolicy } from \"./live-policy\";\nimport {\n asParticipantIdentity,\n isHostRole,\n isGuest,\n WAITING_ADMISSION_STATES,\n type AdmissionState,\n ROLE_RANK,\n type LobbyEntry,\n type MeetIdentity,\n type MeetParticipant,\n type MeetingAttendee,\n type MeetingId,\n type MeetingRecord,\n type ParticipantIdentity,\n type ParticipantRole,\n type RecordingState,\n type RoomName,\n type UserId,\n} from \"./types\";\n\nexport interface MeetingSessionOptions {\n manager: RealtimeManager;\n repository: MeetRepository;\n store: MeetStore;\n identity: MeetIdentity;\n /**\n * The ONE org-aware aidream client (MRI-A16) — `endForEveryone` is a SERVER\n * decision (MRI-A6), and every request it makes carries the organization.\n */\n api: MeetApi;\n onDiagnostic?: ((event: { level: \"info\" | \"warn\" | \"error\"; message: string; remedy?: string }) => void) | undefined;\n /**\n * 🚨 THE HOST ADMITTED *US*. Called exactly once per admission verdict that\n * names this participant, and only for `admitted`.\n *\n * Admission is not a UI state change: a lobby token is minted with\n * `canPublish: false` and `canSubscribe: false`, so the waiting room can only\n * become a room by minting a NEW token. Before this existed the waiting room\n * said \"you will join automatically once you are admitted\" and then waited\n * forever — a sentence on screen that was not true. The provider wires this\n * to `engine.rejoin()`.\n */\n onAdmitted?: (() => void) | undefined;\n /**\n * S4: the meeting pass this device holds (`x-meet-pass`) — what the state read and\n * `/leave` are authorized by. Null when no pass is held yet.\n */\n resolvePass?: (() => string | null) | undefined;\n /** S4: mint again (Ask to join again / Rejoin from the waiting screens). */\n rejoin?: (() => Promise<void>) | undefined;\n}\n\nexport interface MeetingSession {\n /** Subscribe to the room's out-of-band plane. Hosts see knocks; guests see verdicts. */\n watch(roomName: RoomName): void;\n /**\n * Subscribe to the MEETING RECORD's durable feed — transcript segments, notes,\n * and the meeting row itself (which is where the note-taker's state lives).\n * Reconnecting IS re-reading: the backfill door re-reads all three, because\n * realtime has no replay and a socket blip would otherwise leave a transcript\n * permanently missing the minute nobody noticed.\n */\n watchMeeting(meetingId: MeetingId): void;\n admit(entry: LobbyEntry, admitted: boolean): Promise<void>;\n /** S4: lift a denial so the person may knock again (`meet_undeny`). */\n undeny(entry: LobbyEntry): Promise<void>;\n admitAll(): Promise<void>;\n /**\n * 🚨 S4 — THE WAIT READS SERVER TRUTH (CORE-DESIGN §2.6). While this tab waits, the state\n * read (`GET /v1/meet/state`) runs every `waiting_poll_seconds`, on every\n * `mx.state.changed` hint, on `online` and on visibility return. Admitted → a fresh mint\n * (`rejoin`); denied / expired / ended are what the row says, never a broadcast's guess.\n */\n startWaiting(): void;\n stopWaiting(): void;\n /** One state read now (idempotent while one is in flight). */\n readState(): Promise<void>;\n /** One read of the meeting's rules now (chat mode, attendee lock, time limit); resolves once they are on the store. */\n readLivePolicy(): Promise<void>;\n /** rec-consent-gate: tell the SERVER this person agreed to being recorded (a row fact; it gives publish back). */\n acknowledgeConsent(): Promise<void>;\n /**\n * §3.4 ER: what the server recorded for THIS connection's exit, and the run's phase. null =\n * the read failed (offline / unreachable); undefined = no meeting or no pass to ask with.\n */\n readExit(connection: string, pass?: string | null): Promise<ExitServerWord | null | undefined>;\n /** Stop waiting: the row becomes `withdrawn` (`POST /v1/meet/leave {intent: \"withdraw\"}`). */\n withdraw(): Promise<void>;\n /** Ask again after a denial or an expired knock (only offered when allowed). */\n askAgain(): Promise<void>;\n /**\n * S8: the HOST's leave intents through `POST /v1/meet/leave` — end the meeting for everyone,\n * or hand the run's host powers to `assignTo` (a person key) before leaving. The server\n * decides from the pass whether this person holds the run's host powers.\n */\n hostLeave(intent: \"end_for_all\" | { readonly assignTo: string }): Promise<void>;\n setLock(locked: boolean): Promise<MeetingRecord>;\n /** `wr-policy-who-waits`: one rule of the meeting on screen (null when unreadable). */\n meetingRule(key: string): Promise<unknown>;\n /** A host changes one rule for this meeting; waiting people are told to read again. */\n setMeetingRule(key: string, value: string | number | boolean): Promise<unknown>;\n /** End it for everyone, through the server (MRI-A6). */\n endForEveryone(): Promise<EndMeetingResult>;\n dispose(): void;\n}\n\n/** Can `actor` moderate `target`? A host outranks a co-host outranks everyone. */\nexport function canModerateParticipant(\n actor: MeetParticipant | undefined,\n target: MeetParticipant | undefined,\n): boolean {\n if (actor === undefined || target === undefined) return false;\n if (actor.role === \"participant\") return false;\n if (actor.identity === target.identity) return false;\n // A co-host cannot remove the host. Without this rank check, \"host controls\"\n // is a mutual-eviction button.\n return ROLE_RANK[actor.role] > ROLE_RANK[target.role];\n}\n\nexport function roleOf(\n participants: readonly MeetParticipant[],\n identity: ParticipantIdentity | null,\n): ParticipantRole {\n if (identity === null) return \"participant\";\n return participants.find((p) => p.identity === identity)?.role ?? \"participant\";\n}\n\n/**\n * What ending a meeting produced. `served: false` is the honest answer from a\n * server that has not landed `POST /v1/meet/end` yet (MRI-A6) — the control is\n * real, the refusal is a sentence, and nothing pretends the meeting ended.\n */\nexport interface EndMeetingResult {\n readonly endedAt: string | null;\n readonly served: boolean;\n}\n\n/** The sentence a server without MRI-A6 earns. Pinned so every surface says it. */\nexport const END_MEETING_NOT_SERVED =\n \"Ending a meeting is not available on this server yet\";\n\n/**\n * One durable `meet_participants` row as a lobby entry, or `null` when it is\n * not one. A row that has been decided is not a knocker; the panel shows people\n * who are WAITING, and `store.upsertLobby` keyed by identity is what lets the\n * table and the broadcast be two copies of one person rather than two rows.\n */\nfunction lobbyEntryFrom(attendee: MeetingAttendee, now: number = Date.now()): LobbyEntry | null {\n if (!WAITING_ADMISSION_STATES.includes(attendee.admissionState)) return null;\n // A knock past its expiry IS expired (the server CASes it on the knocker's next read).\n const expires = attendee.knockExpiresAt ? Date.parse(attendee.knockExpiresAt) : Number.NaN;\n if (!Number.isNaN(expires) && expires <= now) return null;\n return {\n identity: attendee.identity,\n displayName: attendee.displayName,\n avatarUrl: attendee.avatarUrl,\n userId: attendee.userId,\n state: \"knocking\",\n knockedAt: attendee.knockedAt ?? attendee.joinedAt ?? new Date().toISOString(),\n knockExpiresAt: attendee.knockExpiresAt ?? null,\n decidedAt: null,\n decidedBy: null,\n flag: attendee.knockFlag ?? null,\n };\n}\n\n/**\n * A DENIED row as a lobby entry, so the host can lift the denial (`meet_undeny`) — the only door\n * that lets a refused person knock again under the `until_lifted` rule. Null for every other row.\n */\nfunction deniedEntryFrom(attendee: MeetingAttendee): LobbyEntry | null {\n if (attendee.admissionState !== \"denied\") return null;\n return {\n identity: attendee.identity,\n displayName: attendee.displayName,\n avatarUrl: attendee.avatarUrl,\n userId: attendee.userId,\n state: \"denied\",\n knockedAt: attendee.knockedAt ?? attendee.joinedAt ?? new Date().toISOString(),\n knockExpiresAt: null,\n decidedAt: null,\n decidedBy: null,\n };\n}\n\n/** One row → the door's list: a knocker waits, a denied person can be let back in; the rest clear. */\nfunction applyDoorRow(store: MeetStore, attendee: MeetingAttendee): void {\n const entry = lobbyEntryFrom(attendee) ?? deniedEntryFrom(attendee);\n if (entry !== null) {\n store.upsertLobby(entry);\n return;\n }\n // 🚨 A DECIDED ROW MUST CLEAR THE DOOR. Admitting a guest from another tab writes `admitted`\n // here, and ignoring the row would leave this host's panel offering to admit somebody who is\n // already in the meeting — a ghost knocker who can never be dismissed.\n store.setAdmission(attendee.identity, decidedState(attendee.admissionState), null);\n}\n\n/** The row's word for a person no longer waiting, as the host's lobby list records it. */\nfunction decidedState(state: AdmissionState): AdmissionState {\n return WAITING_ADMISSION_STATES.includes(state) ? \"expired\" : state;\n}\n\n/** The server's state read, as this package reads it. */\ninterface StateRead {\n readonly admission_state?: string | null;\n readonly admitters_present?: boolean;\n readonly reknock_allowed?: boolean;\n readonly reknock_in_seconds?: number | null;\n readonly knock_expires_at?: string | null;\n readonly session_phase?: string;\n readonly meeting_phase?: string;\n readonly live_version?: number;\n readonly poll_seconds?: number;\n readonly exit_reason?: string | null;\n readonly live_policy?: Record<string, unknown>;\n readonly consent_given?: boolean;\n readonly session_started_at?: string | null;\n}\n\n/** How often a person in the call re-reads the meeting's rules. */\nexport const LIVE_POLICY_POLL_MS = 3000;\n\nexport function createMeetingSession(options: MeetingSessionOptions): MeetingSession {\n const { manager, repository, store, identity } = options;\n let channel: ChannelHandle | null = null;\n let meetingChannel: ChannelHandle | null = null;\n\n /**\n * The meeting row, for anyone who may be in it. A member or a person the\n * meeting was shared with reads it under RLS; a signed-in person from ANOTHER\n * organization who followed the link cannot, and gets the same row through\n * the link door (`meet_meeting_by_slug`) a guest uses — the server lets them\n * in by the link, so the screen must not fail them for holding it.\n */\n async function readMeeting(meetingId: MeetingId): Promise<MeetingRecord> {\n try {\n return await repository.meeting(meetingId);\n } catch (error) {\n const slug = store.snapshot().meeting?.slug;\n if (slug === undefined) throw error;\n return repository.meetingBySlug(slug);\n }\n }\n\n function requireHostUser(operation: string): UserId {\n // THE SAME PREDICATE THE UI RENDERS FROM (MRI-A11). Reading only the roster\n // here — while the control renders from the token grant — is how a host\n // control becomes a button that throws the moment it is used.\n if (identity.userId === null || !isHostRole(effectiveRole(store.snapshot()))) {\n throw new MeetError(\n \"forbidden\",\n `${operation}: this participant is not a host`,\n \"Host powers come from the server-minted token metadata and are enforced by the RPC. \" +\n \"Hide the control rather than rendering a button that will be refused.\",\n );\n }\n return identity.userId;\n }\n\n /**\n * The door's own rule, mirrored (`communication._meet_may_admit`): a host or co-host, or — when no\n * host or co-host is in the room — a signed-in member, as far as the meeting's `headless_powers`\n * lets (meeting-without-host). The RPC enforces it; this only keeps a refused button from throwing.\n */\n function requireAdmitterUser(operation: string): UserId {\n const snapshot = store.snapshot();\n if (identity.userId === null || !mayAdmitNow(effectiveRole(snapshot), snapshot.participants, snapshot.livePolicy)) {\n throw new MeetError(\n \"forbidden\",\n `${operation}: this participant may not let people in`,\n \"Only a host or co-host admits people, unless the meeting has no host in it and lets members admit.\",\n );\n }\n return identity.userId;\n }\n\n function requireMeeting(operation: string): MeetingRecord {\n const meeting = store.snapshot().meeting;\n if (meeting === null) {\n throw new MeetError(\n \"misconfigured\",\n `${operation}: no meeting is loaded`,\n \"Ad-hoc call rooms have no meeting record and therefore no lobby or lock. Use \" +\n \"joinMeeting()/scheduleMeeting() when you need those powers.\",\n );\n }\n return meeting;\n }\n\n // ── S4: the waiting supervisor ─────────────────────────────────────────\n let polling = false;\n let pollTimer: ReturnType<typeof setTimeout> | null = null;\n let reading: Promise<void> | null = null;\n let admittedOnce = false;\n let unlisten: (() => void) | null = null;\n\n function report(level: \"info\" | \"warn\" | \"error\", message: string, remedy?: string): void {\n options.onDiagnostic?.(remedy === undefined ? { level, message } : { level, message, remedy });\n }\n\n function pollMs(): number {\n const seconds = store.snapshot().waiting?.pollSeconds ?? 3;\n return Math.max(1, Math.min(60, seconds)) * 1000;\n }\n\n function schedule(ms: number): void {\n if (pollTimer !== null) clearTimeout(pollTimer);\n if (!polling) return;\n pollTimer = setTimeout(() => {\n pollTimer = null;\n void readState().finally(() => schedule(pollMs()));\n }, ms);\n }\n\n function listen(): void {\n if (unlisten !== null || typeof window === \"undefined\") return;\n const nudge = (): void => {\n if (typeof document !== \"undefined\" && document.visibilityState === \"hidden\") return;\n void readState();\n };\n // Closing the tab while waiting withdraws the knock (beacon: no headers, pass in the body).\n const goodbye = (): void => {\n const meeting = store.snapshot().meeting;\n const pass = options.resolvePass?.() ?? null;\n const waiting = store.snapshot().waiting;\n if (meeting === null || pass === null || waiting === null) return;\n if (!WAITING_ADMISSION_STATES.includes(waiting.admission)) return;\n try {\n navigator.sendBeacon?.(\n options.api.url(MEET_ROUTES.leave),\n new Blob([JSON.stringify({ meeting_id: meeting.id, intent: \"withdraw\", meeting_pass: pass })], {\n type: \"application/json\",\n }),\n );\n } catch {\n // Best effort: an unanswered knock still lapses on its own (knock_timeout_minutes).\n }\n };\n window.addEventListener(\"online\", nudge);\n window.addEventListener(\"pagehide\", goodbye);\n document.addEventListener(\"visibilitychange\", nudge);\n unlisten = () => {\n window.removeEventListener(\"online\", nudge);\n window.removeEventListener(\"pagehide\", goodbye);\n document.removeEventListener(\"visibilitychange\", nudge);\n };\n }\n\n function stopWaiting(): void {\n polling = false;\n if (pollTimer !== null) clearTimeout(pollTimer);\n pollTimer = null;\n unlisten?.();\n unlisten = null;\n }\n\n function patchWaiting(next: Partial<MeetWaiting>): void {\n const current = store.snapshot().waiting;\n const base: MeetWaiting = current ?? {\n admission: \"knocking\",\n admittersPresent: null,\n reknockAllowed: true,\n knockExpiresAt: null,\n pollSeconds: 3,\n liveVersion: 0,\n };\n store.patch({ waiting: { ...base, ...next } });\n }\n\n function apply(read: StateRead): void {\n const snapshot = store.snapshot();\n if (!isWaitingPhase(snapshot.phase)) return;\n const admission = (read.admission_state ?? null) as AdmissionState | null;\n const ended =\n admission === \"ended\" || read.session_phase === \"ended\" || read.meeting_phase === \"ended\";\n if (admission === \"admitted\" && !ended) {\n if (admittedOnce) return;\n admittedOnce = true;\n stopWaiting();\n // ADMISSION IS A JOIN: a fresh mint with the grants the server now gives.\n options.onAdmitted?.();\n return;\n }\n if (ended) {\n stopWaiting();\n patchWaiting({ admission: \"ended\" });\n const meeting = snapshot.meeting;\n if (read.meeting_phase === \"ended\" && meeting !== null && meeting.endedAt === null) {\n store.patch({ meeting: { ...meeting, endedAt: new Date().toISOString() } });\n }\n store.dispatch({ type: \"admission\", admission: \"ended\" }, { reason: \"ended\" });\n return;\n }\n if (admission === null) return;\n patchWaiting({\n admission,\n admittersPresent: read.admitters_present ?? null,\n reknockAllowed: read.reknock_allowed ?? true,\n reknockAt: typeof read.reknock_in_seconds === \"number\" && read.reknock_in_seconds > 0 ? Date.now() + read.reknock_in_seconds * 1000 : null,\n knockExpiresAt: read.knock_expires_at ?? null,\n pollSeconds: read.poll_seconds ?? snapshot.waiting?.pollSeconds ?? 3,\n liveVersion: read.live_version ?? 0,\n });\n // THE SERVER'S WORD MOVES THE PHASE (§3.1): one machine event, never a patch.\n store.dispatch({ type: \"admission\", admission });\n // A decision that is not \"yes\" ends the wait; the screen offers what the rules allow.\n if (admission === \"denied\" || admission === \"expired\" || admission === \"withdrawn\" || admission === \"removed\") {\n stopWaiting();\n }\n }\n\n // ── P1: the rules, live, while in the call ─────────────────────────────\n let policyTimer: ReturnType<typeof setInterval> | null = null;\n let policyReading = false;\n let policyInFlight: Promise<void> | null = null;\n\n async function readLivePolicy(): Promise<void> {\n const meeting = store.snapshot().meeting;\n const pass = options.resolvePass?.() ?? null;\n if (meeting === null || pass === null) return;\n // A read already in flight may have been asked BEFORE the host changed a rule (it was sent,\n // then the rule moved, then its answer landed): the caller needs a read that starts now, so\n // wait for that one and then run a fresh one.\n if (policyReading) {\n await (policyInFlight ?? undefined);\n return readLivePolicy();\n }\n policyReading = true;\n let done: () => void = () => undefined;\n policyInFlight = new Promise<void>((resolve) => (done = resolve));\n try {\n const response = await options.api.request({\n path: `${MEET_ROUTES.state}?meeting_id=${encodeURIComponent(meeting.id)}`,\n method: \"GET\",\n requireAuth: false,\n operation: \"meet.state(policy)\",\n headers: { [MEETING_PASS_HEADER]: pass },\n });\n if (!response.ok) return;\n const read = (await response.json()) as StateRead;\n const next = livePolicyFrom(read.live_policy);\n const started = read.session_started_at ? Date.parse(read.session_started_at) : NaN;\n const snapshot = store.snapshot();\n const startedAt = Number.isFinite(started) ? started : snapshot.sessionStartedAt;\n if (!samePolicy(snapshot.livePolicy, next) || startedAt !== snapshot.sessionStartedAt) {\n store.patch({ livePolicy: next, sessionStartedAt: startedAt });\n }\n // The server's row is the consent, not this screen's memory (reload, second tab, rejoin).\n if (read.consent_given === true && !store.snapshot().recordingConsentGiven) {\n store.patch({ recordingConsentGiven: true });\n }\n } catch {\n // Offline: the next tick asks again; the rules on screen stay as they were.\n } finally {\n policyReading = false;\n policyInFlight = null;\n done();\n }\n }\n\n function syncLivePolicyTimer(): void {\n const inCall = store.snapshot().phase === \"in_call\";\n if (inCall && policyTimer === null) {\n void readLivePolicy();\n policyTimer = setInterval(() => void readLivePolicy(), LIVE_POLICY_POLL_MS);\n } else if (!inCall && policyTimer !== null) {\n clearInterval(policyTimer);\n policyTimer = null;\n }\n }\n const unsubscribePolicy = store.subscribe(syncLivePolicyTimer);\n\n async function readState(): Promise<void> {\n if (reading !== null) return reading;\n const meeting = store.snapshot().meeting;\n const pass = options.resolvePass?.() ?? null;\n if (meeting === null || pass === null) return;\n reading = (async () => {\n try {\n const response = await options.api.request({\n path: `${MEET_ROUTES.state}?meeting_id=${encodeURIComponent(meeting.id)}`,\n method: \"GET\",\n requireAuth: false,\n operation: \"meet.state\",\n headers: { [MEETING_PASS_HEADER]: pass },\n });\n if (!response.ok) {\n let body: unknown = null;\n try {\n body = await response.json();\n } catch {\n body = null;\n }\n const refusal = readServerRefusal(body);\n report(\n \"warn\",\n refusal?.reason ?? `meet.state: the server answered ${response.status}`,\n refusal?.remedy ?? \"The waiting room keeps asking; nothing on this screen changes until the server answers.\",\n );\n return;\n }\n apply((await response.json()) as StateRead);\n } catch (error) {\n // Offline or the server is unreachable: the next tick (or `online`) asks again.\n report(\"info\", `meet.state: ${String(error)}`, \"Waiting continues; the state is read again shortly.\");\n } finally {\n reading = null;\n }\n })();\n return reading;\n }\n\n async function postLeave(\n intent: \"withdraw\" | \"leave\" | \"end_for_all\" | \"assign_and_leave\",\n extra: Record<string, unknown> = {},\n ): Promise<void> {\n const meeting = requireMeeting(`meet.${intent}`);\n const pass = options.resolvePass?.() ?? null;\n const response = await options.api.request({\n path: MEET_ROUTES.leave,\n method: \"POST\",\n requireAuth: false,\n operation: `meet.${intent}`,\n body: { meeting_id: meeting.id, intent, ...extra },\n ...(pass !== null ? { headers: { [MEETING_PASS_HEADER]: pass } } : {}),\n });\n if (!response.ok) {\n let body: unknown = null;\n try {\n body = await response.json();\n } catch {\n body = null;\n }\n const refusal = readServerRefusal(body);\n throw new MeetError(\n \"transport\",\n refusal?.reason ?? `meet.${intent}: the server answered ${response.status}`,\n refusal?.remedy ??\n (intent === \"withdraw\" ? \"You are still in the waiting room. Try again.\" : \"Nothing was changed. Try again.\"),\n );\n }\n }\n\n /** After a member-client admission write the CLIENT sends the hint (CORE-DESIGN §2.4). */\n function hint(): void {\n channel?.send(MEET_EVENTS.stateChanged, { v: null });\n }\n\n /**\n * After a member-client admission DOOR the SERVER republishes the room's replicated state\n * (the waiting count everyone's People badge shows) — only the server writes room state\n * (CORE-DESIGN §2.4, §2.6 RS). Truth first, then the hint. A failed tell is reported, never\n * thrown: the decision stands, and the count catches up on the next server write.\n */\n async function tellServerAdmissionChanged(operation: string): Promise<void> {\n const meeting = store.snapshot().meeting;\n if (meeting === null) return;\n const pass = options.resolvePass?.() ?? null;\n try {\n const response = await options.api.request({\n path: MEET_ROUTES.admissionChanged,\n method: \"POST\",\n requireAuth: false,\n operation,\n body: { meeting_id: meeting.id },\n ...(pass !== null ? { headers: { [MEETING_PASS_HEADER]: pass } } : {}),\n });\n if (!response.ok) {\n report(\n \"warn\",\n `${operation}: the server answered ${response.status} when told about the admission change`,\n \"The decision was saved. The waiting count in the room may lag until the next change.\",\n );\n }\n } catch (error) {\n report(\n \"warn\",\n `${operation}: ${String(error)}`,\n \"The decision was saved. The waiting count in the room may lag until the next change.\",\n );\n }\n }\n\n /** The host's second carrier: on a hint, re-read who is at the door. */\n async function refreshQueue(): Promise<void> {\n const meeting = store.snapshot().meeting;\n if (meeting === null || isGuest(identity) || !isHostRole(effectiveRole(store.snapshot()))) return;\n try {\n const attendees = await repository.attendees(meeting.id);\n store.learn(attendees);\n for (const attendee of attendees) applyDoorRow(store, attendee);\n } catch {\n // Postgres Changes on the same rows is the other carrier; a miss here costs nothing.\n }\n }\n\n return {\n watch(roomName) {\n channel?.close();\n channel = manager.open({\n topic: roomTopic(roomName),\n broadcast: [\n {\n // 🚨 PAYLOAD-FREE. Every receiver reads again; a spoofed hint only causes a read.\n event: MEET_EVENTS.stateChanged,\n onMessage: () => {\n if (isWaitingPhase(store.snapshot().phase)) void readState();\n void refreshQueue();\n },\n },\n ],\n // THE BACKFILL DOOR. A socket blip must not lose a decision: re-read the meeting\n // row, and a waiting tab re-reads its own state.\n onBackfill: async () => {\n const meeting = store.snapshot().meeting;\n if (meeting === null) return;\n const fresh = isGuest(identity)\n ? await repository.meetingBySlug(meeting.slug)\n : await readMeeting(meeting.id);\n store.patch({ meeting: fresh, locked: fresh.locked });\n if (isWaitingPhase(store.snapshot().phase)) void readState();\n },\n });\n },\n\n watchMeeting(meetingId) {\n meetingChannel?.close();\n meetingChannel = null;\n if (isGuest(identity)) {\n options.onDiagnostic?.({\n level: \"info\",\n message: \"Guest notes and captions arrive through the live meeting room; saved history is not available without a session.\",\n remedy: \"Sign in with access to this meeting to reload saved notes and transcript history.\",\n });\n return;\n }\n meetingChannel = manager.open({\n topic: meetingTopic(meetingId),\n postgresChanges: [\n {\n event: \"*\",\n schema: repository.schema,\n table: repository.tables.transcripts,\n filter: `meeting_id=eq.${meetingId}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: ({ row }) => {\n // D16: THE ROW IS THE TRANSCRIPT. It replaces the caption that\n // showed the same words a moment earlier.\n if (row !== null) store.ingestRecord(repository.projectSegment(row));\n },\n },\n {\n event: \"*\",\n schema: repository.schema,\n table: repository.tables.notes,\n filter: `meeting_id=eq.${meetingId}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: ({ row }) => {\n // Upsert, not append: a re-run of the wrap-up rewrites the SAME\n // row ids by design, and appending would show every note twice.\n if (row !== null) store.upsertNote(repository.projectNote(row));\n },\n },\n {\n // 🚨 THE LOBBY IS A TABLE, NOT ONLY A CHANNEL (MRI-A16). A guest\n // knocks by writing `admission_state='knocking'` AND broadcasting;\n // through 0.4.7 the host's People panel rendered only the broadcast,\n // so a knocker whose packet landed before this browser opened the\n // channel — a host who joined second, reloaded, or slept a laptop —\n // was INVISIBLE while the guest sat in the waiting room forever. A\n // host can read this table; a guest cannot, and gets no lobby list\n // at all, which is correct.\n event: \"*\",\n schema: repository.schema,\n table: repository.tables.participants,\n filter: `meeting_id=eq.${meetingId}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: ({ row }) => {\n if (row === null) return;\n const attendee = repository.projectAttendee(row);\n store.learn([attendee]);\n // The durable row is the verdict, wherever it was made (see `applyDoorRow`).\n applyDoorRow(store, attendee);\n },\n },\n {\n event: \"*\",\n schema: repository.schema,\n table: repository.tables.recordings,\n filter: `meeting_id=eq.${meetingId}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: ({ row }) => {\n // 🚨 THE INDICATOR RENDERS THE DURABLE STATE, not this browser's\n // hope. Through 0.4.7 nothing subscribed here, so the only\n // transitions the store ever saw were the optimistic `starting`\n // and `stopping` THIS client patched — and the indicator sat on\n // \"Starting recording…\" for a whole meeting while a healthy\n // egress ran, then \"Stopping recording…\" forever (MRI-C8).\n if (row === null) return;\n const recording = repository.projectRecording(row);\n store.setRecording(recording.state, recording.startedBy, \"record\", recording.id);\n },\n },\n {\n event: \"*\",\n schema: repository.schema,\n table: repository.tables.meetings,\n filter: `id=eq.${meetingId}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: ({ row }) => {\n // The note-taker's state lives on this row's metadata (D9/MRI-A2),\n // so this subscription is what keeps each server-recorded state\n // current on screen without a reload.\n if (row === null) return;\n const meeting = repository.projectMeeting(row);\n store.patch({ meeting, locked: meeting.locked });\n },\n },\n ],\n // THE BACKFILL DOOR. Realtime has no replay: everything that happened\n // while the socket was down is gone. Re-reading all three is what keeps\n // a recovered surface from looking healthy and being wrong.\n onBackfill: async () => {\n const [meeting, transcript, notes, recordings, attendees] = await Promise.all([\n readMeeting(meetingId),\n repository.transcript(meetingId),\n repository.notes(meetingId),\n repository.recordings(meetingId),\n repository.attendees(meetingId),\n ]);\n store.patch({ meeting, locked: meeting.locked });\n for (const segment of transcript) store.ingestRecord(segment);\n for (const note of notes) store.upsertNote(note);\n // A socket blip must not leave the indicator saying something the\n // database stopped agreeing with ten minutes ago. `recordings` is\n // newest first.\n const latest = recordings[0];\n if (latest !== undefined) {\n store.setRecording(latest.state, latest.startedBy, \"record\", latest.id);\n }\n // Everyone the durable rows know a name for, and every knocker still\n // waiting. A host who reloads must not lose a person at the door.\n store.learn(attendees);\n // The same rule on the backfill door: a host who reloads must not find people at the\n // door who were admitted while they were gone, and finds the people they declined.\n for (const attendee of attendees) applyDoorRow(store, attendee);\n },\n });\n // The FIRST read is the same read: a surface that joins a meeting already\n // in progress must see what was already said.\n meetingChannel.requestBackfill(\"manual\");\n },\n\n async admit(entry, admitted) {\n const by = requireAdmitterUser(\"meet.admit\");\n const meeting = requireMeeting(\"meet.admit\");\n // THE RPC IS THE DECISION, and it returns the row as it now stands — that row (not\n // this click) is what the panel shows. Two hosts deciding at once is the ordinary\n // case: the second sees the first's verdict.\n const row = await repository.decideAdmission({\n meetingId: meeting.id,\n identity: entry.identity,\n decision: admitted ? \"admit\" : \"deny\",\n byUserId: by,\n });\n store.upsertLobby({\n ...entry,\n state: decidedState(row.admissionState),\n decidedAt: new Date().toISOString(),\n decidedBy: by,\n });\n await tellServerAdmissionChanged(\"meet.admit\");\n hint();\n },\n\n async undeny(entry) {\n const by = requireAdmitterUser(\"meet.undeny\");\n const meeting = requireMeeting(\"meet.undeny\");\n const row = await repository.decideAdmission({\n meetingId: meeting.id,\n identity: entry.identity,\n decision: \"undeny\",\n byUserId: by,\n });\n store.upsertLobby({ ...entry, state: decidedState(row.admissionState), decidedAt: new Date().toISOString(), decidedBy: by });\n await tellServerAdmissionChanged(\"meet.undeny\");\n hint();\n },\n\n async admitAll() {\n const by = requireAdmitterUser(\"meet.admitAll\");\n const meeting = requireMeeting(\"meet.admitAll\");\n const rows = await repository.admitAll(meeting.id, by);\n for (const row of rows) {\n store.setAdmission(row.identity, row.admissionState, by);\n }\n await tellServerAdmissionChanged(\"meet.admitAll\");\n hint();\n },\n\n startWaiting() {\n if (polling) return;\n polling = true;\n admittedOnce = false;\n listen();\n schedule(0);\n },\n\n stopWaiting,\n readState,\n readLivePolicy,\n\n async acknowledgeConsent() {\n const meeting = requireMeeting(\"meet.consent\");\n const pass = options.resolvePass?.() ?? null;\n const response = await options.api.request({\n path: MEET_ROUTES.consent,\n method: \"POST\",\n requireAuth: false,\n operation: \"meet.consent\",\n body: { meeting_id: meeting.id },\n ...(pass !== null ? { headers: { [MEETING_PASS_HEADER]: pass } } : {}),\n });\n if (!response.ok) {\n throw new MeetError(\"transport\", `meet.consent: the server answered ${response.status}`, \"Your agreement was not saved. Try again.\");\n }\n },\n\n async readExit(connection, held) {\n const meeting = store.snapshot().meeting;\n const pass = held ?? options.resolvePass?.() ?? null;\n if (meeting === null || pass === null) return undefined;\n try {\n const response = await options.api.request({\n path: `${MEET_ROUTES.state}?meeting_id=${encodeURIComponent(meeting.id)}&connection=${encodeURIComponent(connection)}`,\n method: \"GET\",\n requireAuth: false,\n operation: \"meet.state(exit)\",\n headers: { [MEETING_PASS_HEADER]: pass },\n });\n if (!response.ok) return null;\n const read = (await response.json()) as StateRead;\n return {\n exitReason: read.exit_reason ?? null,\n sessionPhase: read.session_phase ?? null,\n meetingPhase: read.meeting_phase ?? null,\n };\n } catch {\n return null;\n }\n },\n\n async withdraw() {\n stopWaiting();\n await postLeave(\"withdraw\");\n patchWaiting({ admission: \"withdrawn\" });\n store.dispatch({ type: \"admission\", admission: \"withdrawn\" });\n hint();\n },\n\n async hostLeave(intent) {\n if (intent === \"end_for_all\") await postLeave(\"end_for_all\");\n else await postLeave(\"assign_and_leave\", { assign_to: intent.assignTo });\n },\n\n async askAgain() {\n // A new mint IS the new knock (the server upserts the person row per the re-knock rule).\n await options.rejoin?.();\n },\n\n async meetingRule(key) {\n const meeting = requireMeeting(\"meet.meetingRule\");\n return repository.meetingRule(meeting.id, key);\n },\n\n async setMeetingRule(key, value) {\n requireHostUser(\"meet.setMeetingRule\");\n const meeting = requireMeeting(\"meet.setMeetingRule\");\n const effective = await repository.setMeetingRule(meeting.id, key, value);\n hint();\n return effective;\n },\n\n async setLock(locked) {\n const by = requireHostUser(\"meet.setLock\");\n const meeting = requireMeeting(\"meet.setLock\");\n const updated = await repository.setLock(meeting.id, locked, by);\n store.patch({ meeting: updated, locked: updated.locked });\n // The lock is the meeting row (members see it through Postgres Changes); waiting people\n // are told to read again.\n hint();\n return updated;\n },\n\n /**\n * END IT FOR EVERYONE — a SERVER decision, not a row this client updates.\n *\n * 0.2.1 called the `meet_end_meeting` RPC directly, which ended the meeting\n * in the database while LiveKit kept the room open and the note-taker kept\n * writing into an ended meeting. `POST /v1/meet/end` re-checks the host,\n * closes the room, and triggers the wrap-up — one decision, one place\n * (MRI-A6).\n *\n * 🚨 A SERVER THAT HAS NOT LANDED THAT ROUTE ANSWERS 404, and that is\n * reported as a sentence, never as a dead control and never as a success.\n */\n async endForEveryone() {\n requireHostUser(\"meet.endForEveryone\");\n const meeting = requireMeeting(\"meet.endForEveryone\");\n const response = await options.api.request({\n path: MEET_ROUTES.meetingEnd,\n method: \"POST\",\n requireAuth: true,\n operation: \"meet.endForEveryone\",\n body: {\n meeting_id: meeting.id,\n organization_id: identity.organizationId,\n },\n });\n if (response.status === 404) {\n return { endedAt: null, served: false };\n }\n if (!response.ok) {\n let body: unknown = null;\n try {\n body = await response.json();\n } catch {\n body = null;\n }\n const refusal = readServerRefusal(body);\n throw new MeetError(\n response.status === 403 ? \"forbidden\" : \"transport\",\n refusal?.reason ?? `meet.endForEveryone: the server answered ${response.status}`,\n refusal?.remedy !== undefined && refusal.remedy.length > 0\n ? refusal.remedy\n : \"The meeting is still running. Only a host may end it, and the server \" +\n \"re-checks that — being a host on this screen is not the decision.\",\n );\n }\n let payload: unknown = null;\n try {\n payload = await response.json();\n } catch {\n payload = null;\n }\n const row =\n typeof payload === \"object\" && payload !== null\n ? (payload as Record<string, unknown>)\n : {};\n const endedAt =\n typeof row[\"ended_at\"] === \"string\" ? row[\"ended_at\"] : new Date().toISOString();\n const current = store.snapshot().meeting;\n if (current !== null) store.patch({ meeting: { ...current, endedAt } });\n return { endedAt, served: true };\n },\n\n dispose() {\n stopWaiting();\n unsubscribePolicy();\n if (policyTimer !== null) clearInterval(policyTimer);\n policyTimer = null;\n channel?.close();\n channel = null;\n meetingChannel?.close();\n meetingChannel = null;\n },\n };\n}\n\n/**\n * A durable public slug. Lowercase, hyphenated, and deliberately readable —\n * people read these aloud on phone calls. The alphabet excludes the four\n * characters that get misheard (0/O, 1/l).\n */\nconst SLUG_ALPHABET = \"abcdefghijkmnpqrstuvwxyz23456789\";\n\nexport function meetingSlug(random: () => number = Math.random): string {\n const group = (length: number): string =>\n Array.from({ length }, () => {\n const index = Math.floor(random() * SLUG_ALPHABET.length);\n return SLUG_ALPHABET[index] ?? \"a\";\n }).join(\"\");\n return `${group(3)}-${group(4)}-${group(3)}`;\n}\n\n/** The link a host copies. ONE shape, so every surface produces the same URL. */\nexport function meetingLink(baseUrl: string, slug: string): string {\n return `${baseUrl.replace(/\\/+$/, \"\")}/meet/${slug}`;\n}\n","/**\n * THE ONE WAY THIS PACKAGE TALKS TO AIDREAM (MRI-A16).\n *\n * 🚨 **Every request to the aidream server carries `x-organization-id`, and the\n * only way to guarantee \"every\" is for there to be ONE place that builds the\n * request.** aidream's org-admission middleware refuses an authenticated\n * request that does not name the organization it is acting in — 400\n * `organization_required`, before the router is ever reached.\n *\n * 0.1.3 learned that lesson and put the header on the three meet callers by\n * hand. That fixed the instances and left the CLASS wide open, and on\n * 2026-09-09 the class collected its next victim: `<RecordingPlayer>` fetched\n * `GET /files/{id}/download?inline=1` through a bare `fetch` with only a bearer\n * token, so a perfectly landed 1.38 MB recording could not be played by the\n * host who made it (MRI-C8; the identical URL returned `200 video/mp4` the\n * moment the header was added by hand). A fourth caller was written months\n * after the lesson and never heard it, because a lesson written into three call\n * sites is not a lesson, it is three copies of a workaround.\n *\n * So: this module owns the base URL, the organization header and the bearer\n * token, and `api.test.ts` holds a guard that FAILS THE BUILD if any other file\n * in the package calls `fetch(` against a base URL or points an element `src=`\n * at one. A new caller cannot be written without the header any more.\n *\n * 🚨 **HOW A `<video>` GETS ITS BYTES, given that signed URLs are eradicated.**\n * A browser media element cannot attach a header, so a bare\n * `src=\"https://server…/files/{id}/download\"` carries no session and no\n * organization and is refused — which is precisely what MRI-C8 saw. The\n * platform's answer is NOT a signed URL (`aidream/api/routers/files` mints\n * none, by law) but the FILE-SESSION COOKIE: `POST /files/session` mints an\n * HttpOnly `SameSite=None` cookie that the auth middleware accepts on exactly\n * the GET byte-serving routes (`COOKIE_AUTH_PATH_RE` in\n * `matrx_connect/middleware/session_cookie.py`), and a cookie rides a media\n * request that a header cannot. Every private `<img>`/`<video>` in the platform\n * already renders this way.\n *\n * So `fileMedia` mints the cookie and hands back a STREAMING url — which is\n * what lets a person scrub a two-hour recording instead of waiting for a\n * gigabyte to arrive. When the mint is refused (no session, an older server,\n * a browser blocking third-party cookies) it falls back to reading the bytes\n * with the organization header into a `blob:` url — and it SAYS it did, with\n * the reason, because a silent fallback that downloads a gigabyte is exactly\n * the kind of automatic intervention that must announce itself.\n */\n\nimport { MeetError, normalizeMeetError } from \"./errors\";\nimport type { OrganizationId } from \"./types\";\n\n/**\n * How the host tells us who is asking. Returns null before sign-in resolves,\n * and for a guest, who has no session at all and is still a first-class joiner.\n *\n * It lives HERE rather than in `tokens.ts` (where it was born) because this is\n * now the one module that turns a session into a request.\n */\nexport type AccessTokenSource = () => Promise<string | null>;\n\n/** The header aidream's org-admission middleware demands. One definition. */\nexport const ORGANIZATION_HEADER = \"x-organization-id\";\n\nexport interface MeetApiOptions {\n /** aidream's origin. Never a LiveKit or provider URL. */\n baseUrl: string;\n /** Named on EVERY request; the server refuses one that omits it. */\n organizationId: OrganizationId;\n accessToken: AccessTokenSource;\n fetchImpl?: typeof fetch | undefined;\n}\n\nexport interface MeetApiRequest {\n /** An absolute path on the aidream origin, e.g. `/api/v1/meet/token`. */\n path: string;\n method?: \"GET\" | \"POST\" | undefined;\n /** JSON body. Serialized here so no caller can forget the content type. */\n body?: Record<string, unknown> | undefined;\n /**\n * `true` — refuse before the wire when there is no session, with the given\n * operation named. `false` — send whatever session exists, or none (a guest\n * minting a room token is a real, authorized case).\n */\n requireAuth?: boolean | undefined;\n /** Names the caller in every error this raises. */\n operation: string;\n /** Extra headers. May not override the organization header. */\n headers?: Record<string, string> | undefined;\n /**\n * `\"include\"` for the ONE request that mints the file-session cookie — a\n * `Set-Cookie` on a cross-origin response is dropped without it.\n */\n credentials?: RequestCredentials | undefined;\n /** Cancels the request. The Q&A stream is abortable by the person asking. */\n signal?: AbortSignal | undefined;\n}\n\nexport interface MeetApi {\n readonly baseUrl: string;\n readonly organizationId: OrganizationId;\n /** The URL a request to `path` would go to. For diagnostics, never for fetching. */\n url(path: string): string;\n /** One request, with the organization header and the session already on it. */\n request(input: MeetApiRequest): Promise<Response>;\n /**\n * Read an authenticated file into a `blob:` URL an element can load. The\n * caller MUST `URL.revokeObjectURL` it — a leaked one pins the whole file in\n * memory for the life of the tab.\n */\n fetchObjectUrl(input: MeetApiRequest): Promise<string>;\n /**\n * A url a `<video>`/`<img>` can load for a platform file. Streams when the\n * file-session cookie can be minted; otherwise reads the bytes with the\n * organization header and says why it had to.\n */\n fileMedia(fileId: string, operation: string): Promise<MeetFileMedia>;\n /** Re-mint the file-session cookie. Returns whether it now exists. */\n refreshFileSession(): Promise<boolean>;\n}\n\n/** Where a media element's bytes are coming from, and whether that is plan A. */\nexport interface MeetFileMedia {\n readonly url: string;\n /** `stream` seeks and starts instantly; `bytes` is a fully downloaded blob. */\n readonly kind: \"stream\" | \"bytes\";\n /**\n * Set only for `bytes`, and never null-and-hoping: the sentence a surface\n * shows to explain why it had to download the whole file first.\n */\n readonly fallbackReason: string | null;\n}\n\n/** `POST /files/session` — mints the `mx_files_session` cookie (files law). */\nexport const FILE_SESSION_PATH = \"/files/session\";\n\nexport function createMeetApi(options: MeetApiOptions): MeetApi {\n const doFetch = options.fetchImpl ?? globalThis.fetch;\n const base = options.baseUrl.replace(/\\/+$/, \"\");\n\n async function headersFor(input: MeetApiRequest): Promise<Record<string, string>> {\n const token = await options.accessToken();\n if (token === null && input.requireAuth === true) {\n throw new MeetError(\n \"session-unavailable\",\n `${input.operation}: this request requires a signed-in session`,\n \"Render the signed-out or guest surface rather than a control that will be refused.\",\n );\n }\n return {\n ...(input.headers ?? {}),\n ...(input.body !== undefined ? { \"content-type\": \"application/json\" } : {}),\n // 🚨 LAST, so nothing a caller passes can shadow it.\n [ORGANIZATION_HEADER]: options.organizationId,\n ...(token !== null ? { authorization: `Bearer ${token}` } : {}),\n };\n }\n\n return {\n baseUrl: base,\n organizationId: options.organizationId,\n\n url(path) {\n return `${base}${path}`;\n },\n\n async request(input) {\n const headers = await headersFor(input);\n try {\n return await doFetch(`${base}${input.path}`, {\n method: input.method ?? (input.body !== undefined ? \"POST\" : \"GET\"),\n headers,\n ...(input.credentials !== undefined ? { credentials: input.credentials } : {}),\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n ...(input.body !== undefined ? { body: JSON.stringify(input.body) } : {}),\n });\n } catch (error) {\n throw normalizeMeetError(error, input.operation);\n }\n },\n\n async fetchObjectUrl(input) {\n const response = await this.request(input);\n if (response.status === 401 || response.status === 403) {\n throw new MeetError(\n \"forbidden\",\n `${input.operation}: you are not allowed to read this file`,\n \"The file is safe — this is a read decision, not a lost file. Sign in with an \" +\n \"account in this meeting's organization and try again.\",\n );\n }\n if (!response.ok) {\n throw new MeetError(\n \"transport\",\n `${input.operation}: the server returned ${response.status}`,\n \"The file itself is unaffected. Try again; if it repeats, the platform files \" +\n \"route is the thing to look at, not the recording.\",\n );\n }\n return URL.createObjectURL(await response.blob());\n },\n\n async refreshFileSession() {\n try {\n const response = await this.request({\n path: FILE_SESSION_PATH,\n method: \"POST\",\n // `Set-Cookie` on a cross-origin response is DROPPED without this.\n credentials: \"include\",\n requireAuth: true,\n operation: \"meet.files.session\",\n });\n return response.ok;\n } catch {\n // A refused mint is a fallback, never a failure: the caller reads the\n // bytes with the header instead and says so.\n return false;\n }\n },\n\n async fileMedia(fileId, operation) {\n const path = fileDownloadPath(fileId);\n if (await this.refreshFileSession()) {\n return { url: `${base}${path}`, kind: \"stream\", fallbackReason: null };\n }\n return {\n url: await this.fetchObjectUrl({ path, operation }),\n kind: \"bytes\",\n fallbackReason:\n \"This file was downloaded in full before playing, because the browser session that \" +\n \"lets it stream could not be established. Downloading can take longer, and seeking \" +\n \"is unavailable until it finishes.\",\n };\n },\n };\n}\n\n/** `GET /files/{id}/download?inline=1` — the platform files route (R3). */\nexport function fileDownloadPath(fileId: string): string {\n return `/files/${encodeURIComponent(fileId)}/download?inline=1`;\n}\n","/**\n * AFTER THE MEETING (Meet wave 3) — the pure pieces every client needs once the\n * room is gone: the attendance report, the transcript and notes as documents,\n * a moment in the transcript as a moment in the recording, search within one\n * transcript, and the two server calls behind the recap email and the saved\n * chat.\n *\n * Nothing here computes MEANING (same rule as `record.ts`): the summary,\n * decisions and action items are the wrap-up's durable rows; this file only\n * arranges, measures and formats them. Every function is framework-free so a\n * worker, a test and a browser produce byte-identical exports.\n */\n\nimport { toDelimited } from \"@ai-matrx/kit/delimited\";\nimport { formatDurationMs } from \"@ai-matrx/kit/format\";\nimport { readServerRefusal, refusalFromResponse } from \"./ai\";\nimport type { MeetApi } from \"./api\";\nimport { MeetError } from \"./errors\";\nimport { displayNameFor, type NameBook } from \"./names\";\nimport type { MeetingRecordBundle } from \"./record\";\nimport { chatFromRow } from \"./room-events\";\nimport type { RoomChatMessage } from \"./store\";\nimport { MEET_ROUTES, meetingPath, MEETING_PASS_HEADER } from \"./tokens\";\nimport type {\n MeetingAttendee,\n MeetingId,\n MeetingNote,\n MeetingRecord,\n RecordingRecord,\n TranscriptSegment,\n} from \"./types\";\n\n/* ─────────────────────────── Attendance ─────────────────────────── */\n\n/**\n * Who someone was, in the words an attendance report uses. `signed-in guest` is\n * an account from outside the meeting's organization that followed the link;\n * `guest` has no account at all.\n */\nexport type AttendanceKind = \"host\" | \"cohost\" | \"member\" | \"signed-in guest\" | \"guest\" | \"assistant\";\n\nexport interface AttendanceRow {\n readonly id: string;\n readonly identity: string;\n readonly name: string;\n readonly userId: string | null;\n readonly kind: AttendanceKind;\n /** First time they were in the room; null when they knocked and never came in. */\n readonly joinedAt: string | null;\n /** When they left; null while still in the room or when the leave was never recorded. */\n readonly leftAt: string | null;\n /**\n * Time in the room. A missing `leftAt` is measured to the meeting's end —\n * the room closed around them — and `null` only when neither is known.\n */\n readonly durationMs: number | null;\n /** True when `durationMs` was measured to the meeting's end, not a recorded leave. */\n readonly durationToEnd: boolean;\n readonly admission: MeetingAttendee[\"admissionState\"];\n /** How many times this person came in (a rejoin or a second device). */\n readonly joins: number;\n}\n\nfunction kindOf(person: MeetingAttendee): AttendanceKind {\n if (person.isAgent) return \"assistant\";\n if (person.role === \"host\") return \"host\";\n if (person.role === \"cohost\") return \"cohost\";\n // The durable row stores a link guest as `participant` with no account, so\n // \"no account\" is what makes someone a guest here — not the role word.\n if (person.userId === null) return \"guest\";\n return \"member\";\n}\n\nfunction ms(iso: string | null): number | null {\n if (iso === null) return null;\n const at = new Date(iso).getTime();\n return Number.isNaN(at) ? null : at;\n}\n\n/**\n * The attendance report of ONE meeting, host first then by arrival. People who\n * knocked and were never let in are kept (with no join time) — \"who asked to\n * come in\" is part of the record — but a denied knock has no duration.\n */\n/**\n * WHO a participant row is, as a PERSON (verifier, 2026-09-27: an export listed\n * the same attendee twice under \"attended\"). A signed-in person is their\n * account — every device and every rejoin is one person. A guest without an\n * account gets a fresh `guest:<random>` identity on every join, so the name\n * they typed is the only thread between their joins (Zoom's attendee report\n * merges link guests the same way); two different accountless guests who\n * typed the identical name read as one, which is the lesser error.\n */\nexport function attendeeKey(person: MeetingAttendee, names: NameBook = {}): string {\n if (person.userId !== null) return `user:${person.userId}`;\n if (person.isAgent) return `agent:${person.identity}`;\n const name = displayNameFor(names, person.identity, person.displayName).trim().toLowerCase();\n return name.length > 0 ? `guest-name:${name}` : `identity:${person.identity}`;\n}\n\n/**\n * One row per PERSON, in first-seen order: the row that actually joined (the\n * earliest) represents them. For a list of names or a head-count; the\n * attendance REPORT merges times as well (`attendanceReport`).\n */\nexport function uniqueAttendees(\n attendees: readonly MeetingAttendee[],\n names: NameBook = {},\n): readonly MeetingAttendee[] {\n const byKey = new Map<string, MeetingAttendee>();\n for (const person of attendees) {\n const key = attendeeKey(person, names);\n const kept = byKey.get(key);\n if (kept === undefined) {\n byKey.set(key, person);\n continue;\n }\n const keptJoin = ms(kept.joinedAt);\n const join = ms(person.joinedAt);\n if (join !== null && (keptJoin === null || join < keptJoin)) byKey.set(key, person);\n }\n return [...byKey.values()];\n}\n\nconst KIND_RANK: Record<AttendanceKind, number> = {\n host: 0,\n cohost: 1,\n member: 2,\n \"signed-in guest\": 3,\n guest: 4,\n assistant: 5,\n};\n\n/** Total time covered by a set of [start, end] intervals — overlaps count once. */\nfunction coveredMs(intervals: readonly (readonly [number, number])[]): number {\n const sorted = [...intervals].sort((a, b) => a[0] - b[0]);\n let total = 0;\n let from = -Infinity;\n let to = -Infinity;\n for (const [start, end] of sorted) {\n if (start > to) {\n if (to > from) total += to - from;\n from = start;\n to = end;\n } else if (end > to) {\n to = end;\n }\n }\n if (to > from) total += to - from;\n return total;\n}\n\nexport function attendanceReport(\n meeting: MeetingRecord,\n attendees: readonly MeetingAttendee[],\n names: NameBook = {},\n): readonly AttendanceRow[] {\n const end = ms(meeting.endedAt);\n // EVERY STAY COUNTS (verifier, 2026-09-29: a rejoin overwrote the first join,\n // so fifty minutes in the room read as ten). A row carries its stays; a row\n // written before stays were kept is one stay, first join to last leave.\n const measuredOf = new Map<AttendanceRow, (readonly [number, number])[]>();\n const perJoin = attendees.map((person): AttendanceRow => {\n const stays =\n person.sessions !== undefined && person.sessions.length > 0\n ? person.sessions.map((stay) => [ms(stay.joinedAt), ms(stay.leftAt)] as const)\n : [[ms(person.joinedAt), ms(person.leftAt)] as const];\n let toEnd = false;\n const measured: (readonly [number, number])[] = [];\n for (const [joined, left] of stays) {\n const until = left ?? end;\n if (joined === null || until === null || until < joined) continue;\n if (left === null) toEnd = true;\n measured.push([joined, until]);\n }\n const duration = measured.length === 0 ? null : coveredMs(measured);\n const row: AttendanceRow = {\n id: person.id,\n identity: person.identity,\n name: displayNameFor(names, person.identity, person.displayName),\n userId: person.userId,\n kind: kindOf(person),\n joinedAt: person.joinedAt,\n leftAt: person.leftAt,\n durationMs: duration,\n durationToEnd: duration !== null && toEnd,\n admission: person.admissionState,\n joins: Math.max(1, person.sessions?.length ?? 0),\n };\n measuredOf.set(row, measured);\n return row;\n });\n // ONE ROW PER PERSON: several joins (a rejoin, a second device) are one\n // attendee with their first arrival, last departure and total time — time\n // in the room twice at once counts once.\n const groups = new Map<string, { rows: AttendanceRow[]; people: MeetingAttendee[] }>();\n attendees.forEach((person, index) => {\n const key = attendeeKey(person, names);\n const group = groups.get(key) ?? { rows: [], people: [] };\n group.rows.push(perJoin[index]!);\n group.people.push(person);\n groups.set(key, group);\n });\n const rows = [...groups.values()].map(({ rows: joins }): AttendanceRow => {\n if (joins.length === 1) return joins[0]!;\n const came = joins.filter((row) => row.joinedAt !== null);\n const first =\n [...came].sort((a, b) => (ms(a.joinedAt) ?? 0) - (ms(b.joinedAt) ?? 0))[0] ?? joins[0]!;\n const kind = joins.reduce<AttendanceKind>(\n (best, row) => (KIND_RANK[row.kind] < KIND_RANK[best] ? row.kind : best),\n first.kind,\n );\n const open = came.some((row) => row.leftAt === null);\n const lastLeave = came.reduce<number | null>((latest, row) => {\n const left = ms(row.leftAt);\n return left !== null && (latest === null || left > latest) ? left : latest;\n }, null);\n const intervals = came.flatMap((row) => measuredOf.get(row) ?? []);\n return {\n ...first,\n kind,\n leftAt: open ? null : lastLeave === null ? null : new Date(lastLeave).toISOString(),\n durationMs: intervals.length === 0 ? null : coveredMs(intervals),\n durationToEnd: came.some((row) => row.durationToEnd),\n joins:\n came.length === 0 ? joins.length : came.reduce((total, row) => total + row.joins, 0),\n };\n });\n const rank = KIND_RANK;\n return rows.sort((a, b) => {\n if (rank[a.kind] !== rank[b.kind]) return rank[a.kind] - rank[b.kind];\n return (ms(a.joinedAt) ?? Number.MAX_SAFE_INTEGER) - (ms(b.joinedAt) ?? Number.MAX_SAFE_INTEGER);\n });\n}\n\n/** Headline numbers for the top of a report. The assistant is never a person. */\nexport function attendanceTotals(rows: readonly AttendanceRow[]): {\n readonly people: number;\n readonly guests: number;\n readonly neverAdmitted: number;\n readonly averageMs: number | null;\n} {\n const humans = rows.filter((row) => row.kind !== \"assistant\");\n const attended = humans.filter((row) => row.joinedAt !== null);\n const timed = attended.filter((row) => row.durationMs !== null);\n return {\n people: attended.length,\n guests: attended.filter((row) => row.kind === \"guest\" || row.kind === \"signed-in guest\").length,\n neverAdmitted: humans.filter((row) => row.joinedAt === null).length,\n averageMs:\n timed.length === 0\n ? null\n : Math.round(timed.reduce((sum, row) => sum + (row.durationMs ?? 0), 0) / timed.length),\n };\n}\n\nexport const ATTENDANCE_CSV_HEADER = [\n \"Meeting\",\n \"Meeting started\",\n \"Name\",\n \"Attended as\",\n \"Joined\",\n \"Left\",\n \"Minutes in the meeting\",\n \"Admission\",\n] as const;\n\n/**\n * CSV for a spreadsheet: one header row, one row per person, ISO timestamps (a\n * spreadsheet parses them; a formatted local clock loses the zone). Several\n * meetings — a series — concatenate with one header.\n */\nexport function attendanceCsv(\n reports: readonly { meeting: MeetingRecord; rows: readonly AttendanceRow[] }[],\n): string {\n // THE one writer (kit `toDelimited`): CRLF records, a trailing CRLF, names formula-guarded.\n return toDelimited(\n [\n ATTENDANCE_CSV_HEADER,\n ...reports.flatMap(({ meeting, rows }) =>\n rows.map((row) => [\n meeting.title,\n meeting.startedAt ?? meeting.scheduledFor,\n row.name,\n row.kind,\n row.joinedAt,\n row.leftAt,\n row.durationMs === null ? null : Math.round(row.durationMs / 6_000) / 10,\n row.admission,\n ]),\n ),\n ],\n { lineEnding: \"\\r\\n\", trailingNewline: true },\n );\n}\n\n/* ───────────────────── A moment in the recording ───────────────────── */\n\n/**\n * Where an instant of the meeting falls in the RECORDING, in milliseconds from\n * its start — or `null` when the recording does not cover it (a recording is\n * often a few minutes of a longer meeting). Never a clamp: seeking a line that\n * was said before recording began to 0:00 would play the wrong words.\n */\nexport function recordingOffsetMs(at: string, recording: RecordingRecord | null): number | null {\n if (recording === null || recording.state !== \"available\") return null;\n const start = ms(recording.startedAt);\n const moment = ms(at);\n if (start === null || moment === null || moment < start) return null;\n const end = ms(recording.endedAt);\n if (end !== null && moment > end) return null;\n return moment - start;\n}\n\n/** `m:ss` or `h:mm:ss` — a position in a recording or a meeting. */\nexport function clockPosition(offsetMs: number): string {\n return formatDurationMs(Math.max(0, offsetMs), { style: \"clock\" });\n}\n\n/* ───────────────────── Search within one transcript ───────────────────── */\n\nexport interface TranscriptMatch {\n readonly segmentId: string;\n /** [start, end) character ranges inside the segment's text, in order. */\n readonly ranges: readonly (readonly [number, number])[];\n}\n\n/**\n * Case-insensitive search of the transcript text AND the speaker's\n * name. Returns matches in transcript order. An empty query matches nothing.\n */\nexport function searchTranscript(\n segments: readonly TranscriptSegment[],\n query: string,\n names: NameBook = {},\n): readonly TranscriptMatch[] {\n const needle = fold(query.trim());\n if (needle.length === 0) return [];\n const out: TranscriptMatch[] = [];\n for (const segment of segments) {\n const hay = fold(segment.text);\n const ranges: [number, number][] = [];\n let from = 0;\n for (;;) {\n const at = hay.indexOf(needle, from);\n if (at < 0) break;\n ranges.push([at, at + needle.length]);\n from = at + needle.length;\n }\n const speaker = fold(displayNameFor(names, segment.identity, segment.speaker));\n if (ranges.length > 0 || speaker.includes(needle)) {\n out.push({ segmentId: segment.id, ranges });\n }\n }\n return out;\n}\n\n/** Lowercase, strip combining marks; length-preserving for the Latin range. */\nfunction fold(value: string): string {\n return value\n .normalize(\"NFD\")\n .replace(/[̀-ͯ]/g, (mark) => (mark.length === 1 ? \"\" : mark))\n .toLowerCase();\n}\n\n/* ───────────────────────── Documents ───────────────────────── */\n\nexport type TranscriptFormat = \"txt\" | \"md\" | \"vtt\";\n\ninterface ExportOptions {\n readonly names?: NameBook;\n /** Timestamps as a clock of day (default) or offsets from `zero`. */\n readonly locale?: string;\n readonly timeZone?: string;\n}\n\nfunction clockOfDay(iso: string, options: ExportOptions): string {\n const at = new Date(iso);\n if (Number.isNaN(at.getTime())) return \"\";\n return at.toLocaleTimeString(options.locale, {\n hour: \"numeric\",\n minute: \"2-digit\",\n second: \"2-digit\",\n ...(options.timeZone !== undefined ? { timeZone: options.timeZone } : {}),\n });\n}\n\nfunction vttStamp(offsetMs: number): string {\n const total = Math.max(0, Math.round(offsetMs));\n const h = Math.floor(total / 3_600_000);\n const m = Math.floor((total % 3_600_000) / 60_000);\n const s = Math.floor((total % 60_000) / 1000);\n const milli = total % 1000;\n return `${String(h).padStart(2, \"0\")}:${String(m).padStart(2, \"0\")}:${String(s).padStart(2, \"0\")}.${String(milli).padStart(3, \"0\")}`;\n}\n\n/**\n * The transcript as a document. `txt` and `md` group consecutive lines by one\n * speaker with the clock of day; `vtt` is WebVTT cued from the recording's\n * start when a recording exists (so it plays as captions over it), otherwise\n * from the meeting's start — and a cue ends where the next line begins.\n */\nexport function transcriptDocument(\n bundle: Pick<MeetingRecordBundle, \"meeting\" | \"transcript\" | \"recording\" | \"names\">,\n format: TranscriptFormat,\n options: ExportOptions = {},\n): string {\n const names = options.names ?? bundle.names;\n const lines = bundle.transcript.filter((line) => line.text.trim().length > 0);\n const speaker = (line: TranscriptSegment) => displayNameFor(names, line.identity, line.speaker);\n\n if (format === \"vtt\") {\n const zeroIso =\n bundle.recording?.state === \"available\" && bundle.recording.startedAt !== null\n ? bundle.recording.startedAt\n : (bundle.meeting.startedAt ?? lines[0]?.startedAt ?? null);\n const zero = ms(zeroIso) ?? 0;\n const cues = [\"WEBVTT\", \"\"];\n lines.forEach((line, index) => {\n const start = (ms(line.startedAt) ?? zero) - zero;\n if (start < 0) return;\n const next = lines[index + 1];\n const nextStart = next !== undefined ? (ms(next.startedAt) ?? zero) - zero : start + 4000;\n const end = Math.max(start + 500, Math.min(nextStart, start + 10_000));\n cues.push(`${vttStamp(start)} --> ${vttStamp(end)}`);\n cues.push(`<v ${speaker(line).replace(/[<>]/g, \"\")}>${line.text.replace(/-->/g, \"->\")}`);\n cues.push(\"\");\n });\n return cues.join(\"\\n\");\n }\n\n const out: string[] = [];\n const title = bundle.meeting.title;\n out.push(format === \"md\" ? `# ${title} — transcript` : `${title} — transcript`);\n out.push(\"\");\n let last: string | null = null;\n for (const line of lines) {\n if (line.identity !== last) {\n if (last !== null) out.push(\"\");\n const who = speaker(line);\n const clock = clockOfDay(line.startedAt, options);\n out.push(format === \"md\" ? `**${who}** · ${clock}` : `${who} (${clock})`);\n last = line.identity;\n }\n out.push(line.text.trim());\n }\n if (lines.length === 0) out.push(\"No transcript was captured for this meeting.\");\n return `${out.join(\"\\n\")}\\n`;\n}\n\nfunction bullet(note: MeetingNote): string {\n const owner = note.assigneeDisplayName !== null ? ` — ${note.assigneeDisplayName}` : \"\";\n return `- ${note.text.trim()}${owner}`;\n}\n\n/**\n * The meeting's notes as Markdown: when, who attended, the summary, decisions\n * and action items (with owners), and the link back. What a person pastes into\n * a doc or an email; also the source for PDF / Word export.\n */\nexport function notesDocument(\n bundle: Pick<\n MeetingRecordBundle,\n \"meeting\" | \"summary\" | \"decisions\" | \"actionItems\" | \"attendees\" | \"names\"\n >,\n options: { readonly link?: string | null; readonly when?: string | null } = {},\n): string {\n const out: string[] = [`# ${bundle.meeting.title}`, \"\"];\n if (options.when) out.push(`_${options.when}_`, \"\");\n // One name per PERSON — a rejoin is not a second attendee.\n const people = uniqueAttendees(\n bundle.attendees.filter((person) => !person.isAgent && person.joinedAt !== null),\n bundle.names,\n ).map((person) => displayNameFor(bundle.names, person.identity, person.displayName));\n if (people.length > 0) out.push(`**Attended:** ${people.join(\", \")}`, \"\");\n out.push(\"## Summary\", \"\", bundle.summary?.text.trim() || \"No summary was written.\", \"\");\n out.push(\"## Decisions\", \"\");\n out.push(...(bundle.decisions.length > 0 ? bundle.decisions.map(bullet) : [\"None recorded.\"]), \"\");\n out.push(\"## Action items\", \"\");\n out.push(\n ...(bundle.actionItems.length > 0 ? bundle.actionItems.map(bullet) : [\"None recorded.\"]),\n \"\",\n );\n if (options.link) out.push(`[Open the meeting record](${options.link})`, \"\");\n return out.join(\"\\n\");\n}\n\n/** How long the meeting ran, in words (\"42 minutes\"), or null when unknown. */\nexport function meetingLength(meeting: MeetingRecord): string | null {\n const start = ms(meeting.startedAt);\n const end = ms(meeting.endedAt);\n if (start === null || end === null || end < start) return null;\n return formatDurationMs(end - start, { style: \"long\" });\n}\n\n/* ───────────────────────── Saved chat ───────────────────────── */\n\nexport interface MeetingChatHistory {\n readonly messages: readonly RoomChatMessage[];\n /** The server's refusal, in its words, when the history could not be read. */\n readonly refusal: { readonly message: string; readonly remedy: string } | null;\n}\n\n/**\n * The saved chat of a finished meeting (`GET /v1/meet/chat`). Private messages\n * are never saved, by design, so they are never here. A refusal is returned as\n * data — the record still renders everything else.\n */\nexport async function loadMeetingChat(\n api: MeetApi,\n meetingId: MeetingId,\n meetingPass: string | null = null,\n): Promise<MeetingChatHistory> {\n const operation = \"meet.loadMeetingChat\";\n let response: Response;\n try {\n response = await api.request({\n path: `${MEET_ROUTES.chat}?meeting_id=${encodeURIComponent(meetingId)}`,\n method: \"GET\",\n operation,\n requireAuth: meetingPass === null,\n ...(meetingPass !== null ? { headers: { [MEETING_PASS_HEADER]: meetingPass } } : {}),\n });\n } catch (error) {\n return {\n messages: [],\n refusal: {\n message: `The chat could not be read (${(error as Error).message}).`,\n remedy: (error as { remedy?: string }).remedy ?? \"Reload the page.\",\n },\n };\n }\n if (!response.ok) {\n let body: unknown = null;\n try {\n body = await response.json();\n } catch {\n body = null;\n }\n const said = readServerRefusal(body);\n return {\n messages: [],\n refusal: {\n message: said?.reason ?? `The chat could not be read (the server answered ${response.status}).`,\n remedy: said?.remedy || \"Reload the page.\",\n },\n };\n }\n const payload = (await response.json()) as { messages?: unknown };\n const rows = Array.isArray(payload.messages) ? payload.messages : [];\n return {\n messages: rows\n .map((row) =>\n typeof row === \"object\" && row !== null ? chatFromRow(row as Record<string, unknown>) : null,\n )\n .filter((message): message is RoomChatMessage => message !== null),\n refusal: null,\n };\n}\n\n/* ───────────────────────── The recap email ───────────────────────── */\n\n/** One person the recap can go to, as the server resolved them. */\nexport interface FollowUpRecipient {\n /** Stable key for the choice: `user:<id>` or `email:<address>`. */\n readonly key: string;\n readonly name: string;\n /** The address, when this reader may see it (the host sees their guests'). */\n readonly email: string | null;\n readonly userId: string | null;\n /** `invitee`, `attendee` or both — where the server found them. */\n readonly sources: readonly (\"invitee\" | \"attendee\")[];\n /** False when the server has no way to reach them (no account, no address). */\n readonly reachable: boolean;\n}\n\n/** The draft the host reviews. The text is the wrap-up's own, arranged. */\nexport interface FollowUpDraft {\n readonly subject: string;\n readonly body: string;\n readonly recipients: readonly FollowUpRecipient[];\n /** False while the wrap-up has not been written — the draft says so. */\n readonly wrapUpWritten: boolean;\n /** When a recap for this meeting was last sent, and to how many. */\n readonly lastSent: { readonly at: string; readonly count: number } | null;\n}\n\nexport interface FollowUpSendResult {\n readonly dryRun: boolean;\n /** Recipients the send queued (or, on a dry run, would queue). */\n readonly queued: readonly string[];\n /** Recipient keys skipped, each with the reason. */\n readonly skipped: readonly { readonly key: string; readonly reason: string }[];\n}\n\nfunction refusalOf(response: Response, operation: string): Promise<MeetError> {\n return refusalFromResponse(response, operation, \"Nothing was sent. Try again.\");\n}\n\nfunction asRecipient(row: Record<string, unknown>): FollowUpRecipient {\n const sources = Array.isArray(row[\"sources\"]) ? (row[\"sources\"] as string[]) : [];\n return {\n key: String(row[\"key\"] ?? \"\"),\n name: String(row[\"name\"] ?? \"\"),\n email: typeof row[\"email\"] === \"string\" ? row[\"email\"] : null,\n userId: typeof row[\"user_id\"] === \"string\" ? row[\"user_id\"] : null,\n sources: sources.filter((s): s is \"invitee\" | \"attendee\" => s === \"invitee\" || s === \"attendee\"),\n reachable: row[\"reachable\"] !== false,\n };\n}\n\n/** The recap draft for a finished meeting. The host, a co-host or an editor. */\nexport async function loadFollowUpDraft(api: MeetApi, meetingId: MeetingId): Promise<FollowUpDraft> {\n const operation = \"meet.loadFollowUpDraft\";\n const response = await api.request({\n path: meetingPath(MEET_ROUTES.meetingFollowUp, meetingId),\n method: \"GET\",\n requireAuth: true,\n operation,\n });\n if (!response.ok) throw await refusalOf(response, operation);\n const body = (await response.json()) as Record<string, unknown>;\n const last = body[\"last_sent\"] as Record<string, unknown> | null | undefined;\n return {\n subject: String(body[\"subject\"] ?? \"\"),\n body: String(body[\"body\"] ?? \"\"),\n recipients: (Array.isArray(body[\"recipients\"]) ? body[\"recipients\"] : []).map((row) =>\n asRecipient(row as Record<string, unknown>),\n ),\n wrapUpWritten: body[\"wrap_up_written\"] === true,\n lastSent:\n last && typeof last[\"at\"] === \"string\"\n ? { at: last[\"at\"], count: typeof last[\"count\"] === \"number\" ? last[\"count\"] : 0 }\n : null,\n };\n}\n\n/**\n * Send the recap the host reviewed. `dryRun` resolves every recipient and\n * renders every email but queues nothing — how a test proves the path without\n * mailing a real inbox. Never called automatically.\n */\nexport async function sendFollowUp(\n api: MeetApi,\n meetingId: MeetingId,\n input: {\n readonly subject: string;\n readonly body: string;\n readonly recipientKeys: readonly string[];\n readonly dryRun?: boolean;\n },\n): Promise<FollowUpSendResult> {\n const operation = \"meet.sendFollowUp\";\n const response = await api.request({\n path: meetingPath(MEET_ROUTES.meetingFollowUp, meetingId),\n method: \"POST\",\n requireAuth: true,\n operation,\n body: {\n subject: input.subject,\n body: input.body,\n recipient_keys: [...input.recipientKeys],\n dry_run: input.dryRun === true,\n },\n });\n if (!response.ok) throw await refusalOf(response, operation);\n const body = (await response.json()) as Record<string, unknown>;\n return {\n dryRun: body[\"dry_run\"] === true,\n queued: Array.isArray(body[\"queued\"]) ? (body[\"queued\"] as string[]) : [],\n skipped: Array.isArray(body[\"skipped\"])\n ? (body[\"skipped\"] as Record<string, unknown>[]).map((row) => ({\n key: String(row[\"key\"] ?? \"\"),\n reason: String(row[\"reason\"] ?? \"\"),\n }))\n : [],\n };\n}\n","/**\n * MEET WAVE 1 — the two server calls a management screen makes (0.8).\n *\n * Every WRITE (schedule, edit, reschedule, cancel, archive, occurrence change,\n * invite, answer) goes straight to the database through `MeetRepository` — the\n * database decides who may. What a browser cannot do is send an email with a\n * calendar object in it, so after any of those writes a screen calls\n * `announceMeeting`: the server tells each invitee exactly what they have not\n * been told (invitation, update or cancellation, each with its `.ics`), and\n * re-queues reminders. It is idempotent — call it after every save.\n */\n\nimport type { MeetApi } from \"./api\";\nimport { refusalFromResponse } from \"./ai\";\nimport { MeetError } from \"./errors\";\nimport { MEET_ROUTES, meetingPath } from \"./tokens\";\nimport type { MeetingId } from \"./types\";\n\nexport interface AnnounceResult {\n readonly invitations: number;\n readonly updates: number;\n readonly cancellations: number;\n readonly alreadyCurrent: number;\n readonly remindersQueued: number;\n readonly remindersCancelled: number;\n}\n\nfunction refusal(response: Response, operation: string): Promise<MeetError> {\n return refusalFromResponse(\n response,\n operation,\n \"The meeting itself is saved — only the telling failed. Try again; the call is idempotent.\",\n );\n}\n\nfunction count(body: Record<string, unknown>, key: string): number {\n const value = body[key];\n return typeof value === \"number\" ? value : 0;\n}\n\n/** Bring every invitee's inbox and calendar up to date. The host, a co-host or an editor. */\nexport async function announceMeeting(api: MeetApi, meetingId: MeetingId): Promise<AnnounceResult> {\n const operation = \"meet.announceMeeting\";\n const response = await api.request({\n path: meetingPath(MEET_ROUTES.meetingAnnounce, meetingId),\n method: \"POST\",\n requireAuth: true,\n operation,\n });\n if (!response.ok) throw await refusal(response, operation);\n const body = (await response.json()) as Record<string, unknown>;\n return {\n invitations: count(body, \"invitations\"),\n updates: count(body, \"updates\"),\n cancellations: count(body, \"cancellations\"),\n alreadyCurrent: count(body, \"already_current\"),\n remindersQueued: count(body, \"reminders_queued\"),\n remindersCancelled: count(body, \"reminders_cancelled\"),\n };\n}\n\n/**\n * Put a one-off meeting on the caller's own connected Google Calendar. GATED by\n * the server to Google's review accounts while the Calendar scope is in review —\n * a refusal is a `forbidden` MeetError carrying the server's sentence. Everyone\n * already gets the meeting into any calendar through the invitation's `.ics`.\n */\nexport async function pushMeetingToGoogleCalendar(\n api: MeetApi,\n meetingId: MeetingId,\n target: { connectionId: string; calendarId?: string },\n): Promise<Record<string, unknown>> {\n const operation = \"meet.pushMeetingToGoogleCalendar\";\n const response = await api.request({\n path: meetingPath(MEET_ROUTES.meetingGoogleCalendar, meetingId),\n method: \"POST\",\n requireAuth: true,\n operation,\n body: { connection_id: target.connectionId, calendar_id: target.calendarId ?? \"primary\" },\n });\n if (!response.ok) throw await refusal(response, operation);\n return (await response.json()) as Record<string, unknown>;\n}\n","/**\n * MS — THE MEDIA SUPERVISOR (CORE-DESIGN §3.3, step S6c).\n *\n * Owns the media regions of §3.2 that are about THIS person's devices and speakers:\n *\n * - **Permission per kind.** The browser's error alone cannot tell a person's Block from an\n * operating-system refusal or a stale device id (`NotAllowedError` is all three in Chrome), so\n * every refusal is classified against the Permissions API: `denied` there is the person's (or\n * the site's) Block; a refusal while the browser says `granted` is the operating system. A kind\n * whose permission is `denied` reads `unavailable:denied` even when nothing tried to open it —\n * a camera the browser will not hand over is not \"off by choice\".\n * - **Live device switching.** LiveKit falls back to the default for a lost MICROPHONE by itself\n * but MUTES a lost camera (client-sdk-js #2035). On every device change the supervisor checks\n * the devices in use against the devices present: a vanished camera moves to the next camera;\n * a vanished microphone is reported with the device LiveKit moved to; nothing left of a kind is\n * `unavailable:none`. Each is a notice — never a silent switch. A NEW device is offered, never\n * taken (a person mid-sentence does not want their mic to jump to a headset being charged).\n * - **Autoplay.** `audio_output` = `blocked` while the browser holds remote audio until a gesture;\n * `enableAudio()` runs inside the person's click; the check is repeated when the tab comes back.\n *\n * 🚨 NOTHING HERE CHANGES THE CALL PHASE (§3.1 invariant). Every outcome is region state.\n * 🚨 NO BROWSER GLOBAL IS TOUCHED HERE: the browser half is the `MediaPlatform` port (§3.5) —\n * `webMediaPlatform()` below is the web adapter; a React Native host passes its own.\n */\n\nimport type {\n DevicePreview,\n DeviceUnavailableReason,\n LocalTrackState,\n MediaRegionState,\n MediaUnavailable,\n PermissionReading,\n} from \"./attendance\";\nimport { unavailableFrom } from \"./attendance\";\nimport { normalizeDeviceError, type MeetError } from \"./errors\";\nimport type { MeetRoomDriver } from \"./room-driver\";\nimport type { MeetStore } from \"./store\";\n\nexport type MediaKind = \"microphone\" | \"camera\";\nexport type BrowserFamily = \"chrome\" | \"edge\" | \"firefox\" | \"safari\" | \"other\";\nexport type OsFamily = \"mac\" | \"windows\" | \"ios\" | \"android\" | \"linux\" | \"other\";\n\nexport interface PlatformDevice {\n readonly deviceId: string;\n readonly kind: string;\n readonly label: string;\n}\n\n/** The slice of §3.5's `MeetPlatform` the media supervisor needs. */\nexport interface MediaPlatform {\n readonly browser: BrowserFamily;\n readonly os: OsFamily;\n /** The Permissions API reading, or `unknown` where the browser has none for this kind. */\n queryPermission(kind: MediaKind): Promise<PermissionReading>;\n /** Call `onChange` whenever the permission for `kind` changes. Returns the unsubscribe. */\n watchPermission(kind: MediaKind, onChange: (reading: PermissionReading) => void): () => void;\n listDevices(): Promise<readonly PlatformDevice[]>;\n /** The tab became visible again (autoplay is re-checked then). Returns the unsubscribe. */\n onVisible(callback: () => void): () => void;\n}\n\n/* ------------------------------------------------------------ classification */\n\n/**\n * The region reason for a refused device, from the error AND the permission store.\n * Pure — the rules are the tests.\n */\nexport function classifyDeviceFailure(\n error: { readonly code: string; readonly message: string },\n permission: PermissionReading,\n): DeviceUnavailableReason {\n if (permission === \"denied\") return \"denied\";\n switch (error.code) {\n case \"permission-denied\":\n // Chrome names an OS refusal (\"Permission denied by system\"); everywhere else a refusal\n // while the browser itself says `granted` can only be the operating system.\n if (/by system/i.test(error.message) || permission === \"granted\") return \"blocked_os\";\n return \"denied\";\n case \"device-not-found\":\n return \"none\";\n case \"device-in-use\":\n return \"busy\";\n case \"insecure-context\":\n return \"blocked_site\";\n default:\n return \"failed\";\n }\n}\n\n/** The contract notice code for an unusable device (HARNESS-CONTRACT §4). */\nexport function deviceNoticeCode(kind: MediaKind, reason: DeviceUnavailableReason): \"mic-blocked\" | \"camera-blocked\" | \"device-lost\" {\n if (reason === \"denied\" || reason === \"blocked_site\" || reason === \"blocked_os\") {\n return kind === \"microphone\" ? \"mic-blocked\" : \"camera-blocked\";\n }\n return \"device-lost\";\n}\n\n/**\n * The region reason this track is in, or null when the device is usable (on, or off by choice).\n * A kind the browser's permission store denies is `denied` even when nothing tried to open it.\n */\nexport function deviceUnavailableReason(track: LocalTrackState, permission: PermissionReading): DeviceUnavailableReason | null {\n if (track.enabled) return null;\n if (permission === \"denied\") return \"denied\";\n const refused = track.unavailable;\n if (refused === null) return null;\n return refused.reason ?? classifyDeviceFailure({ code: refused.code, message: refused.message }, permission);\n}\n\n/** The short chip text — the skin's words may differ; the reason is the fact. */\nexport function deviceChipLabel(kind: MediaKind, reason: DeviceUnavailableReason): string {\n const what = kind === \"microphone\" ? \"Mic\" : \"Camera\";\n switch (reason) {\n case \"denied\":\n case \"blocked_site\":\n case \"blocked_os\":\n return `${what} blocked`;\n case \"none\":\n return kind === \"microphone\" ? \"No microphone found\" : \"No camera found\";\n case \"busy\":\n return `${what} in use by another app`;\n case \"revoked\":\n return `${what} access was turned off`;\n default:\n return `${what} didn't start`;\n }\n}\n\n/**\n * THE REAL REMEDY, per browser and operating system: the steps a person actually follows. Short\n * lines (interface text is layout); the last step is always the Try again the screen offers.\n */\nexport function deviceFixSteps(\n kind: MediaKind,\n reason: DeviceUnavailableReason,\n where: { readonly browser: BrowserFamily; readonly os: OsFamily },\n): readonly string[] {\n const device = kind === \"microphone\" ? \"Microphone\" : \"Camera\";\n const lower = device.toLowerCase();\n switch (reason) {\n case \"denied\":\n case \"revoked\":\n case \"blocked_site\":\n switch (where.browser) {\n case \"firefox\":\n return [`Click the crossed-out ${lower} icon in the address bar`, \"Clear the Blocked setting\", \"Press Try again and choose Allow\"];\n case \"safari\":\n return where.os === \"ios\"\n ? [`Settings › Apps › Safari › ${device}`, \"Choose Allow\", \"Return here and press Try again\"]\n : [\"Safari › Settings for This Website\", `Set ${device} to Allow`, \"Press Try again\"];\n default:\n return [\"Click the site settings icon in the address bar\", `Set ${device} to Allow`, \"Press Try again\"];\n }\n case \"blocked_os\": {\n const app = where.browser === \"chrome\" ? \"Google Chrome\" : where.browser === \"edge\" ? \"Microsoft Edge\" : where.browser === \"firefox\" ? \"Firefox\" : \"your browser\";\n if (where.os === \"mac\") return [`System Settings › Privacy & Security › ${device}`, `Turn on ${app}`, \"Reopen the browser if asked, then Try again\"];\n if (where.os === \"windows\") return [`Settings › Privacy & security › ${device}`, `Turn on ${device} access for apps`, \"Press Try again\"];\n return [`Allow ${lower} access for ${app} in your system settings`, \"Press Try again\"];\n }\n case \"busy\":\n return [`Close other apps using the ${lower} (Zoom, Teams, another tab)`, \"Press Try again\"];\n case \"none\":\n return [`Connect a ${lower}`, \"Press Try again\"];\n default:\n return [`Pick another ${lower} in Settings`, \"Press Try again\"];\n }\n}\n\n/* ------------------------------------------------------------ the web adapter */\n\nexport function detectBrowser(userAgent: string): BrowserFamily {\n if (/Edg\\//.test(userAgent)) return \"edge\";\n if (/Firefox\\//.test(userAgent)) return \"firefox\";\n if (/Chrome\\/|Chromium\\/|CriOS\\//.test(userAgent)) return \"chrome\";\n if (/Safari\\//.test(userAgent)) return \"safari\";\n return \"other\";\n}\n\nexport function detectOs(userAgent: string, maxTouchPoints = 0): OsFamily {\n if (/iPhone|iPad|iPod/.test(userAgent)) return \"ios\";\n // iPadOS reports a Mac user agent; touch points give it away (catalog `mobile-ipad-detect`).\n if (/Macintosh/.test(userAgent)) return maxTouchPoints > 1 ? \"ios\" : \"mac\";\n if (/Android/.test(userAgent)) return \"android\";\n if (/Windows/.test(userAgent)) return \"windows\";\n if (/Linux|CrOS/.test(userAgent)) return \"linux\";\n return \"other\";\n}\n\ninterface PermissionStatusLike {\n state: string;\n onchange: (() => void) | null;\n}\n\n/** The browser adapter, or null outside a browser (SSR, a test, React Native without one). */\nexport function webMediaPlatform(): MediaPlatform | null {\n const scope = globalThis as unknown as {\n navigator?: {\n userAgent?: string;\n maxTouchPoints?: number;\n permissions?: { query(descriptor: { name: string }): Promise<PermissionStatusLike> };\n mediaDevices?: { enumerateDevices(): Promise<readonly PlatformDevice[]> };\n };\n document?: { visibilityState?: string; addEventListener(t: string, f: () => void): void; removeEventListener(t: string, f: () => void): void };\n };\n const nav = scope.navigator;\n if (nav === undefined) return null;\n const ua = nav.userAgent ?? \"\";\n const reading = (state: string | undefined): PermissionReading =>\n state === \"granted\" || state === \"denied\" || state === \"prompt\" ? state : \"unknown\";\n const query = async (kind: MediaKind): Promise<PermissionStatusLike | null> => {\n try {\n return (await nav.permissions?.query({ name: kind })) ?? null;\n } catch {\n // Firefox before 2024 and older Safari have no camera/microphone descriptor.\n return null;\n }\n };\n return {\n browser: detectBrowser(ua),\n os: detectOs(ua, nav.maxTouchPoints ?? 0),\n async queryPermission(kind) {\n return reading((await query(kind))?.state);\n },\n watchPermission(kind, onChange) {\n let status: PermissionStatusLike | null = null;\n let stopped = false;\n void query(kind).then((found) => {\n if (stopped || found === null) return;\n status = found;\n found.onchange = () => onChange(reading(found.state));\n });\n return () => {\n stopped = true;\n if (status !== null) status.onchange = null;\n };\n },\n async listDevices() {\n try {\n return (await nav.mediaDevices?.enumerateDevices()) ?? [];\n } catch {\n return [];\n }\n },\n onVisible(callback) {\n const doc = scope.document;\n if (doc === undefined) return () => undefined;\n const handler = (): void => {\n if (doc.visibilityState === \"visible\") callback();\n };\n doc.addEventListener(\"visibilitychange\", handler);\n return () => doc.removeEventListener(\"visibilitychange\", handler);\n },\n };\n}\n\n/* ------------------------------------------------------------ the supervisor */\n\nexport interface MediaSupervisorOptions {\n readonly store: MeetStore;\n readonly platform: MediaPlatform | null;\n /** The live room's driver, or null between rooms. */\n readonly driver: () => MeetRoomDriver | null;\n readonly report: (level: \"info\" | \"warn\" | \"error\", message: string, remedy?: string) => void;\n}\n\nexport interface MediaSupervisor {\n /** Begin watching permissions and visibility (idempotent). */\n start(): void;\n stop(): void;\n /** The `MediaUnavailable` for a refused device: the browser's words + the region reason. */\n unavailable(error: unknown, kind: MediaKind, operation: string): Promise<MediaUnavailable>;\n /** Driver handlers (wired by the engine). */\n onAudioPlayback(canPlay: boolean): void;\n onDevicesChanged(): void;\n onDeviceError(error: unknown, kind: \"audioinput\" | \"videoinput\" | null): void;\n /** The engine connected a room: remember which devices are in use now. */\n onConnected(): void;\n /** The room is gone: autoplay and device notices belong to the room. */\n onDisconnected(): void;\n /** \"Enable sound\" — call from inside the person's click. */\n enableAudio(): Promise<void>;\n /**\n * The pre-join preview's outcome for one kind: `prompting` when it asks the browser, `on` / `off`\n * when it settles or is turned off, or the error it was refused with (classified here, against\n * the permission store, exactly as an in-room refusal is).\n */\n preview(kind: MediaKind, outcome: \"prompting\" | \"on\" | \"off\" | { readonly error: unknown }): Promise<void>;\n /** Use the offered new device. */\n acceptOffer(): Promise<void>;\n dismissOffer(): void;\n dismissNotice(): void;\n /** Undo on a \"switched\" notice: go back to the device that went away, when it is available again. */\n undoNotice(): Promise<void>;\n}\n\nconst INPUT_KIND: Record<MediaKind, \"audioinput\" | \"videoinput\"> = { microphone: \"audioinput\", camera: \"videoinput\" };\n\n/** A browser's pseudo-devices: they follow the system choice and are never a \"new device\". */\nfunction isAlias(deviceId: string): boolean {\n return deviceId === \"default\" || deviceId === \"communications\" || deviceId === \"\";\n}\n\nexport function createMediaSupervisor(options: MediaSupervisorOptions): MediaSupervisor {\n const { store, platform } = options;\n let unsubscribers: (() => void)[] = [];\n let started = false;\n let noticeId = 0;\n /** The devices present at the last look, per input kind (ids → label). */\n let known: Record<\"audioinput\" | \"videoinput\", Map<string, string>> | null = null;\n /** The device in use per kind at the last look (to name what went away). */\n let inUse: Record<\"audioinput\" | \"videoinput\", string | null> = { audioinput: null, videoinput: null };\n\n function region(): MediaRegionState {\n return store.snapshot().mediaRegion;\n }\n function setRegion(next: Partial<MediaRegionState>): void {\n const current = region();\n const merged = { ...current, ...next };\n if (\n merged.audioOutput === current.audioOutput &&\n merged.notice === current.notice &&\n merged.offer === current.offer &&\n merged.preview === current.preview &&\n merged.permission.microphone === current.permission.microphone &&\n merged.permission.camera === current.permission.camera\n ) {\n return;\n }\n store.patch({ mediaRegion: merged });\n }\n\n function setPermission(kind: MediaKind, reading: PermissionReading): void {\n const current = region().permission;\n if (current[kind] === reading) return;\n setRegion({ permission: { ...current, [kind]: reading } });\n // Allowed again while the device sits refused: say what is true now (the person presses\n // Try again — LiveKit itself resumes a REVOKED track on re-grant).\n const track = store.snapshot().media[kind];\n if (reading === \"granted\" && !track.enabled && track.unavailable?.reason === \"denied\") {\n store.setLocalTrack(kind, {\n enabled: false,\n unavailable: { ...track.unavailable, reason: \"failed\", remedy: \"Access is allowed now. Press Try again.\" },\n });\n }\n }\n\n async function snapshotDevices(): Promise<Record<\"audioinput\" | \"videoinput\", Map<string, string>>> {\n const list = platform === null ? [] : await platform.listDevices();\n const out = { audioinput: new Map<string, string>(), videoinput: new Map<string, string>() };\n for (const device of list) {\n if (device.kind === \"audioinput\" || device.kind === \"videoinput\") out[device.kind].set(device.deviceId, device.label);\n }\n return out;\n }\n\n function readInUse(): void {\n const driver = options.driver();\n if (driver === null) return;\n const active = driver.activeDevices();\n inUse = { audioinput: active.audioinput, videoinput: active.videoinput };\n }\n\n async function handleDevicesChanged(): Promise<void> {\n if (platform === null) return;\n const before = known;\n const now = await snapshotDevices();\n known = now;\n const driver = options.driver();\n if (driver === null || before === null) {\n readInUse();\n return;\n }\n for (const kind of [\"microphone\", \"camera\"] as const) {\n const deviceKind = INPUT_KIND[kind];\n const previous = inUse[deviceKind];\n const track = store.snapshot().media[kind];\n // An alias (\"default\") is the device the system points it at: it is gone when it left the list or now names a different device.\n const aliasLabel = previous !== null && isAlias(previous) ? before[deviceKind].get(previous) : undefined;\n const aliasGone =\n previous !== null && aliasLabel !== undefined && aliasLabel !== \"\" && (!now[deviceKind].has(previous) || now[deviceKind].get(previous) !== aliasLabel);\n const gone = aliasGone || (previous !== null && !isAlias(previous) && before[deviceKind].has(previous) && !now[deviceKind].has(previous));\n if (gone) {\n const from = before[deviceKind].get(previous) ?? \"\";\n const remaining = [...now[deviceKind].entries()].filter(([id]) => !isAlias(id));\n if (remaining.length === 0) {\n // Nothing of this kind is left: the device is unusable, not \"off\".\n store.setLocalTrack(kind, {\n enabled: false,\n unavailable: {\n code: \"device-not-found\",\n message: `meet.${kind}: the ${kind} was disconnected and no other is connected`,\n remedy: `Connect a ${kind === \"microphone\" ? \"microphone\" : \"camera\"}, then press Try again.`,\n reason: \"none\",\n },\n });\n setRegion({ notice: { id: ++noticeId, kind, outcome: \"lost\", from, to: null } });\n continue;\n }\n if (kind === \"camera\") {\n // LiveKit mutes a lost camera (#2035): the supervisor moves it, keeping on/off as it was.\n const [nextId, nextLabel] = remaining[0]!;\n try {\n await driver.switchDevice(\"videoinput\", nextId);\n if (track.enabled || track.unavailable !== null) {\n await driver.setCameraEnabled(true);\n store.setLocalTrack(\"camera\", { enabled: true, unavailable: null });\n }\n setRegion({ notice: { id: ++noticeId, kind, outcome: \"switched\", from, fromId: previous!, to: nextLabel } });\n } catch (error) {\n const refused = await unavailable(error, \"camera\", \"meet.camera(fallback)\");\n store.setLocalTrack(\"camera\", { enabled: false, unavailable: refused });\n setRegion({ notice: { id: ++noticeId, kind, outcome: \"lost\", from, to: null } });\n }\n } else {\n // LiveKit restarts the microphone on the default device when it can; when the default went with it,\n // the supervisor moves it to what is left, keeping on/off as it was (device-unplugged-midcall).\n const active = driver.activeDevices().audioinput;\n let to = (active !== null && !aliasGone ? now.audioinput.get(active) : undefined) ?? now.audioinput.get(\"default\");\n if (to === undefined || aliasGone) {\n const [nextId, nextLabel] = remaining[0]!;\n try {\n await driver.switchDevice(\"audioinput\", nextId);\n if (track.enabled || track.unavailable !== null) {\n await driver.setMicrophoneEnabled(true);\n store.setLocalTrack(\"microphone\", { enabled: true, unavailable: null });\n }\n to = nextLabel;\n } catch (error) {\n const refused = await unavailable(error, \"microphone\", \"meet.microphone(fallback)\");\n store.setLocalTrack(\"microphone\", { enabled: false, unavailable: refused });\n setRegion({ notice: { id: ++noticeId, kind, outcome: \"lost\", from, to: null } });\n continue;\n }\n }\n setRegion({ notice: { id: ++noticeId, kind, outcome: \"switched\", from, fromId: previous!, to } });\n }\n continue;\n }\n // A NEW device appeared: offer it, never switch silently.\n const added = [...now[deviceKind].entries()].find(([id]) => !isAlias(id) && !before[deviceKind].has(id));\n if (added !== undefined && added[0] !== driver.activeDevices()[deviceKind]) {\n setRegion({ offer: { kind, deviceId: added[0], label: added[1] } });\n }\n }\n readInUse();\n }\n\n async function unavailable(error: unknown, kind: MediaKind, operation: string): Promise<MediaUnavailable> {\n const normalized = normalizeDeviceError(error, operation);\n const permission = platform === null ? \"unknown\" : await platform.queryPermission(kind);\n if (permission !== \"unknown\") setPermission(kind, permission);\n return unavailableFrom(normalized, classifyDeviceFailure(normalized, permission));\n }\n\n const api: MediaSupervisor = {\n start() {\n if (started || platform === null) return;\n started = true;\n for (const kind of [\"microphone\", \"camera\"] as const) {\n void platform.queryPermission(kind).then((reading) => setPermission(kind, reading));\n unsubscribers.push(platform.watchPermission(kind, (reading) => setPermission(kind, reading)));\n }\n unsubscribers.push(\n platform.onVisible(() => {\n // Safari re-suspends audio after a background stint (LiveKit #1751): try again on\n // return. If the browser still refuses, the Enable sound offer simply stays.\n const driver = options.driver();\n if (driver?.startAudio === undefined || region().audioOutput !== \"blocked\") return;\n driver.startAudio().catch(() => undefined);\n }),\n );\n },\n stop() {\n for (const off of unsubscribers) off();\n unsubscribers = [];\n started = false;\n },\n unavailable,\n async preview(kind, outcome) {\n // Reading the permission store before the room exists is what lets the pre-join say\n // \"blocked\" (not \"didn't start\") — start() is idempotent.\n api.start();\n let next: DevicePreview;\n if (typeof outcome === \"string\") next = { state: outcome, unavailable: null };\n else next = { state: \"unavailable\", unavailable: await unavailable(outcome.error, kind, `meet.${kind}Preview`) };\n const current = region().preview;\n const was = current[kind];\n if (was.state === next.state && was.unavailable?.reason === next.unavailable?.reason && was.unavailable?.message === next.unavailable?.message) return;\n setRegion({ preview: { ...region().preview, [kind]: next } });\n },\n onAudioPlayback(canPlay) {\n setRegion({ audioOutput: canPlay ? \"ok\" : \"blocked\" });\n },\n onDevicesChanged() {\n void handleDevicesChanged().catch((error: unknown) => {\n options.report(\"warn\", `meet: device change could not be handled: ${String(error)}`, \"Pick the device in Settings.\");\n });\n },\n onDeviceError(error, deviceKind) {\n if (deviceKind === null) return;\n const kind: MediaKind = deviceKind === \"audioinput\" ? \"microphone\" : \"camera\";\n void unavailable(error, kind, `meet.${kind}`).then((refused) => {\n const track = store.snapshot().media[kind];\n if (track.enabled) return; // the SDK recovered on its own\n store.setLocalTrack(kind, { enabled: false, unavailable: refused });\n });\n },\n onConnected() {\n void snapshotDevices().then((now) => {\n known = now;\n readInUse();\n });\n },\n onDisconnected() {\n known = null;\n inUse = { audioinput: null, videoinput: null };\n setRegion({ audioOutput: \"ok\", notice: null, offer: null });\n },\n async enableAudio() {\n const driver = options.driver();\n if (driver?.startAudio === undefined) return;\n try {\n await driver.startAudio();\n setRegion({ audioOutput: \"ok\" });\n } catch (error) {\n options.report(\"warn\", `meet: sound could not be enabled: ${String(error)}`, \"Click Enable sound again, or check the tab is not muted.\");\n }\n },\n async acceptOffer() {\n const offer = region().offer;\n const driver = options.driver();\n setRegion({ offer: null });\n if (offer === null || driver === null) return;\n try {\n await driver.switchDevice(INPUT_KIND[offer.kind], offer.deviceId);\n readInUse();\n } catch (error) {\n const refused = await unavailable(error, offer.kind, \"meet.switchDevice\");\n options.report(\"warn\", refused.message, refused.remedy);\n }\n },\n dismissOffer() {\n setRegion({ offer: null });\n },\n dismissNotice() {\n setRegion({ notice: null });\n },\n async undoNotice() {\n const notice = region().notice;\n setRegion({ notice: null });\n const driver = options.driver();\n if (notice === null || notice.outcome !== \"switched\" || notice.fromId === undefined || driver === null) return;\n const list = await snapshotDevices();\n if (!list[INPUT_KIND[notice.kind]].has(notice.fromId)) return;\n try {\n await driver.switchDevice(INPUT_KIND[notice.kind], notice.fromId);\n readInUse();\n } catch (error) {\n const refused = await unavailable(error, notice.kind, \"meet.undoDeviceSwitch\");\n store.setLocalTrack(notice.kind, { enabled: false, unavailable: refused });\n }\n },\n };\n return api;\n}\n\n","/**\n * THE ROOM ENGINE — the object a host mounts once and never reasons about.\n *\n * It owns every sequence a calling product gets wrong: ask for a token, connect,\n * project the roster, carry the in-room control plane, keep the phase honest\n * through a reconnect, and tear everything down exactly once. A consumer's job\n * is `engine.join(...)` and rendering a snapshot.\n *\n * THREE rules are structural here, not documented-and-hoped:\n *\n * 1. **The phase never lies.** `connecting` becomes `connected` only when the\n * driver says so, and any failure becomes `failed` WITH the remedy attached\n * to the snapshot. There is no path that leaves a spinner running forever —\n * the single most common complaint about every video product.\n * 2. **Role comes from the TOKEN, never from the client.** A participant's role\n * is read out of the server-minted metadata. A client that could decide it\n * was host could mute anyone; here the claim is unforgeable because the\n * server signed it.\n * 3. **Teardown is idempotent and total.** Leaving twice, or unmounting during\n * a connect, disconnects once and leaves no listener behind. The half-torn\n * room is what produces \"my camera light stays on after the call\".\n */\n\nimport {\n IDLE_TRACK,\n type LocalTrackState,\n} from \"./attendance\";\nimport { detectCapabilities } from \"./capabilities\";\nimport { createMediaSupervisor, type MediaPlatform, webMediaPlatform } from \"./media-supervisor\";\nimport {\n classifyScreenShareError,\n MeetError,\n normalizeDeviceError,\n normalizeMeetError,\n normalizeScreenShareError,\n SCREEN_SHARE_BLOCKED_REMEDY,\n} from \"./errors\";\n\n/** A system/policy block: the error's own remedy when it names one (policy), else the system one. */\nfunction SCREEN_SHARE_BLOCKED_REMEDY_FOR(remedy: string): string {\n return /administrator|policy/i.test(remedy) ? remedy : SCREEN_SHARE_BLOCKED_REMEDY;\n}\nimport { applyRecordingState, applyRoomEvent, isRoomEventTopic, ROOM_EVENTS } from \"./room-events\";\nimport { displayNameFor, personOf } from \"./names\";\nimport type { TabClaim } from \"./tab-claim\";\nimport { noteTakerIdentity } from \"./note-taker\";\nimport { captionSegment } from \"./transcript\";\nimport {\n DATA_TOPICS,\n type DriverParticipant,\n type MeetRoomDriver,\n type RoomDriverFactory,\n type RoomDriverHandlers,\n type VideoEffect,\n} from \"./room-driver\";\nimport { type MediaEffects, type MeetStore, NO_SHARE, UNKNOWN_LOCAL_NETWORK, type LocalNetwork } from \"./store\";\nimport {\n ALIVE_TOPIC,\n createLivenessTracker,\n createSelfAssessor,\n createVideoAutoOff,\n decodeBeat,\n encodeBeat,\n encodeBye,\n isBye,\n heartbeatIntervalMs,\n pathOf,\n worstQuality,\n} from \"./network-health\";\nimport { resolveExit, type ExitServerWord } from \"./phase\";\nimport {\n createConnectionSupervisor,\n createWebNetworkPort,\n DEFAULT_CONNECTION_POLICY,\n isUnannouncedDeparture,\n mergeGhosts,\n type ConnectionPolicy,\n type Ghost,\n type NetworkPort,\n type ResumeStorage,\n} from \"./connection-supervisor\";\nimport type { RoomToken, SecondDeviceAnswer, TokenClient } from \"./tokens\";\nimport {\n asParticipantIdentity,\n isHostRole,\n type CallMode,\n type ConnectionQuality,\n type MeetIdentity,\n type MeetingRecord,\n type MeetParticipant,\n type ParticipantIdentity,\n type ParticipantRole,\n type ParticipantStanding,\n type RoomName,\n type StageLayout,\n asUserId,\n type UserId,\n} from \"./types\";\n\nexport interface JoinRequest {\n readonly roomName: RoomName;\n readonly meetingId?: string | undefined;\n readonly slug?: string | undefined;\n /** `audio` joins with the camera off. Same room, same code path (D3). */\n readonly mode: CallMode;\n /**\n * Whether to ATTEMPT the microphone on join. Defaults to true. `false` is a\n * deliberate listen-only join — the pre-join screen's \"Join with microphone\"\n * toggle. Either way the join itself never depends on the outcome; a refused\n * device lands in `snapshot.media.microphone.unavailable`, not in a throw.\n */\n readonly microphone?: boolean | undefined;\n /** Meet wave 5: a breakout room of this meeting; absent/null = the main room. */\n readonly breakoutRoomId?: string | null | undefined;\n /** HR-360 wave 2 (MD-16): ask to join as a silent observer. The server decides. */\n readonly observe?: boolean | undefined;\n /**\n * S7: the person's answer when they are already in this meeting on another tab or device\n * (`snapshot.secondDevice`): `switch_here` moves the call here, `join_too` adds this tab as a\n * companion (no microphone, speaker off).\n */\n readonly secondDevice?: SecondDeviceAnswer | undefined;\n}\n\nexport interface RoomEngineOptions {\n store: MeetStore;\n tokens: TokenClient;\n identity: MeetIdentity;\n driverFactory: RoomDriverFactory;\n /** S7: this page's tab claim (`core/tab-claim.ts`); absent = one connection per person. */\n tabClaim?: TabClaim | undefined;\n /** Canonical public metadata read; room packets alone never authorize teardown. */\n resolveMeeting?: ((slug: string) => Promise<MeetingRecord>) | undefined;\n onDiagnostic?: ((event: { level: \"info\" | \"warn\" | \"error\"; message: string; remedy?: string }) => void) | undefined;\n /** Fired when the local participant leaves for any reason, including kicked. */\n onLeft?: ((reason: string | null) => void) | undefined;\n now?: (() => number) | undefined;\n /** Remembered noise suppression / background choice (Meet wave 2). */\n effectsStorage?: EffectsStorage | undefined;\n /**\n * The device the person PICKED for each kind (the pre-join pickers, the\n * settings panel). Applied before any track is created — through 0.7.x the\n * pickers stored a choice nothing ever read, so every join used the browser\n * default whatever the screen said.\n */\n resolveDevice?: ((kind: \"audioinput\" | \"videoinput\" | \"audiooutput\") => string | null) | undefined;\n /**\n * §3.4 EXIT-REASON RESOLUTION: on every LiveKit disconnect the engine asks the server's state\n * read what happened to THIS connection (`GET /v1/meet/state?connection=`). Resolves null when\n * the read failed (offline / unreachable), undefined when this room has no state read (a call).\n */\n readExit?: ((connection: string, pass?: string | null) => Promise<ExitServerWord | null | undefined>) | undefined;\n /** Was the page frozen when the connection dropped (Page Lifecycle)? → `inactive`. */\n wasFrozen?: (() => boolean) | undefined;\n /**\n * S6b — the connection supervisor's ports. `network` = does the device have a network\n * (default: `navigator.onLine`); `probe` = do our API and the media server answer (default:\n * assume yes); `resume` = per-tab storage for the reload-after-offline marker (default: none).\n */\n /**\n * S6c — the media supervisor's platform port (permissions, device list, visibility). Default:\n * the browser's (`webMediaPlatform()`); `null` turns the supervisor's browser reads off.\n */\n mediaPlatform?: MediaPlatform | null | undefined;\n connection?: {\n readonly network?: NetworkPort | undefined;\n readonly probe?: ((serverUrl: string | null) => Promise<boolean>) | undefined;\n readonly resume?: ResumeStorage | undefined;\n } | undefined;\n}\n\nexport interface RoomEngine {\n join(request: JoinRequest): Promise<void>;\n /**\n * 🚨 THE HOST SAID YES — COME IN.\n *\n * A lobby token is minted with `canPublish: false` and `canSubscribe: false`\n * (that is what makes the lobby real rather than a screen over a live\n * microphone), so admission is not a UI state change: the cached token has to\n * be thrown away and a NEW one minted with the grants the server now gives.\n * Without this the waiting room's own sentence — \"you will join automatically\n * once you are admitted\" — is a lie, and the guest waits forever.\n */\n rejoin(): Promise<void>;\n /**\n * S6b: rejoin BY ITSELF from `disconnected` (the connection supervisor calls this once the\n * network and our servers answer). A fresh mint into the same identity: the server restores\n * role, hand and admission from the person row.\n */\n autoRejoin(): Promise<void>;\n /**\n * S6b: this tab was in (or dropped from) the call before the page reloaded — Chrome's offline\n * page reloading, a refresh. Returns true when it went straight back to joining (from\n * `resolving`), false when there is nothing fresh to resume (the caller resolves as usual).\n */\n resume(roomName: RoomName): boolean;\n /**\n * Meet wave 5: go to a breakout room (`null` = back to the main room). The server\n * mints the pass (`/token` with `breakout_room_id`) and refuses a room the plan does\n * not allow; the move is a reconnect that never reads as leaving the meeting.\n */\n moveToBreakout(roomId: string | null): Promise<void>;\n leave(): Promise<void>;\n /** The person's own \"back to the device check\" press from `left` / `disconnected`. */\n backToPrejoin(): void;\n /**\n * S7: BEFORE Join — is this person already live in `roomName` on another tab of THIS browser\n * (`TabClaim.presentElsewhere`)? When yes the pre-join offers Switch here / Join here too up\n * front (the same choice the server would otherwise ask after the press). Returns whether it did.\n * A guest has no stable person key before the pass, so the server's own answer covers them.\n */\n checkElsewhere(roomName: RoomName): Promise<boolean>;\n /** Clear a refused-share notice (the `share` region) the person dismissed. */\n dismissShareNotice(): void;\n setMicrophoneEnabled(enabled: boolean): Promise<void>;\n setCameraEnabled(enabled: boolean): Promise<void>;\n setScreenShareEnabled(enabled: boolean): Promise<void>;\n react(emoji: string): Promise<void>;\n /**\n * A PRIVATE message to one person (Meet wave 2). Data plane only, addressed\n * to that identity, NEVER saved — see `core/chat.ts` for the saved,\n * everyone-chat, which goes through the server.\n */\n sendPrivateChat(to: ParticipantIdentity, text: string): Promise<void>;\n /**\n * Change this participant's own media processing — noise suppression and the\n * camera background — on the live tracks, and remember it in this browser.\n * A refused effect is a diagnostic and a revert, never a lost camera.\n */\n setEffects(next: Partial<MediaEffects>): Promise<void>;\n /** Whether this browser can run a background effect at all (absent, never dead). */\n supportsVideoEffects(): boolean;\n /** Whether this browser can share a screen at all — the control is ABSENT where it cannot. */\n supportsScreenShare(): boolean;\n /** S6c: \"Enable sound\" — resume remote audio the autoplay policy holds. Call inside the click. */\n enableAudio(): Promise<void>;\n /** S6c: switch to the device the supervisor offered (a new one was plugged in). */\n /** The pre-join preview's outcome for one kind (the media supervisor classifies a refusal). */\n previewDevice(kind: \"microphone\" | \"camera\", outcome: \"prompting\" | \"on\" | \"off\" | { readonly error: unknown }): Promise<void>;\n acceptDeviceOffer(): Promise<void>;\n dismissDeviceOffer(): void;\n dismissDeviceNotice(): void;\n undoDeviceNotice(): Promise<void>;\n /** Choose the speaker (audio output) for this meeting. */\n switchDevice(kind: \"audioinput\" | \"videoinput\" | \"audiooutput\", deviceId: string): Promise<void>;\n pin(identity: ParticipantIdentity | null): void;\n setLayout(layout: StageLayout): void;\n attach(\n identity: ParticipantIdentity,\n kind: \"camera\" | \"microphone\" | \"screen\",\n element: HTMLMediaElement,\n ): () => void;\n token(): RoomToken | null;\n dispose(): Promise<void>;\n}\n\n/** Where this browser keeps a participant's media choices. */\nexport interface EffectsStorage {\n load(): MediaEffects;\n save(effects: MediaEffects): void;\n}\n\n/** What the server puts in the token's participant metadata. */\ninterface ParticipantMetadata {\n readonly user_id?: string;\n readonly role?: string;\n readonly can_record?: boolean;\n readonly is_agent?: boolean;\n readonly avatar_url?: string;\n}\n\n/**\n * 🚨 THE SERVER'S WORD, ON TWO CARRIERS (MRI-A11, feedback `7418ef78`).\n *\n * aidream mints the role into the token's participant METADATA (a JSON blob)\n * AND as a LiveKit participant ATTRIBUTE. Both come out of one decision in\n * `services/meet/service.py`; neither can be written by a client. The attribute\n * is preferred because LiveKit delivers the attribute map on every roster\n * update, while a roster event can carry an empty metadata string — which is\n * precisely how, through 2026-09-08, EVERY participant projected as\n * \"participant\" and no host ever saw a host control in a browser.\n *\n * These strings are pinned on the server side too: `test_meet_service.py` reads\n * them out of THIS file and fails if the two sides drift.\n */\nexport const PARTICIPANT_ROLE_ATTRIBUTE = \"matrx.participant.role\";\n/** S8: the server's raise time of a hand (epoch ms), \"\" when down. Pinned with aidream's `room_state.ATTRIBUTE_HAND`. */\nexport const PARTICIPANT_HAND_ATTRIBUTE = \"matrx.hand\";\n\nfunction handFrom(value: string | undefined): { handRaised: boolean; handRaisedAt: number | null } {\n const at = value === undefined || value.length === 0 ? Number.NaN : Number(value);\n return Number.isFinite(at) ? { handRaised: true, handRaisedAt: at } : { handRaised: false, handRaisedAt: null };\n}\nexport const PARTICIPANT_CAN_RECORD_ATTRIBUTE = \"matrx.participant.can_record\";\n/** S7: `main` or `companion` (a \"join here too\" connection) — `config.py::ATTRIBUTE_CONNECTION_KIND`. */\nexport const CONNECTION_KIND_ATTRIBUTE = \"matrx.participant.connection_kind\";\n/** Standing travels beside the role (CORE-DESIGN §2.1). Pinned by aidream's tests too. */\nexport const PARTICIPANT_STANDING_ATTRIBUTE = \"matrx.participant.standing\";\n\nconst KNOWN_STANDINGS: readonly ParticipantStanding[] = [\"member\", \"invited\", \"outsider\", \"guest\"];\n\nfunction parseStanding(value: unknown): ParticipantStanding | null {\n return KNOWN_STANDINGS.includes(value as ParticipantStanding) ? (value as ParticipantStanding) : null;\n}\n\nconst KNOWN_ROLES: readonly ParticipantRole[] = [\"host\", \"cohost\", \"presenter\", \"participant\", \"viewer\"];\n\n/**\n * An unrecognized role is `participant` — never `host`. A word this package\n * does not know is a server that is ahead of it, and the safe reading of an\n * unknown claim is the one that grants nothing.\n */\nfunction parseRole(value: string | undefined): ParticipantRole {\n return KNOWN_ROLES.includes(value as ParticipantRole) ? (value as ParticipantRole) : \"participant\";\n}\n\n/**\n * Metadata is SERVER-MINTED and therefore trusted; it is still parsed\n * defensively, because a malformed blob must degrade a tile to \"participant\"\n * rather than throw inside a roster event and freeze the whole grid.\n */\nexport function projectParticipant(\n driver: DriverParticipant,\n joinedAt: string,\n): MeetParticipant {\n let metadata: ParticipantMetadata = {};\n if (driver.metadata !== null && driver.metadata.length > 0) {\n try {\n const parsed: unknown = JSON.parse(driver.metadata);\n if (typeof parsed === \"object\" && parsed !== null) {\n metadata = parsed as ParticipantMetadata;\n }\n } catch {\n metadata = {};\n }\n }\n const userId = typeof metadata.user_id === \"string\" ? asUserId(metadata.user_id) : null;\n return {\n // S7: a tile is a PERSON; LiveKit names the tab's connection (`<person>~<tab>`).\n identity: personOf(driver.identity),\n userId,\n // 🚨 A RAW LIVEKIT IDENTITY IS NEVER A NAME (MRI-A18). Through 0.4.12 this\n // line read `driver.name.length > 0 ? driver.name : driver.identity`, and\n // that single fallback put `guest:v-spk-1` on the participant strip, the\n // People panel, the meeting header and every Pin/Mute aria-label, in BOTH\n // lanes — inside the very structure `core/names.ts` is taught from. A\n // nameless tile is still a bug report nobody can action, so the fallback\n // stays; it is now the name book's HONEST placeholder (\"Unnamed guest\", or\n // the agent's product name) instead of the token. `store.setParticipants`\n // then upgrades this to the best name anything has ever learned.\n displayName: displayNameFor({}, driver.identity, driver.name),\n avatarUrl: typeof metadata.avatar_url === \"string\" ? metadata.avatar_url : null,\n // ATTRIBUTE FIRST, metadata second — see the constants above. Both are the\n // server's; the attribute is simply the one that always arrives.\n role: parseRole(driver.attributes[PARTICIPANT_ROLE_ATTRIBUTE] ?? metadata.role),\n standing: parseStanding(\n driver.attributes[PARTICIPANT_STANDING_ATTRIBUTE] ??\n (metadata as { standing?: unknown }).standing,\n ),\n canRecord:\n driver.attributes[PARTICIPANT_CAN_RECORD_ATTRIBUTE] === \"true\" ||\n metadata.can_record === true,\n isLocal: driver.isLocal,\n isAgent: metadata.is_agent === true,\n attributes: driver.attributes,\n micEnabled: driver.micEnabled,\n cameraEnabled: driver.cameraEnabled,\n screenSharing: driver.screenSharing,\n speaking: false,\n // S8: a hand is the SERVER's (`hand_raised_at` on the person row, `/moderate raise_hand`),\n // replicated as the `matrx.hand` attribute: epoch ms of the raise, \"\" when down. It is\n // therefore delivered to late joiners and survives refresh and reconnect.\n ...handFrom(driver.attributes[PARTICIPANT_HAND_ATTRIBUTE]),\n quality: \"unknown\",\n joinedAt,\n };\n}\n\ninterface DataEnvelope {\n readonly id?: string;\n readonly at?: string;\n /** Epoch ms a hand went up — the queue's order. */\n readonly raisedAt?: number;\n /** A private chat message's addressee. */\n readonly to?: string;\n readonly raised?: boolean;\n readonly emoji?: string;\n readonly text?: string;\n readonly name?: string;\n}\n\nexport function createRoomEngine(options: RoomEngineOptions): RoomEngine {\n const { store, tokens, identity } = options;\n const now = options.now ?? (() => Date.now());\n let driver: MeetRoomDriver | null = null;\n let current: RoomToken | null = null;\n let joining: Promise<void> | null = null;\n // The request this engine last joined with, so an admitted guest can be let\n // in without the surface having to remember how it got here.\n let lastRequest: JoinRequest | null = null;\n let disposed = false;\n // True while a breakout move tears down one room to connect the next: that\n // disconnect is not the person leaving the meeting.\n let moving = false;\n const confirmingEnds = new WeakSet<MeetRoomDriver>();\n /** The media server of the last mint — what the probe asks after the room is gone. */\n let lastServerUrl: string | null = null;\n /** The meeting's connection knobs, from the last mint (S6b). */\n let connectionPolicy: ConnectionPolicy = DEFAULT_CONNECTION_POLICY;\n /** Remote people who dropped without leaving, held as `reconnecting` tiles (S6b). */\n let ghosts = new Map<string, Ghost>();\n let ghostTimer: ReturnType<typeof setTimeout> | null = null;\n /** The last LIVE roster the driver reported, so an expiring ghost can re-project it. */\n let liveRoster: readonly DriverParticipant[] = [];\n /** S7: person key → every LiveKit connection they hold, and the one their tile shows. */\n let personConnections = new Map<string, string[]>();\n let mainConnection = new Map<string, string>();\n function connectionsOf(who: string): string[] {\n return personConnections.get(personOf(who)) ?? [who];\n }\n /** Departures LiveKit announced since the last roster: identity → reason name. */\n const departures = new Map<string, string | null>();\n\n /* P1 connection-network (`network-health.ts`): this tab's own path, and everyone's heartbeat. */\n const liveness = createLivenessTracker(now);\n const assessor = createSelfAssessor();\n const autoOff = createVideoAutoOff(() => connectionPolicy.videoAutoOffAfterPoorSeconds * 1000, now);\n /** LiveKit's own quality per PERSON — combined with the self-reported reading, never replaced. */\n const lkQuality = new Map<string, ConnectionQuality>();\n /** Connections that beat before and went silent: shown `reconnecting` to everyone here. */\n let silent = new Set<string>();\n /** Connections that announced a deliberate Leave on the heartbeat. */\n const saidBye = new Set<string>();\n let healthTimer: ReturnType<typeof setInterval> | null = null;\n let lastBeatAt = 0;\n let wasUnstable = false;\n\n function patchLocalNetwork(next: Partial<LocalNetwork>): void {\n const before = store.snapshot().localNetwork;\n const merged = { ...before, ...next };\n if (merged.path !== before.path || merged.quality !== before.quality || merged.videoPaused !== before.videoPaused) {\n store.patch({ localNetwork: merged });\n }\n }\n\n /** What a person's tile says about their connection: the worse of LiveKit's and their own word. */\n function effectiveQuality(person: string, connection: string | undefined, isLocal: boolean): ConnectionQuality {\n const fromServer = lkQuality.get(person) ?? \"unknown\";\n if (isLocal) return worstQuality(fromServer, assessor.current());\n return worstQuality(fromServer, connection === undefined ? null : liveness.reported(connection)?.q);\n }\n\n function refreshQualities(): void {\n const snap = store.snapshot();\n for (const p of snap.participants) {\n if (p.reconnecting === true) continue;\n const next = effectiveQuality(p.identity, mainConnection.get(p.identity), p.isLocal);\n if (next !== \"unknown\" && next !== p.quality) store.setQuality(p.identity, next);\n }\n }\n\n function stopHealth(): void {\n if (healthTimer !== null) clearInterval(healthTimer);\n healthTimer = null;\n liveness.clear();\n assessor.reset();\n autoOff.reset();\n silent = new Set();\n saidBye.clear();\n lkQuality.clear();\n lastBeatAt = 0;\n wasUnstable = false;\n if (store.snapshot().localNetwork !== UNKNOWN_LOCAL_NETWORK) store.patch({ localNetwork: UNKNOWN_LOCAL_NETWORK });\n }\n\n /** One tick a second while in the call: beat, and judge who went silent. */\n function healthTick(): void {\n const snap = store.snapshot();\n const target = driver;\n if (disposed || target === null || snap.phase !== \"in_call\") return;\n if (snap.connection !== \"stable\") {\n // THIS tab is the one cut off: it hears nobody, which says nothing about them.\n wasUnstable = true;\n return;\n }\n if (wasUnstable) {\n wasUnstable = false;\n liveness.restartClock();\n }\n const size = liveRoster.length;\n const at = now();\n if (at - lastBeatAt >= heartbeatIntervalMs(size)) {\n lastBeatAt = at;\n const beat = encodeBeat({ q: assessor.current(), vp: snap.localNetwork.videoPaused });\n void target.publishData(ALIVE_TOPIC, beat).catch(() => undefined);\n }\n const remotes = liveRoster.filter((p) => !p.isLocal).map((p) => p.identity);\n const next = liveness.stale(remotes, size);\n if (next.size !== silent.size || [...next].some((who) => !silent.has(who))) {\n silent = next;\n projectRoster(liveRoster, new Date(at).toISOString());\n }\n }\n\n function startHealth(): void {\n if (healthTimer !== null) return;\n healthTimer = setInterval(healthTick, 1000);\n }\n\n /** The request to come back with: the last join, with the camera/mic as they are NOW. */\n function requestAsNow(): JoinRequest | null {\n if (lastRequest === null) return null;\n const media = store.snapshot().media;\n if (store.snapshot().phase !== \"in_call\") return lastRequest;\n return { ...lastRequest, mode: media.camera.enabled ? \"video\" : \"audio\", microphone: media.microphone.enabled };\n }\n\n /** Drop the room this engine holds (no phase change — the caller says where the person went). */\n async function dropRoom(): Promise<void> {\n const target = driver;\n driver = null;\n if (current !== null) {\n tokens.invalidate(current.roomName);\n current = null;\n }\n store.setLocalTrack(\"microphone\", IDLE_TRACK);\n store.setLocalTrack(\"camera\", IDLE_TRACK);\n media.onDisconnected();\n store.patch({ share: NO_SHARE, connection: \"stable\" });\n clearGhosts();\n stopHealth();\n await target?.disconnect();\n }\n\n /** MY Leave: the room is dropped and the phase is `left` (§3.1). */\n async function leaveRoom(): Promise<void> {\n // A deliberate Leave says so on the heartbeat first, so nobody holds a `reconnecting` tile for\n // someone who meant to go (`net-others-see-drop`). Bounded: a dead path never delays the Leave.\n const target = driver;\n if (target !== null && store.snapshot().phase === \"in_call\") {\n await Promise.race([\n target.publishData(ALIVE_TOPIC, encodeBye()).catch(() => undefined),\n new Promise<void>((resolve) => setTimeout(resolve, 300)),\n ]);\n }\n store.dispatch({ type: \"leave\" });\n await dropRoom();\n options.onLeft?.(null);\n }\n\n async function confirmMeetingEnd(): Promise<void> {\n const meeting = store.snapshot().meeting;\n const target = driver;\n if (meeting === null || target === null || confirmingEnds.has(target) || options.resolveMeeting === undefined) return;\n confirmingEnds.add(target);\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n const verified = await Promise.race([\n options.resolveMeeting(meeting.slug),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(\"Meeting end verification timed out\")), 5000);\n }),\n ]);\n if (disposed || driver !== target || store.snapshot().meeting?.id !== meeting.id) return;\n if (verified.id !== meeting.id || verified.roomName !== meeting.roomName || verified.endedAt === null) {\n report(\"warn\", \"Meet ignored an unconfirmed meeting-end notice\", \"The canonical meeting is still live or does not match this room.\");\n return;\n }\n // Keep newer roster/metadata updates; only the durable ending is needed.\n const latest = store.snapshot().meeting;\n if (latest === null) return;\n store.patch({ meeting: { ...latest, endedAt: verified.endedAt } });\n // time-limit-warning: the screen says WHY the meeting ended (the server recorded the trigger on this connection).\n const connection = current?.connectionIdentity ?? current?.identity ?? store.snapshot().localIdentity ?? null;\n let why: ExitServerWord | null | undefined;\n try {\n why = options.readExit !== undefined && connection !== null ? await options.readExit(connection, current?.meetingPass ?? null) : undefined;\n } catch {\n why = null;\n }\n store.dispatch({ type: \"ended\" }, { reason: why?.exitReason === \"time_limit\" ? \"time_limit\" : \"ended\" });\n await dropRoom();\n options.onLeft?.(\"ended\");\n } catch (error) {\n report(\"error\", `Meet could not confirm or finish meeting teardown: ${String(error)}`, \"Check the meeting link again; if media remains active, close this meeting tab.\");\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n confirmingEnds.delete(target);\n }\n }\n\n function report(level: \"info\" | \"warn\" | \"error\", message: string, remedy?: string): void {\n options.onDiagnostic?.(remedy === undefined ? { level, message } : { level, message, remedy });\n }\n\n /** Consecutive screen-share refusals no browser can tell from a closed picker. */\n let ambiguousShareDenials = 0;\n\n /**\n * THE JOIN FAILED. Only a join can fail the join: the machine accepts `join_failed` from\n * `joining` alone, so a caller anywhere else (an in-call action) cannot move the phase with it\n * — the failure is still recorded and reported, and the call goes on. A gate reason renders\n * `refused:<reason>` (`ended` → `ended`); no reason renders `disconnected` with Rejoin — never\n * pre-join (the old `failed` → pre-join fallback is gone).\n */\n function failJoin(error: unknown, operation: string): MeetError {\n const normalized = normalizeMeetError(error, operation);\n store.patch({\n failure: {\n message: normalized.message,\n remedy: normalized.remedy,\n reason: normalized.reason,\n retryAt: normalized.retryAfterSeconds === null ? null : now() + normalized.retryAfterSeconds * 1000,\n stage: \"join\",\n },\n });\n store.dispatch({ type: \"join_failed\", reason: normalized.reason ?? null });\n report(\n normalized.code === \"session-unavailable\" ? \"warn\" : \"error\",\n normalized.message,\n normalized.remedy,\n );\n return normalized;\n }\n\n /**\n * A move between rooms (breakout) left the person in NO room: that is an exit, resolved as\n * `disconnected` with Rejoin (§3.4) — the failure is kept for the screen's remedy.\n */\n async function lostRoom(error: unknown, operation: string): Promise<MeetError> {\n const normalized = normalizeMeetError(error, operation);\n store.patch({ failure: { message: normalized.message, remedy: normalized.remedy, reason: normalized.reason } });\n await dropRoom();\n store.dispatch({ type: \"exit\", to: \"disconnected\" });\n report(\"error\", normalized.message, normalized.remedy);\n return normalized;\n }\n\n // S6c — MS, the media supervisor. Its regions never move the phase.\n const media = createMediaSupervisor({\n store,\n platform: options.mediaPlatform === undefined ? webMediaPlatform() : options.mediaPlatform,\n driver: () => driver,\n report,\n });\n\n const handlers: RoomDriverHandlers = {\n audioPlayback(canPlay) {\n media.onAudioPlayback(canPlay);\n },\n devicesChanged() {\n media.onDevicesChanged();\n },\n mediaDeviceError(error, kind) {\n media.onDeviceError(error, kind);\n },\n participants(list) {\n const joinedAt = new Date(now()).toISOString();\n projectRoster(list, joinedAt);\n },\n participantLeft(who, reason) {\n departures.set(who, reason);\n },\n roomMetadata(metadata) {\n // S8 THE SERVER'S ROOM STATE (CORE-DESIGN §2.6 RS). Only aidream's\n // `room_state.publish_room_state` writes it, always whole, read back from the rows. A\n // malformed or empty string means \"nothing set\", never a throw inside a room event.\n let parsed: Record<string, unknown> = {};\n if (metadata.length > 0) {\n try {\n const value: unknown = JSON.parse(metadata);\n if (typeof value === \"object\" && value !== null) parsed = value as Record<string, unknown>;\n } catch {\n report(\n \"warn\",\n \"meet: the room's metadata is not JSON, so the room state was not read from it\",\n \"Only aidream writes room metadata; check services/meet/room_state.py.\",\n );\n }\n }\n const key = (name: string): ParticipantIdentity | null => {\n const value = parsed[name];\n return typeof value === \"string\" && value.length > 0 ? asParticipantIdentity(personOf(value)) : null;\n };\n const waiting = typeof parsed[\"waiting_count\"] === \"number\" ? (parsed[\"waiting_count\"] as number) : 0;\n const before = store.snapshot();\n const next = { spotlight: key(\"spotlight\"), actingHost: key(\"acting_host\"), presenter: key(\"presenter\"), waitingCount: waiting };\n if (\n before.spotlight !== next.spotlight ||\n before.actingHost !== next.actingHost ||\n before.presenter !== next.presenter ||\n before.waitingCount !== next.waitingCount\n ) {\n store.patch(next);\n }\n if (parsed[\"recording\"] !== undefined && parsed[\"recording\"] !== null) {\n applyRecordingState(store, { meeting_id: parsed[\"meeting_id\"], recording: parsed[\"recording\"] });\n }\n },\n activeSpeakers(identities) {\n store.setSpeaking([...new Set(identities.map((who) => personOf(who)))]);\n },\n quality(who, value) {\n const person = personOf(who);\n lkQuality.set(person, value);\n const local = store.snapshot().participants.find((p) => p.identity === person)?.isLocal === true;\n store.setQuality(person, value === \"lost\" ? value : effectiveQuality(person, who, local));\n },\n networkReading(reading) {\n const snap = store.snapshot();\n if (snap.phase !== \"in_call\" || snap.connection !== \"stable\") return;\n startHealth();\n const before = assessor.current();\n const quality = assessor.push(reading);\n const path = pathOf(reading);\n if (quality !== before) {\n report(\"info\", `meet: this connection reads ${quality} (round trip ${reading.rttMs ?? \"?\"} ms, path ${path})`, \"Diagnostic only.\");\n }\n patchLocalNetwork({ quality, ...(path !== \"unknown\" ? { path } : {}) });\n refreshQualities();\n // `net-video-auto-off`: a path poor for the knob's time pauses the camera to keep the audio.\n const cameraOn = snap.media.camera.enabled;\n if (autoOff.step(quality, cameraOn) === \"pause\" && driver !== null) {\n const target = driver;\n report(\"info\", \"meet: the camera was paused because the connection is poor\", \"The audio is kept; Try video again turns it back on.\");\n void enableTrack(target, \"camera\", false).then((state) => {\n if (!state.enabled) patchLocalNetwork({ videoPaused: true });\n });\n }\n },\n connectionState(state) {\n // `reconnecting` is a VISIBLE state, not a silent one — and it is the `connection`\n // REGION, never a phase (CORE-DESIGN §3.1/§3.2): the person is still in the call. Which\n // region value (signal blip, full reconnect, offline, servers unreachable) is the\n // connection supervisor's call (S6b).\n supervisor.driverState(state);\n if (state === \"connected\" && store.snapshot().phase === \"joining\") store.dispatch({ type: \"connected\" });\n if (state === \"connected\") {\n // Beat at once: a person who drops in their first seconds is still known to everyone.\n startHealth();\n lastBeatAt = 0;\n healthTick();\n }\n },\n transcription(event) {\n // A CAPTION IS A HINT (D16). It is stored as one, next to the durable\n // rows, and the reconciliation in `transcript.ts` decides which of the two\n // a line ends up being. Nothing else in this package reads captions.\n const snapshot = store.snapshot();\n const meeting = snapshot.meeting;\n if (meeting === null) {\n // A caption with no meeting record has nothing durable to reconcile\n // against later. An ad-hoc call room is exactly that case; dropping it\n // is correct, and it is not silent — it is stated here.\n return;\n }\n // `lk.transcribed_participant_identity` is supplied by the publisher and\n // only says who they CLAIM to be transcribing. The stream's publisher is\n // the SFU-reported identity; captions are admitted only from this\n // meeting's deterministic note-taker. This remains display-only (D16),\n // but a room participant cannot write a fake line into the caption rail.\n // This check deliberately does not wait for a roster event: the first\n // note-taker utterance may arrive before its roster projection.\n if (event.publisherIdentity !== noteTakerIdentity(meeting.id)) {\n report(\n \"warn\",\n \"meet: ignored a caption from a participant that is not this meeting's note-taker\",\n \"Captions are accepted only from the server-dispatched note-taker; the durable transcript remains authoritative.\",\n );\n return;\n }\n const speakerIdentity = personOf(event.speakerIdentity);\n const speaker = snapshot.participants.find(\n (participant) => participant.identity === speakerIdentity,\n );\n store.ingestCaption(\n captionSegment({\n captionId: event.segmentId,\n meetingId: meeting.id,\n identity: speakerIdentity,\n // 🚨 ONE ANSWER TO \"WHO SAID THAT\" (MRI-A16). This line used to fall\n // back to `event.speakerIdentity` — the raw LiveKit token — which is\n // how a guest's caption rail read `guest:c8-spk-1` in the same second\n // the host's read \"Ada Lovelace\". The book keeps the last known name\n // for somebody who has left, and says \"Unnamed guest\" when nothing\n // anywhere has ever known one. It never prints a token.\n speaker: displayNameFor(snapshot.names, speakerIdentity, speaker?.displayName),\n text: event.text,\n final: event.final,\n startedAt: event.at,\n }),\n );\n },\n data(topic, payload, from) {\n // 🚨 THE SERVER'S OWN EVENTS COME FIRST, and they are the only live feed a\n // session-less guest has (MRI-A12). They are handled before the `mx.*`\n // control plane because they are a different KIND of message: published by\n // the platform with a `roomAdmin` grant no participant token can claim,\n // carrying durable rows rather than a sender's intent.\n if (isRoomEventTopic(topic)) {\n // `matrx.meet.*` is a server namespace, not an ACL. Any participant\n // may publish to a topic, including one with an agent-looking name;\n // only RoomService.SendData reaches the SDK without a participant.\n if (from !== null) {\n report(\n \"warn\",\n `meet: ignored participant-published server event on ${topic}`,\n \"Only aidream may publish matrx.meet.* events. The durable meeting record remains unchanged.\",\n );\n return;\n }\n let body: unknown;\n try {\n body = JSON.parse(payload);\n } catch {\n report(\n \"warn\",\n `meet: dropped an unparseable room event on ${topic}`,\n \"This topic is published by aidream only. Non-JSON here means a client is \" +\n \"publishing under the server's namespace, or the payload was truncated.\",\n );\n return;\n }\n if (topic === ROOM_EVENTS.ended && options.resolveMeeting !== undefined) {\n const meeting = store.snapshot().meeting;\n if (typeof body === \"object\" && body !== null && \"meeting_id\" in body && body.meeting_id === meeting?.id) {\n void confirmMeetingEnd();\n } else {\n report(\"warn\", \"Meet ignored an ending for another or missing meeting\", \"Only a matching canonical meeting can end this transport.\");\n }\n return;\n }\n const outcome = applyRoomEvent({ store, topic, payload: body, now });\n if (!outcome.handled) {\n // NEVER SILENT. A dropped room event is a note, a decision or an\n // ending that a guest will simply never see, and the reason has to be\n // reachable from the surface rather than from a debugger.\n report(\n \"warn\",\n `meet: ignored a room event on ${topic} — ${outcome.ignored ?? \"no reason given\"}`,\n \"The durable row still exists behind the meeting link. Signed-in members \" +\n \"receive it over Postgres Changes; a guest sees it on their next join.\",\n );\n }\n return;\n }\n let envelope: DataEnvelope = {};\n try {\n const parsed: unknown = JSON.parse(payload);\n if (typeof parsed === \"object\" && parsed !== null) envelope = parsed as DataEnvelope;\n } catch {\n report(\n \"warn\",\n `meet: dropped an unparseable data message on ${topic}`,\n \"A sender is publishing non-JSON on a reserved mx.* topic. Use publishData through \" +\n \"the engine so the envelope is always well formed.\",\n );\n return;\n }\n const sender = personOf(from ?? \"\");\n if (from !== null) {\n if (topic === ALIVE_TOPIC && isBye(payload)) {\n saidBye.add(from);\n return;\n }\n const beat = topic === ALIVE_TOPIC ? decodeBeat(payload) : null;\n const before = liveness.reported(from);\n liveness.heard(from, beat);\n if (topic === ALIVE_TOPIC) {\n startHealth();\n if (beat !== null && (before?.q !== beat.q || before?.vp !== beat.vp || silent.has(from))) {\n silent.delete(from);\n projectRoster(liveRoster, new Date(now()).toISOString());\n refreshQualities();\n } else if (silent.delete(from)) {\n projectRoster(liveRoster, new Date(now()).toISOString());\n }\n return;\n }\n }\n switch (topic) {\n case DATA_TOPICS.reaction:\n if (typeof envelope.emoji === \"string\") {\n store.addReaction({\n id: envelope.id ?? `${sender}-${now()}`,\n identity: sender,\n emoji: envelope.emoji,\n at: now(),\n });\n }\n return;\n case DATA_TOPICS.chat:\n // ONLY PRIVATE MESSAGES ride the data plane since 0.8: a message to\n // everyone is SAVED through the server and arrives as the server's\n // `matrx.meet.chat`. A participant's un-addressed `mx.chat` packet is\n // an older client's ephemeral chat — shown, never mistaken for saved.\n if (typeof envelope.text === \"string\") {\n const local = store.snapshot().localIdentity;\n store.addChat({\n id: envelope.id ?? `${sender}-${now()}`,\n identity: sender,\n displayName: envelope.name ?? sender,\n text: envelope.text,\n at: envelope.at ?? new Date(now()).toISOString(),\n ...(typeof envelope.to === \"string\" && local !== null ? { to: local } : {}),\n });\n }\n return;\n default:\n // Unknown topics are IGNORED, never thrown on — that is what lets a\n // newer client ship a new in-room feature without breaking old ones.\n return;\n }\n },\n disconnected(reason) {\n // A breakout move, or a disconnect THIS engine asked for (Leave, end, dispose), is not\n // an exit to resolve: the caller already said where the person went.\n if (moving || driver === null) return;\n void resolveExitAndApply(reason);\n },\n };\n\n /**\n * §3.4 — LiveKit dropped this tab. Ask the server what happened to THIS connection, then land\n * on the phase the resolution names. Never pre-join; `disconnected` always offers Rejoin.\n */\n async function resolveExitAndApply(hint: string | null): Promise<void> {\n const connection = current?.connectionIdentity ?? current?.identity ?? store.snapshot().localIdentity ?? null;\n // The pass this connection held — read BEFORE the room is dropped (which forgets the token):\n // S7's \"switched to another tab\" is only the server's word, never LiveKit's.\n const heldPass = current?.meetingPass ?? null;\n // Come back as you were: the camera and microphone as they are at the drop (S6b), for the\n // auto-rejoin, a manual Rejoin and a reload alike — `dropRoom` resets the local tracks.\n lastRequest = requestAsNow();\n // THE SERVER ENDED THIS SESSION (removed, room closed, token revoked), so the cached token\n // is dead: the next Join must mint again and read the server's own decision. Reusing it\n // showed \"invalid token: revoked\" under a camera-refusal remedy (production 2026-09-27).\n await dropRoom();\n let server: ExitServerWord | null | undefined;\n if (options.readExit === undefined || connection === null || store.snapshot().meeting === null) {\n server = undefined;\n } else {\n try {\n server = await options.readExit(connection, heldPass);\n } catch {\n server = null;\n }\n }\n if (disposed) return;\n const resolved = resolveExit({ server, hint, frozen: options.wasFrozen?.() ?? false });\n // The state read failed: the supervisor knows WHICH failure — no network, or our servers.\n store.patch({ connection: server === null ? supervisor.exitConnection() : resolved.connection });\n if (resolved.to === \"waiting:knocking\") {\n store.patch({\n waiting: {\n admission: \"knocking\",\n admittersPresent: null,\n reknockAllowed: true,\n knockExpiresAt: null,\n pollSeconds: store.snapshot().waiting?.pollSeconds ?? 3,\n liveVersion: 0,\n },\n });\n }\n store.dispatch({ type: \"exit\", to: resolved.to }, { reason: resolved.reason, notice: resolved.notice });\n report(\n resolved.to === \"disconnected\" ? \"warn\" : \"info\",\n `meet: the room connection ended (${hint ?? \"no LiveKit reason\"}) → ${resolved.to}`,\n resolved.to === \"disconnected\" ? \"Press Rejoin; the meeting is still going.\" : \"The screen says what happened.\",\n );\n options.onLeft?.(hint);\n }\n\n async function connect(request: JoinRequest): Promise<void> {\n store.patch({ failure: null, waiting: null, roomName: request.roomName, share: NO_SHARE, connection: \"stable\" });\n const tabId = options.tabClaim !== undefined ? await options.tabClaim.tabId : null;\n const token = await tokens.get({\n roomName: request.roomName,\n identity,\n ...(request.meetingId !== undefined ? { meetingId: request.meetingId } : {}),\n ...(request.slug !== undefined ? { slug: request.slug } : {}),\n ...(request.breakoutRoomId != null ? { breakoutRoomId: request.breakoutRoomId } : {}),\n ...(request.observe === true ? { observe: true } : {}),\n ...(tabId !== null ? { tabId } : {}),\n ...(request.secondDevice !== undefined ? { secondDevice: request.secondDevice } : {}),\n });\n if (token.secondDeviceChoice != null) {\n // S7: ALREADY IN THIS MEETING on another tab or device. Nothing was minted; the pre-join\n // offers the meeting's choice and the person's press joins again with the answer.\n store.patch({ secondDevice: { rule: token.secondDeviceChoice }, localIdentity: token.identity });\n store.dispatch({ type: \"second_device\" });\n report(\"info\", `meet: already in this meeting elsewhere — offering the choice (${token.secondDeviceChoice})`);\n return;\n }\n if (store.snapshot().secondDevice !== null) store.patch({ secondDevice: null });\n // A switch happens once: a later rejoin of THIS tab must not displace a tab opened since.\n if (request.secondDevice === \"switch_here\" && lastRequest !== null) {\n const { secondDevice: _answered, ...rest } = lastRequest;\n lastRequest = rest;\n }\n options.tabClaim?.inRoom(String(request.roomName), String(token.identity), token.secondDeviceRule ?? null);\n current = token;\n lastServerUrl = token.serverUrl;\n connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;\n store.patch({ captionsStartTimeoutSeconds: connectionPolicy.captionsStartTimeoutSeconds });\n if (token.leavePolicy !== undefined) store.patch({ leavePolicy: token.leavePolicy });\n store.patch({ canReclaim: token.canReclaim === true });\n // 🚨 THE GRANTS LAND WITH THE TOKEN, before any roster exists (MRI-A11).\n // Waiting for the LiveKit roster to learn that this person is the host means\n // the host's own controls flicker in — or, when a roster event arrives with\n // empty metadata, never appear at all. That was the shipped defect.\n store.patch({\n localIdentity: token.identity,\n breakoutRoomId: token.breakoutRoomId ?? null,\n grants: {\n role: token.role,\n standing: token.standing,\n canRecord: token.canRecord,\n canPublish: token.canPublish,\n canSubscribe: token.canSubscribe,\n observersVisibleTo: token.observersVisibleTo ?? \"everyone\",\n },\n });\n\n // THE LOBBY IS A PHASE, NOT AN ERROR (D6). The server said \"wait\"; the UI\n // shows the waiting room and the room channel delivers the host's decision.\n //\n // 🚨 S4: A TOKEN THAT CANNOT SUBSCRIBE NEVER BECOMES AN IN-CALL SCREEN. A waiting guest\n // was once shown an empty room (\"Just you\") while the host still listed them as waiting:\n // a media connection that can hear nobody IS the waiting room, whatever flag came with\n // it. The wait itself is then read from the server (`GET /v1/meet/state`), never from a\n // broadcast (CORE-DESIGN §2.6).\n if (token.lobby || !token.canSubscribe) {\n const admission = token.admissionState !== undefined && token.admissionState !== \"admitted\"\n ? token.admissionState\n : \"knocking\";\n store.patch({\n waiting: {\n admission,\n admittersPresent: null,\n reknockAllowed: true,\n knockExpiresAt: null,\n pollSeconds: token.pollSeconds ?? 3,\n liveVersion: 0,\n },\n });\n store.dispatch({ type: \"admission\", admission });\n return;\n }\n\n store.patch({ waiting: null });\n const created = await options.driverFactory(handlers);\n if (disposed) {\n // Unmounted mid-connect. Disconnect the room we just built rather than\n // leaking it — this is the \"camera light stays on\" bug.\n await created.disconnect();\n return;\n }\n driver = created;\n // S7: a companion plays no remote audio — set before the connect so no packet is ever heard.\n if (token.connectionKind === \"companion\") created.setRemoteAudioPlayback?.(false);\n await created.connect(token.serverUrl, token.token);\n\n // 🚨 IN THE ROOM. Attendance is settled HERE, before a single device is\n // touched. Everything below is best-effort publishing; nothing below can\n // unwind the join. See `attendance.ts` for the defect this closes.\n store.patch({ failure: null, connection: \"stable\" });\n store.dispatch({ type: \"connected\" });\n store.setLocalTrack(\"microphone\", IDLE_TRACK);\n store.setLocalTrack(\"camera\", IDLE_TRACK);\n media.start();\n\n // THE REMEMBERED CHOICES GO ON BEFORE ANY DEVICE DOES, so the first frame\n // anyone sees is already blurred and the first word already cleaned.\n const effects = options.effectsStorage?.load() ?? store.snapshot().effects;\n store.patch({ effects });\n // Only what DIFFERS from the browser default is applied on the way in: a\n // default join touches nothing it does not have to.\n if (!effects.noiseSuppression || effects.background !== \"none\") {\n try {\n await applyEffects(created, effects);\n } catch {\n // Reported inside; the join never depends on an effect.\n }\n }\n for (const kind of [\"audioinput\", \"videoinput\", \"audiooutput\"] as const) {\n const chosen = options.resolveDevice?.(kind) ?? null;\n if (chosen === null || chosen === \"default\") continue;\n try {\n await created.switchDevice(kind, chosen);\n } catch (error) {\n const normalized = normalizeDeviceError(error, `meet.switchDevice(${kind})`);\n report(\"warn\", normalized.message, normalized.remedy);\n }\n }\n\n if (request.mode === \"video\") {\n await enableTrack(created, \"camera\", true);\n }\n // S7: a companion (\"join here too\") never opens the microphone — the person's other device\n // is the one that speaks.\n if (request.microphone !== false && current?.connectionKind !== \"companion\") {\n await enableTrack(created, \"microphone\", true);\n }\n // What is in use now is the baseline a later unplug is measured against.\n media.onConnected();\n }\n\n /**\n * THE ONE PATH that turns a local device on or off — used by the join and by\n * the in-room controls alike, so \"enable it later\" cannot behave differently\n * from \"enable it on the way in\". A refusal is recorded as state with the\n * browser's own reason and a remedy, reported as a diagnostic, and returned;\n * it is never thrown, because there is no caller for whom losing the room is\n * the right answer to a busy microphone.\n */\n async function enableTrack(\n target: MeetRoomDriver,\n kind: \"microphone\" | \"camera\",\n enabled: boolean,\n ): Promise<LocalTrackState> {\n try {\n if (kind === \"microphone\") await target.setMicrophoneEnabled(enabled);\n else await target.setCameraEnabled(enabled);\n const state: LocalTrackState = { enabled, unavailable: null };\n store.setLocalTrack(kind, state);\n return state;\n } catch (error) {\n // S6c: the refusal is classified against the permission store (Block vs the operating\n // system vs a missing or busy device) — the error name alone cannot tell them apart.\n const refused = await media.unavailable(error, kind, `meet.${kind}`);\n const state: LocalTrackState = { enabled: false, unavailable: refused };\n store.setLocalTrack(kind, state);\n report(\"warn\", refused.message, refused.remedy);\n return state;\n }\n }\n\n async function publish(\n topic: string,\n body: Record<string, unknown>,\n to?: readonly string[],\n ): Promise<void> {\n if (driver === null) return;\n // S7: a private message goes to a PERSON — every connection they hold.\n const addresses = to === undefined ? undefined : to.flatMap((who) => connectionsOf(who));\n await driver.publishData(topic, JSON.stringify(body), addresses);\n }\n\n async function applyEffects(target: MeetRoomDriver, effects: MediaEffects): Promise<void> {\n try {\n await target.setAudioProcessing?.({\n noiseSuppression: effects.noiseSuppression,\n echoCancellation: true,\n autoGainControl: true,\n });\n } catch (error) {\n report(\"warn\", `meet: noise suppression could not be applied: ${String(error)}`, \"The microphone keeps working without it.\");\n }\n if (target.setVideoEffect === undefined) return;\n const effect: VideoEffect =\n effects.background === \"blur\"\n ? { kind: \"blur\" }\n : effects.background === \"image\" && effects.backgroundImage !== null\n ? { kind: \"image\", imageUrl: effects.backgroundImage }\n : { kind: \"none\" };\n if (effect.kind !== \"none\" && target.supportsVideoEffects?.() === false) return;\n try {\n await target.setVideoEffect(effect);\n } catch (error) {\n const normalized = normalizeMeetError(error, \"meet.setVideoEffect\");\n report(\"warn\", normalized.message, normalized.remedy);\n throw normalized;\n }\n }\n\n /**\n * THE ROSTER, WITH OTHERS' DROPS HELD (S6b, `net-others-see-drop`). A participant LiveKit\n * dropped without a deliberate reason stays as a `reconnecting` tile for\n * `reconnect_grace_seconds`; they disappear at once when they come back (same identity — one\n * tile, never a copy) or when the grace runs out.\n */\n function projectRoster(list: readonly DriverParticipant[], joinedAt: string): void {\n const at = now();\n const previous = new Map(liveRoster.map((p) => [p.identity, p]));\n const present = new Set(list.map((p) => p.identity));\n const graceMs = connectionPolicy.reconnectGraceSeconds * 1000;\n for (const [who, reason] of departures) {\n const last = previous.get(who);\n // A drop is a LiveKit reason that is not deliberate, OR a person who had already gone silent\n // on the heartbeat (the media server can report a pulled cable as an ordinary close) — never\n // someone who said goodbye.\n const dropped = !saidBye.has(who) && (isUnannouncedDeparture(reason) || silent.has(who));\n report(\"info\", `meet: ${who} left the room (${reason ?? \"no reason\"}${dropped ? \", held as reconnecting\" : \"\"})`, \"Diagnostic only.\");\n if (last !== undefined && !last.isLocal && !present.has(who) && graceMs > 0 && dropped) {\n ghosts.set(who, { participant: last, until: at + graceMs });\n }\n }\n for (const who of departures.keys()) {\n liveness.forget(who);\n silent.delete(who);\n saidBye.delete(who);\n }\n departures.clear();\n liveRoster = list;\n // `share-stopped-by-browser`: the browser's own \"Stop sharing\" ends the track and LiveKit\n // unpublishes it — the share region follows the roster, never a stale \"presenting\".\n const localNow = list.find((p) => p.isLocal);\n if (localNow !== undefined && !localNow.screenSharing && store.snapshot().share.state === \"presenting\") {\n store.patch({ share: NO_SHARE });\n }\n const merged = mergeGhosts(list, ghosts, at);\n ghosts = merged.kept;\n // S7: ONE TILE PER PERSON. A person on two tabs/devices is two LiveKit connections; the tile\n // shows the one to look at — this tab first, then a live main connection, then any.\n const people = new Map<string, { entry: (typeof merged.roster)[number]; rank: number }>();\n const connections = new Map<string, string[]>();\n for (const entry of merged.roster) {\n const person = personOf(entry.identity);\n connections.set(person, [...(connections.get(person) ?? []), entry.identity]);\n const rank =\n (entry.isLocal ? 8 : 0) +\n (entry.reconnecting === true ? 0 : 4) +\n (entry.attributes[CONNECTION_KIND_ATTRIBUTE] === \"companion\" ? 0 : 2) +\n (entry.cameraEnabled || entry.micEnabled ? 1 : 0);\n const held = people.get(person);\n if (held === undefined || rank > held.rank) people.set(person, { entry, rank });\n }\n personConnections = connections;\n mainConnection = new Map([...people].map(([person, { entry }]) => [person, entry.identity]));\n store.setParticipants(\n [...people.values()].map(({ entry }) => {\n const projected = projectParticipant(entry, joinedAt);\n if (entry.reconnecting === true || (!entry.isLocal && silent.has(entry.identity))) {\n return { ...projected, reconnecting: true, quality: \"lost\" as const };\n }\n const videoPaused = entry.isLocal\n ? store.snapshot().localNetwork.videoPaused\n : liveness.reported(entry.identity)?.vp === true;\n const quality = effectiveQuality(projected.identity, entry.identity, entry.isLocal);\n return {\n ...projected,\n ...(quality !== \"unknown\" && quality !== \"lost\" ? { quality } : {}),\n ...(videoPaused ? { videoPaused: true } : {}),\n };\n }),\n );\n scheduleGhostExpiry();\n }\n\n function scheduleGhostExpiry(): void {\n if (ghostTimer !== null) clearTimeout(ghostTimer);\n ghostTimer = null;\n if (ghosts.size === 0 || disposed) return;\n const next = Math.min(...[...ghosts.values()].map((g) => g.until));\n ghostTimer = setTimeout(() => {\n ghostTimer = null;\n if (disposed || driver === null) return;\n projectRoster(liveRoster, new Date(now()).toISOString());\n }, Math.max(0, next - now()) + 50);\n }\n\n function clearGhosts(): void {\n ghosts = new Map();\n departures.clear();\n liveRoster = [];\n if (ghostTimer !== null) clearTimeout(ghostTimer);\n ghostTimer = null;\n }\n\n const supervisor = createConnectionSupervisor({\n store,\n network: options.connection?.network ?? createWebNetworkPort(),\n probe: () => options.connection?.probe?.(current?.serverUrl ?? lastServerUrl) ?? Promise.resolve(true),\n policy: () => connectionPolicy,\n autoRejoin: () => engine.autoRejoin(),\n currentRequest: requestAsNow,\n resume: options.connection?.resume,\n now,\n report: (level, message) => report(level, message),\n });\n\n const engine: RoomEngine = {\n join(request) {\n // SINGLE FLIGHT: a strict-mode double effect and a user's second click\n // share one join. Two joins for one identity make LiveKit drop the first.\n if (joining !== null) return joining;\n // THE PERSON'S PRESS (§3.1): prejoin / left / disconnected / superseded → joining. An\n // answered call or an admission already moved the phase to `joining`.\n if (store.snapshot().phase !== \"joining\" && !store.dispatch({ type: \"join\", click: true })) {\n report(\n \"warn\",\n `meet.join: cannot join from \"${store.snapshot().phase}\"`,\n \"Join is offered from the pre-join, left, disconnected and replaced screens only.\",\n );\n return Promise.resolve();\n }\n lastRequest = request;\n joining = connect(request)\n .catch((error: unknown) => {\n throw failJoin(error, \"meet.join\");\n })\n .finally(() => {\n joining = null;\n });\n return joining;\n },\n async rejoin() {\n const request = lastRequest;\n if (request === null) {\n // Nothing to rejoin is not an error and never a silent no-op: it means\n // this engine never joined, which a caller can only reach by wiring the\n // admission signal to a room nobody is waiting in.\n report(\n \"warn\",\n \"meet.rejoin: this room engine has never joined, so there is nothing to rejoin\",\n \"Call join() first. An admission verdict for a participant who never knocked is \" +\n \"ignored rather than minting a token nobody asked for.\",\n );\n return;\n }\n if (joining !== null) return joining;\n // ADMITTED from the waiting room, or the person's own Rejoin (disconnected / left /\n // replaced) — the two ways in that are not the first Join (§3.1).\n const phase = store.snapshot().phase;\n // `join` covers the person's presses (Rejoin, Ask to join again); `admitted` the server's yes.\n const moved =\n phase === \"joining\" ||\n store.dispatch({ type: \"join\", click: true }) ||\n store.dispatch({ type: \"admitted\" });\n if (!moved) {\n report(\"warn\", `meet.rejoin: cannot rejoin from \"${phase}\"`, \"Rejoin is offered after leaving, a lost connection, or admission.\");\n return;\n }\n // THE LOBBY TOKEN IS THROWN AWAY. It grants neither publish nor\n // subscribe; reusing it would put the admitted guest in the room as a\n // participant who can neither speak nor hear.\n tokens.invalidate(request.roomName);\n current = null;\n joining = connect(request)\n .catch((error: unknown) => {\n throw failJoin(error, \"meet.rejoin\");\n })\n .finally(() => {\n joining = null;\n });\n return joining;\n },\n async autoRejoin() {\n const request = lastRequest;\n if (request === null || joining !== null) return joining ?? undefined;\n // Not a click: only `disconnected` → `joining` (§3.1). Anything else (the person already\n // pressed Rejoin, left, or the meeting ended) means there is nothing to do.\n if (!store.dispatch({ type: \"auto_rejoin\" })) return;\n tokens.invalidate(request.roomName);\n current = null;\n joining = connect(request)\n .catch((error: unknown) => {\n throw failJoin(error, \"meet.autoRejoin\");\n })\n .finally(() => {\n joining = null;\n });\n return joining;\n },\n resume(roomName) {\n const marker = supervisor.resumable(roomName);\n if (marker === null || joining !== null) return false;\n if (!store.dispatch({ type: \"resumed\" })) return false;\n lastRequest = { ...marker.request, roomName };\n report(\"info\", \"meet: this tab was in the call before the page reloaded — rejoining\");\n tokens.invalidate(roomName);\n current = null;\n joining = connect(lastRequest)\n .catch((error: unknown) => {\n throw failJoin(error, \"meet.resume\");\n })\n .finally(() => {\n joining = null;\n });\n joining.catch(() => undefined);\n return true;\n },\n async moveToBreakout(roomId) {\n const request = lastRequest;\n if (request === null || driver === null) {\n report(\n \"warn\",\n \"meet.moveToBreakout: this engine is not in a meeting room\",\n \"Join the meeting first; a breakout is a room OF a meeting.\",\n );\n return;\n }\n if ((store.snapshot().breakoutRoomId ?? null) === roomId) return;\n if (joining !== null) await joining.catch(() => undefined);\n const previous = driver;\n const next: JoinRequest = {\n ...request,\n breakoutRoomId: roomId,\n // Arrive as you were: camera and microphone as they are now.\n mode: store.snapshot().media.camera.enabled ? \"video\" : \"audio\",\n microphone: store.snapshot().media.microphone.enabled,\n };\n moving = true;\n try {\n driver = null;\n tokens.invalidate(request.roomName);\n current = null;\n await previous.disconnect();\n lastRequest = next;\n joining = connect(next).finally(() => {\n joining = null;\n });\n await joining;\n } catch (error) {\n // THE WAY BACK IS ALWAYS OPEN: a refused breakout returns the person to the\n // main room rather than leaving them in no room at all.\n const normalized = normalizeMeetError(error, \"meet.moveToBreakout\");\n report(\"warn\", normalized.message, normalized.remedy);\n if (roomId !== null) {\n lastRequest = { ...next, breakoutRoomId: null };\n joining = connect(lastRequest).finally(() => {\n joining = null;\n });\n await joining.catch(async (again: unknown) => {\n throw await lostRoom(again, \"meet.moveToBreakout\");\n });\n } else {\n throw await lostRoom(error, \"meet.moveToBreakout\");\n }\n throw normalized;\n } finally {\n moving = false;\n }\n },\n leave: leaveRoom,\n async checkElsewhere(roomName) {\n const claim = options.tabClaim;\n const userId = identity.userId;\n if (claim === undefined || userId === null) return false;\n const before = store.snapshot();\n if (before.phase !== \"prejoin\" || before.secondDevice !== null) return false;\n const person = `user:${String(userId)}`;\n const found =\n claim.presenceElsewhere !== undefined\n ? await claim.presenceElsewhere(String(roomName), person)\n : (await claim.presentElsewhere(String(roomName), person))\n ? { rule: null }\n : null;\n // `allow_both` (Zoom): nobody is asked — Join adds this tab, as the server would.\n if (found === null || found.rule === \"allow_both\") return false;\n // The person may have pressed Join (or left) while the other tabs answered.\n const now = store.snapshot();\n if (now.phase !== \"prejoin\" || now.secondDevice !== null) return false;\n // The live tab's word for the meeting's `second_device` rule (Meet's choice when unknown).\n store.patch({ secondDevice: { rule: found.rule ?? \"ask_switch_or_join\" } });\n report(\"info\", \"meet: this browser already has you in this meeting — offering the choice before Join\");\n return true;\n },\n backToPrejoin() {\n // The person's own press from `left` / `disconnected` (§3.1) — the only way back.\n store.dispatch({ type: \"back_to_prejoin\", click: true });\n },\n // Turning a device on mid-meeting is the SAME operation as turning it on\n // during the join, so it runs the same function. A failure here used to\n // stamp the room `failed` — a busy microphone would blank a live meeting.\n async setMicrophoneEnabled(enabled) {\n if (driver === null) return;\n await enableTrack(driver, \"microphone\", enabled);\n },\n async setCameraEnabled(enabled) {\n if (driver === null) return;\n const state = await enableTrack(driver, \"camera\", enabled);\n // `net-video-auto-off` \"Try video again\": the person's own choice wins; no re-pause until\n // the path has recovered once.\n if (enabled && state.enabled && store.snapshot().localNetwork.videoPaused) {\n autoOff.retried();\n patchLocalNetwork({ videoPaused: false });\n }\n if (!enabled && store.snapshot().localNetwork.videoPaused) patchLocalNetwork({ videoPaused: false });\n },\n async setScreenShareEnabled(enabled) {\n if (driver === null) return;\n // 🚨 AN IN-CALL ACTION NEVER CHANGES THE CALL PHASE (CORE-DESIGN §3.3 SA). Every outcome\n // lands in the `share` REGION; there is no phase event for it at all.\n // A probe that itself throws is not \"unsupported\": try the share, and let ITS outcome say.\n let unsupported = false;\n try {\n unsupported = enabled && driver.supportsScreenShare?.() === false;\n } catch {\n unsupported = false;\n }\n if (unsupported) {\n // This browser cannot share (no getDisplayMedia): say so, keep the call.\n store.patch({ share: { state: \"refused\", reason: \"unsupported\", remedy: null, at: now() } });\n report(\"warn\", \"meet: this browser cannot share a screen\", \"Share from a desktop browser (Chrome, Edge, Firefox or Safari).\");\n return;\n }\n if (enabled) store.patch({ share: { state: \"picking\", reason: null, remedy: null, at: now() } });\n try {\n await driver.setScreenShareEnabled(enabled);\n ambiguousShareDenials = 0;\n const audio = enabled ? driver?.screenShareHasAudio?.() : undefined;\n store.patch({\n share: { state: enabled ? \"presenting\" : \"none\", reason: null, remedy: null, at: now(), ...(audio !== undefined ? { audio } : {}) },\n });\n } catch (error) {\n const kind = classifyScreenShareError(error);\n if (kind === \"ambiguous\") {\n // Closing the picker is a choice, and some browsers cannot tell it from a\n // block: stay silent once, explain on the second consecutive refusal.\n ambiguousShareDenials += 1;\n if (ambiguousShareDenials < 2) {\n store.patch({ share: NO_SHARE });\n return;\n }\n }\n ambiguousShareDenials = 0;\n const normalized = normalizeScreenShareError(error, \"meet.setScreenShareEnabled\");\n report(\"warn\", normalized.message, normalized.remedy);\n store.patch({\n share: {\n state: \"refused\",\n reason: kind === \"other\" ? \"failed\" : \"blocked\",\n remedy: kind === \"other\" ? normalized.remedy : SCREEN_SHARE_BLOCKED_REMEDY_FOR(normalized.remedy),\n at: now(),\n },\n });\n }\n },\n dismissShareNotice() {\n const state = store.snapshot().share.state;\n if (state === \"refused\" || state === \"ended\") store.patch({ share: NO_SHARE });\n },\n async react(emoji) {\n const local = store.snapshot().localIdentity;\n const id = `${local ?? \"local\"}-${now()}`;\n if (local !== null) {\n store.addReaction({ id, identity: local, emoji, at: now() });\n }\n await publish(DATA_TOPICS.reaction, { id, emoji });\n },\n async sendPrivateChat(to, text) {\n const trimmed = text.trim();\n if (trimmed.length === 0) return;\n const local = store.snapshot().localIdentity;\n const id = `${local ?? \"local\"}-${now()}`;\n const at = new Date(now()).toISOString();\n if (local !== null) {\n store.addChat({ id, identity: local, displayName: identity.displayName, text: trimmed, at, to });\n }\n await publish(\n DATA_TOPICS.chat,\n { id, text: trimmed, at, name: identity.displayName, to },\n [to],\n );\n },\n async setEffects(next) {\n const previous = store.snapshot().effects;\n const merged: MediaEffects = { ...previous, ...next };\n store.patch({ effects: merged });\n options.effectsStorage?.save(merged);\n if (driver === null) return;\n try {\n await applyEffects(driver, merged);\n } catch {\n // The camera keeps running without the effect; the choice reverts so\n // the control never claims an effect that is not on screen.\n const reverted: MediaEffects = { ...merged, background: previous.background, backgroundImage: previous.backgroundImage };\n store.patch({ effects: reverted });\n options.effectsStorage?.save(reverted);\n }\n },\n supportsVideoEffects() {\n return driver?.supportsVideoEffects?.() ?? false;\n },\n supportsScreenShare() {\n // Before a room exists the browser itself is asked (a phone never shows Share screen).\n return driver?.supportsScreenShare?.() ?? detectCapabilities().canShareScreen;\n },\n async switchDevice(kind, deviceId) {\n if (driver === null) return;\n await driver.switchDevice(kind, deviceId);\n },\n enableAudio: () => media.enableAudio(),\n previewDevice: (kind, outcome) => media.preview(kind, outcome),\n acceptDeviceOffer: () => media.acceptOffer(),\n dismissDeviceOffer: () => media.dismissOffer(),\n dismissDeviceNotice: () => media.dismissNotice(),\n undoDeviceNotice: () => media.undoNotice(),\n pin(who) {\n store.patch({ pinned: who, layout: who === null ? \"grid\" : \"speaker\" });\n },\n setLayout(layout) {\n store.patch({ layout });\n },\n attach(who, kind, element) {\n if (driver === null) return () => undefined;\n // S7: the tile is the person; its media is their main connection's.\n return driver.attach(mainConnection.get(personOf(who)) ?? who, kind, element);\n },\n token: () => current,\n async dispose() {\n disposed = true;\n supervisor.dispose();\n media.stop();\n clearGhosts();\n if (driver !== null) {\n const target = driver;\n driver = null;\n await target.disconnect();\n }\n store.reset();\n },\n };\n return engine;\n}\n\n/** Exported for the host-power check in UI code — one definition, one rank. */\nexport function canModerate(participant: MeetParticipant | undefined): boolean {\n return participant !== undefined && isHostRole(participant.role);\n}\n\nexport function hostUserIds(participants: readonly MeetParticipant[]): readonly UserId[] {\n return participants\n .filter((participant) => participant.role === \"host\" && participant.userId !== null)\n .map((participant) => participant.userId as UserId);\n}\n","/**\n * A PARTICIPANT'S OWN MEDIA CHOICES, REMEMBERED (Meet wave 2).\n *\n * Noise suppression and the camera background are personal and per device —\n * Zoom and Meet both remember them — so they live in this browser, never on a\n * server. Blocked storage means the defaults (suppression on, no background),\n * never a failed join.\n */\n\nimport type { EffectsStorage } from \"./room\";\nimport { DEFAULT_EFFECTS, type MediaEffects } from \"./store\";\n\nfunction coerce(value: unknown): MediaEffects {\n if (typeof value !== \"object\" || value === null) return DEFAULT_EFFECTS;\n const row = value as Record<string, unknown>;\n const background =\n row[\"background\"] === \"blur\" || row[\"background\"] === \"image\" ? row[\"background\"] : \"none\";\n const backgroundImage = typeof row[\"backgroundImage\"] === \"string\" ? row[\"backgroundImage\"] : null;\n return {\n noiseSuppression: row[\"noiseSuppression\"] !== false,\n background: background === \"image\" && backgroundImage === null ? \"none\" : background,\n backgroundImage,\n };\n}\n\nexport function createWebEffectsStorage(key = \"mx.meet.effects\"): EffectsStorage {\n let memory: MediaEffects = DEFAULT_EFFECTS;\n const storage = (globalThis as { localStorage?: Storage }).localStorage;\n return {\n load() {\n if (storage === undefined) return memory;\n try {\n const raw = storage.getItem(key);\n return raw === null ? memory : coerce(JSON.parse(raw));\n } catch {\n return memory;\n }\n },\n save(effects) {\n memory = effects;\n try {\n storage?.setItem(key, JSON.stringify(effects));\n } catch {\n // Storage blocked: the choice lasts for this visit.\n }\n },\n };\n}\n","/**\n * THE IN-ROOM SERVER CALLS (Meet wave 2) — host moderation and the SAVED chat.\n *\n * 🚨 **Why these are server calls and not data packets.** Through 0.7.x a host's\n * \"Mute\" and \"Remove\" were published on the room's data plane (`mx.host`) and\n * nothing anywhere listened: the buttons did nothing. A client cannot be the\n * authority on host power, and a guest cannot write the database. So both go to\n * aidream (`POST /v1/meet/moderate`, `POST|GET /v1/meet/chat`), which decides the\n * actor's role from the meeting itself, performs the act through LiveKit's server\n * API, writes what is durable, and tells the room on its own `matrx.meet.*` topic.\n *\n * A guest proves who they are with the meeting pass this server signed for them, in\n * `x-meet-pass` — a header, never a URL.\n */\n\nimport { refusalFromResponse } from \"./ai\";\nimport type { MeetApi } from \"./api\";\nimport { MeetError } from \"./errors\";\nimport { collabFromState, withItem } from \"./collab\";\nimport { chatFromRow } from \"./room-events\";\nimport type { MeetStore, RoomChatMessage } from \"./store\";\nimport { MEET_ROUTES, MEETING_PASS_HEADER } from \"./tokens\";\nimport type { ParticipantIdentity } from \"./types\";\n\n/** Every host power the server performs. One vocabulary with `services/meet/moderation.py`. */\nexport type ModerationAction =\n | \"mute\"\n | \"stop_video\"\n | \"stop_screen\"\n | \"mute_all\"\n | \"ask_unmute\"\n | \"ask_all_unmute\"\n | \"spotlight\"\n | \"sync_presenter\"\n | \"raise_hand\"\n | \"lower_hand\"\n | \"lower_all_hands\"\n | \"make_cohost\"\n | \"make_presenter\"\n | \"revoke_presenter\"\n | \"set_av_lock\"\n | \"revoke_cohost\"\n | \"transfer_host\"\n | \"reclaim_host\"\n | \"rename\"\n | \"remove\"\n | \"report\";\n\nexport const MODERATION_ACTIONS: readonly ModerationAction[] = [\n \"mute\",\n \"stop_video\",\n \"stop_screen\",\n \"mute_all\",\n \"ask_unmute\",\n \"ask_all_unmute\",\n \"spotlight\",\n \"sync_presenter\",\n \"raise_hand\",\n \"lower_hand\",\n \"lower_all_hands\",\n \"make_cohost\",\n \"make_presenter\",\n \"revoke_presenter\",\n \"set_av_lock\",\n \"revoke_cohost\",\n \"transfer_host\",\n \"reclaim_host\",\n \"rename\",\n \"remove\",\n \"report\",\n];\n\nexport interface ModerationRequest {\n readonly action: ModerationAction;\n readonly identity?: ParticipantIdentity | null | undefined;\n readonly name?: string | undefined;\n readonly value?: string | undefined;\n readonly reason?: string | undefined;\n}\n\nexport interface ModerationResult {\n readonly action: ModerationAction;\n readonly affected: readonly string[];\n /** The server's sentence for the actor. */\n readonly message: string;\n}\n\nexport interface InRoomClient {\n moderate(request: ModerationRequest): Promise<ModerationResult>;\n /** Send a message to EVERYONE. Saved; shows at once as pending, then as saved. */\n sendChat(text: string): Promise<void>;\n /** Read the saved chat once per join, so a late joiner sees what was said. */\n loadChat(): Promise<void>;\n /**\n * Meet wave 5: read the polls, questions, whiteboard and breakout plan this viewer\n * may see, once per join. A refusal is written to `collab.error`, never thrown away.\n */\n loadCollab(): Promise<void>;\n /**\n * Meet wave 5: one poll / Q&A / whiteboard / breakout action. The server's answer is\n * folded into the store at once (the room packet that follows is the same item).\n * Returns the server's sentence; throws a `MeetError` with the server's words.\n */\n collab(request: CollabRequest): Promise<CollabResult>;\n /**\n * Meet wave 5: translate finished caption lines into `language` for this viewer.\n * Results land in `collab.captionTranslations`; a refusal in `captionTranslationError`.\n */\n translateCaptions(language: string, lines: readonly { id: string; text: string }[]): Promise<void>;\n}\n\n/** Every room-item action the server performs. One vocabulary with `services/meet/collab.py`. */\nexport type CollabAction =\n | \"poll_create\"\n | \"poll_update\"\n | \"poll_launch\"\n | \"poll_close\"\n | \"poll_share\"\n | \"poll_vote\"\n | \"poll_delete\"\n | \"question_ask\"\n | \"question_upvote\"\n | \"question_answer\"\n | \"question_reopen\"\n | \"question_dismiss\"\n | \"question_delete\"\n | \"stroke_add\"\n | \"stroke_erase\"\n | \"whiteboard_clear\"\n | \"breakout_open\"\n | \"breakout_update\"\n | \"breakout_broadcast\"\n | \"breakout_move\"\n | \"breakout_join\"\n | \"breakout_close\"\n | \"breakout_expire\"\n /** HR-360 wave 2 (MD-15): move everyone's app panel to one section. */\n | \"focus_set\";\n\nexport interface CollabRequest {\n readonly action: CollabAction;\n readonly itemId?: string | undefined;\n readonly question?: string | undefined;\n readonly options?: readonly string[] | undefined;\n readonly multiple?: boolean | undefined;\n readonly anonymous?: boolean | undefined;\n readonly optionIds?: readonly string[] | undefined;\n readonly text?: string | undefined;\n readonly stroke?:\n | {\n readonly points: readonly (readonly [number, number])[];\n readonly color: string;\n readonly width: number;\n readonly tool: \"pen\" | \"highlighter\";\n }\n | undefined;\n readonly rooms?: readonly { readonly name: string; readonly identities: readonly string[] }[] | undefined;\n readonly allowChoose?: boolean | undefined;\n readonly minutes?: number | undefined;\n readonly identity?: string | undefined;\n /** `null` = the main room (breakout_join / breakout_move). */\n readonly roomId?: string | null | undefined;\n /** focus_set: the app panel's key and the section everyone looks at. */\n readonly panel?: string | undefined;\n readonly section?: string | undefined;\n}\n\nexport interface CollabResult {\n readonly action: CollabAction;\n readonly message: string;\n readonly removed: readonly string[];\n /** The id of the item the server returned (a created poll, a new question), or null. */\n readonly itemId: string | null;\n}\n\nexport interface InRoomClientOptions {\n api: MeetApi;\n store: MeetStore;\n /** The room pass for THIS meeting, or null (a signed-in member needs none). */\n resolveRoomToken: () => string | null;\n now?: (() => number) | undefined;\n}\n\nexport function createInRoomClient(options: InRoomClientOptions): InRoomClient {\n const { api, store } = options;\n const now = options.now ?? (() => Date.now());\n\n function meetingId(operation: string): string {\n const meeting = store.snapshot().meeting;\n if (meeting === null) {\n throw new MeetError(\n \"not-found\",\n `${operation}: this room has no meeting record`,\n \"Moderation and saved chat belong to a meeting. An ad-hoc call has neither.\",\n );\n }\n return meeting.id;\n }\n\n function headers(): Record<string, string> | undefined {\n const token = options.resolveRoomToken();\n return token === null ? undefined : { [MEETING_PASS_HEADER]: token };\n }\n\n function refusal(response: Response, operation: string, remedy: string): Promise<MeetError> {\n return refusalFromResponse(response, operation, remedy);\n }\n\n // S8 PRESENTER IS TRUTH FROM THE MEDIA. Who presents is the run's (replicated room state) and\n // the SERVER decides it from the screen-share track it sees in the room (track webhooks, and\n // this nudge) — a client never claims it. The nudge carries no claim: \"my share may have\n // changed, look\". It is derived from the local screen-share track, once per change, because\n // that track also ends when the browser's own \"Stop sharing\" bar (or the shared window\n // closing) kills it — a path no button of ours sees.\n let toldSharing = false;\n let tellChain: Promise<void> = Promise.resolve();\n store.subscribe(() => {\n const local = store.snapshot().participants.find((p) => p.isLocal);\n const sharing = local?.screenSharing === true;\n if (sharing === toldSharing || local === undefined) return;\n toldSharing = sharing;\n tellChain = tellChain\n .then(() => client.moderate({ action: \"sync_presenter\" }))\n .then(\n () => undefined,\n (error: unknown) => {\n // Not retried on every store change (that would be a storm). The share itself is\n // media and keeps working; the track webhook still tells the server.\n console.warn(`[meet] the server did not take the screen-share change: ${String(error)}`);\n },\n );\n });\n\n const client: InRoomClient = {\n async moderate(request) {\n const operation = `meet.moderate(${request.action})`;\n const hdrs = headers();\n const response = await api.request({\n path: MEET_ROUTES.moderate,\n method: \"POST\",\n operation,\n body: {\n meeting_id: meetingId(operation),\n action: request.action,\n ...(request.identity != null ? { identity: request.identity } : {}),\n ...(request.name !== undefined ? { name: request.name } : {}),\n ...(request.value !== undefined ? { value: request.value } : {}),\n ...(request.reason !== undefined ? { reason: request.reason } : {}),\n },\n ...(hdrs !== undefined ? { headers: hdrs } : {}),\n });\n if (!response.ok) {\n throw await refusal(\n response,\n operation,\n \"Nothing was changed. The server decides host powers from the meeting itself.\",\n );\n }\n const payload = (await response.json()) as Record<string, unknown>;\n return {\n action: request.action,\n affected: Array.isArray(payload[\"affected\"])\n ? payload[\"affected\"].filter((value): value is string => typeof value === \"string\")\n : [],\n message: typeof payload[\"message\"] === \"string\" ? payload[\"message\"] : \"Done.\",\n };\n },\n\n async sendChat(text) {\n const body = text.trim();\n if (body.length === 0) return;\n const operation = \"meet.sendChat\";\n const id = meetingId(operation);\n const snapshot = store.snapshot();\n const me = snapshot.participants.find((p) => p.identity === snapshot.localIdentity);\n // PENDING AT ONCE: a message that appears only after a round trip feels\n // broken. It is marked pending (not `saved`) until the server's row lands.\n const localId = `pending-${now()}`;\n const pending: RoomChatMessage = {\n id: localId,\n identity: snapshot.localIdentity ?? (\"local\" as ParticipantIdentity),\n displayName: me?.displayName ?? \"You\",\n text: body,\n at: new Date(now()).toISOString(),\n saved: false,\n };\n store.addChat(pending);\n const hdrs = headers();\n let response: Response;\n try {\n response = await api.request({\n path: MEET_ROUTES.chat,\n method: \"POST\",\n operation,\n body: { meeting_id: id, body },\n ...(hdrs !== undefined ? { headers: hdrs } : {}),\n });\n } catch (error) {\n store.settleChat(localId, { failed: \"Not sent — the server could not be reached.\" });\n throw error;\n }\n if (!response.ok) {\n const error = await refusal(response, operation, \"Your message was not sent.\");\n store.settleChat(localId, { failed: error.message });\n throw error;\n }\n const saved = chatFromRow((await response.json()) as Record<string, unknown>);\n if (saved === null) {\n store.settleChat(localId, { failed: \"The server's answer could not be read.\" });\n return;\n }\n store.settleChat(localId, saved);\n },\n\n async loadChat() {\n const operation = \"meet.loadChat\";\n const id = meetingId(operation);\n const hdrs = headers();\n const response = await api.request({\n path: `${MEET_ROUTES.chat}?meeting_id=${encodeURIComponent(id)}`,\n method: \"GET\",\n operation,\n ...(hdrs !== undefined ? { headers: hdrs } : {}),\n });\n if (!response.ok) {\n throw await refusal(\n response,\n operation,\n \"Messages sent before you joined could not be read. New messages still arrive.\",\n );\n }\n const payload = (await response.json()) as { messages?: unknown };\n const rows = Array.isArray(payload.messages) ? payload.messages : [];\n const messages = rows\n .map((row) =>\n typeof row === \"object\" && row !== null ? chatFromRow(row as Record<string, unknown>) : null,\n )\n .filter((message): message is RoomChatMessage => message !== null);\n store.loadChat(messages);\n },\n\n async loadCollab() {\n const operation = \"meet.loadCollab\";\n const id = meetingId(operation);\n const hdrs = headers();\n const response = await api.request({\n path: `${MEET_ROUTES.collab}?meeting_id=${encodeURIComponent(id)}`,\n method: \"GET\",\n operation,\n ...(hdrs !== undefined ? { headers: hdrs } : {}),\n });\n if (!response.ok) {\n const error = await refusal(\n response,\n operation,\n \"Polls, questions and the whiteboard could not be read. New ones still arrive.\",\n );\n store.patch({ collab: { ...store.snapshot().collab, loaded: true, error: error.message } });\n throw error;\n }\n const body = (await response.json()) as Record<string, unknown>;\n store.patch({ collab: collabFromState(body, store.snapshot().collab) });\n },\n\n async collab(request) {\n const operation = `meet.collab(${request.action})`;\n const hdrs = headers();\n const response = await api.request({\n path: MEET_ROUTES.collab,\n method: \"POST\",\n operation,\n body: {\n meeting_id: meetingId(operation),\n action: request.action,\n ...(request.itemId !== undefined ? { item_id: request.itemId } : {}),\n ...(request.question !== undefined ? { question: request.question } : {}),\n ...(request.options !== undefined ? { options: request.options } : {}),\n ...(request.multiple !== undefined ? { multiple: request.multiple } : {}),\n ...(request.anonymous !== undefined ? { anonymous: request.anonymous } : {}),\n ...(request.optionIds !== undefined ? { option_ids: request.optionIds } : {}),\n ...(request.text !== undefined ? { text: request.text } : {}),\n ...(request.stroke !== undefined ? { stroke: request.stroke } : {}),\n ...(request.rooms !== undefined ? { rooms: request.rooms } : {}),\n ...(request.allowChoose !== undefined ? { allow_choose: request.allowChoose } : {}),\n ...(request.minutes !== undefined ? { minutes: request.minutes } : {}),\n ...(request.identity !== undefined ? { identity: request.identity } : {}),\n ...(request.roomId !== undefined ? { room_id: request.roomId } : {}),\n ...(request.panel !== undefined ? { panel: request.panel } : {}),\n ...(request.section !== undefined ? { section: request.section } : {}),\n },\n ...(hdrs !== undefined ? { headers: hdrs } : {}),\n });\n if (!response.ok) {\n throw await refusal(response, operation, \"Nothing was changed.\");\n }\n const payload = (await response.json()) as Record<string, unknown>;\n const removed = Array.isArray(payload[\"removed\"])\n ? payload[\"removed\"].filter((v): v is string => typeof v === \"string\")\n : [];\n let next = store.snapshot().collab;\n const item = payload[\"item\"];\n if (typeof item === \"object\" && item !== null) next = withItem(next, item as Record<string, unknown>);\n if (removed.length > 0) {\n const gone = new Set(removed);\n next = {\n ...next,\n polls: next.polls.filter((p) => !gone.has(p.id)),\n questions: next.questions.filter((q) => !gone.has(q.id)),\n strokes: next.strokes.filter((st) => !gone.has(st.id)),\n };\n }\n store.patch({ collab: next });\n const itemId =\n typeof item === \"object\" && item !== null && typeof (item as Record<string, unknown>)[\"id\"] === \"string\"\n ? ((item as Record<string, unknown>)[\"id\"] as string)\n : null;\n return {\n action: request.action,\n message: typeof payload[\"message\"] === \"string\" ? payload[\"message\"] : \"Done.\",\n removed,\n itemId,\n };\n },\n\n async translateCaptions(language, lines) {\n if (lines.length === 0) return;\n const operation = \"meet.translateCaptions\";\n const hdrs = headers();\n const response = await api.request({\n path: MEET_ROUTES.captionsTranslate,\n method: \"POST\",\n operation,\n body: { meeting_id: meetingId(operation), target_language: language, lines },\n ...(hdrs !== undefined ? { headers: hdrs } : {}),\n });\n if (!response.ok) {\n const error = await refusal(response, operation, \"Captions stay in the language they were spoken.\");\n store.patch({\n collab: { ...store.snapshot().collab, captionTranslationError: error.message },\n });\n throw error;\n }\n const payload = (await response.json()) as { translations?: Record<string, unknown> };\n const current = store.snapshot().collab;\n // A reply for a language the viewer has since left is not shown.\n if (current.captionLanguage !== language) return;\n const merged: Record<string, string> = { ...current.captionTranslations };\n for (const [id, value] of Object.entries(payload.translations ?? {})) {\n if (typeof value === \"string\") merged[id] = value;\n }\n store.patch({\n collab: { ...current, captionTranslations: merged, captionTranslationError: null },\n });\n },\n };\n return client;\n}\n","/**\n * THE MEETING RECORD — what a meeting IS once the room is gone (MRI-D2).\n *\n * 🚨 THE DEFECT THIS EXISTS TO KILL. Through 2026-09-08 a meeting produced a\n * wrap-up summary, decisions, owner-assigned action items, five windows of live\n * notes, thirty-two transcript segments and a recording — and then had NO\n * SURFACE AT ALL. `/meet/<slug>` fell back to `<PreJoin>` on an ended meeting,\n * so the durable link that R3 promises would outlive the meeting resolved to a\n * screen offering to join a room that no longer exists. Everything the campaign\n * built after the room closed was reachable only from SQL.\n *\n * Three rules hold this file together:\n *\n * 1. **Every read is INDEPENDENT and every refusal is NAMED.** The transcript,\n * the notes, the attendance list and the recording live in four RLS-guarded\n * tables, and a guest holding the link can read NONE of them (only\n * `meet_meeting_by_slug` is granted to `anon`). One `Promise.all` would turn\n * a permission boundary into a blank page. Known guest restrictions never issue private reads; each authenticated read that is refused\n * becomes a `MeetingRecordRestriction` with a sentence — the reader is told\n * what exists and why they cannot see it, which is the difference between a\n * record they can ask for and a product that looks broken.\n * 2. **A partial record is still a record.** A failed transcript read never\n * hides the summary somebody CAN read.\n * 3. **Nothing here computes meaning.** Grouping, ordering and formatting only.\n * The summary, decisions and action items are durable rows written by the\n * wrap-up mandate (D5/D18); this module never re-derives them from the\n * transcript, because two sources for one fact is how they start to disagree.\n */\n\nimport { formatDurationMs } from \"@ai-matrx/kit/format\";\nimport type { MeetApi } from \"./api\";\nimport { MeetError } from \"./errors\";\nimport { displayNameFor, learnNames, type NameBook } from \"./names\";\nimport type { MeetRepository } from \"./repository\";\nimport { MEET_ROUTES, MEETING_PASS_HEADER, type RoomToken } from \"./tokens\";\nimport type {\n MeetingAttendee,\n MeetingId,\n MeetingNote,\n MeetIdentity,\n MeetingRecord,\n OrganizationId,\n ParticipantIdentity,\n RecordingRecord,\n TranscriptSegment,\n} from \"./types\";\n\n/** One thing the reader could not see, and the honest reason. Never a blank. */\nexport interface MeetingRecordRestriction {\n /** Which part of the record: `transcript`, `notes`, `attendance`, `recording`. */\n readonly part: \"transcript\" | \"notes\" | \"attendance\" | \"recording\";\n readonly message: string;\n readonly remedy: string;\n}\n\n/** One visible access state, made from restrictions that share one remedy. */\nexport interface MeetingRecordRestrictionNotice {\n readonly parts: readonly MeetingRecordRestriction[\"part\"][];\n readonly message: string;\n readonly remedy: string;\n}\n\n/** A run of consecutive lines by one speaker — how a person reads a transcript. */\nexport interface TranscriptBlock {\n readonly id: string;\n /** Who spoke. Blocks break on THIS, never on the rendered name (MRI-A16). */\n readonly identity: ParticipantIdentity;\n readonly speaker: string;\n /** ISO of the first line in the block. */\n readonly startedAt: string;\n readonly lines: readonly TranscriptSegment[];\n}\n\nexport interface MeetingRecordBundle {\n readonly meeting: MeetingRecord;\n readonly attendees: readonly MeetingAttendee[];\n /** The wrap-up summary: `kind='note'` with `metadata.role='summary'` (D5). */\n readonly summary: MeetingNote | null;\n /** D20: the AUTHORITATIVE set — window rows the wrap-up superseded are out. */\n readonly decisions: readonly MeetingNote[];\n readonly actionItems: readonly MeetingNote[];\n /** The live-notes windows, in window order (D3). */\n readonly liveNotes: readonly MeetingNote[];\n readonly transcript: readonly TranscriptSegment[];\n /** The most recent recording attempt, whatever state it reached. */\n readonly recording: RecordingRecord | null;\n /** What this reader could NOT see. Rendered; never swallowed. */\n readonly restrictions: readonly MeetingRecordRestriction[];\n /**\n * Identity → name, learned from everything this read COULD see: the durable\n * attendance rows and the transcript's own `speaker` column. It is what stops\n * a raw LiveKit token reaching the screen when one of those reads was refused\n * (MRI-A16).\n */\n readonly names: NameBook;\n}\n\n/**\n * The current wrap-up summary. Repairs preserve the prior durable row for\n * history, then mark it `superseded_by`; every current-facing projection must\n * therefore make the same distinction as decisions and action items.\n */\nexport function activeWrapUpSummary(notes: readonly MeetingNote[]): MeetingNote | null {\n return notes.find((note) => note.role === \"summary\" && note.supersededBy === null) ?? null;\n}\n\n/**\n * The sentence a reader gets when the database refuses them a part of the\n * record. It is deliberately about TENANCY, not about them: `meet_notes`,\n * `meet_transcript_segments`, `meet_participants` and `meet_recordings` have no\n * `anon` policy at all, so a link-follower with no session reaches exactly the\n * meeting row and nothing else. That is the correct posture and it must be\n * SAID, not rendered as emptiness.\n */\nconst RESTRICTION_REMEDY =\n \"Open it from the meeting link in the browser you attended with, or ask the host to share it.\";\n\nconst PART_LABEL: Readonly<Record<MeetingRecordRestriction[\"part\"], string>> = {\n transcript: \"The transcript of this meeting is not readable from this account.\",\n notes: \"The notes, decisions and action items are not readable from this account.\",\n attendance: \"Who attended is not readable from this account.\",\n recording: \"The recording is not readable from this account.\",\n};\n\n/** A refusal is DATA. A transport wobble is not, and is re-thrown as itself. */\nfunction restrictionFor(\n part: MeetingRecordRestriction[\"part\"],\n error: unknown,\n): MeetingRecordRestriction {\n const code = (error as { code?: string }).code;\n const denied =\n code === \"forbidden\" || code === \"session-unavailable\" || code === \"not-found\";\n return {\n part,\n message: denied\n ? PART_LABEL[part]\n : `${PART_LABEL[part]} (${(error as Error).message ?? \"the read failed\"})`,\n remedy: denied\n ? RESTRICTION_REMEDY\n : (error as { remedy?: string }).remedy ??\n \"This is a transient read failure, not a permission decision. Reload the page.\",\n };\n}\n\n/**\n * The four durable reads behind a record, as the database returned them, plus\n * what the reader could not see. `assembleMeetingRecord` turns them into the\n * bundle; a cache that keeps them can apply a realtime row to one part and\n * assemble again, without reading the other three (0.7.79).\n */\nexport interface MeetingRecordParts {\n readonly attendees: readonly MeetingAttendee[];\n /** Every note row (all kinds, superseded included), oldest first. */\n readonly notes: readonly MeetingNote[];\n readonly transcript: readonly TranscriptSegment[];\n /** Newest first; archived recordings are not here. */\n readonly recordings: readonly RecordingRecord[];\n readonly restrictions: readonly MeetingRecordRestriction[];\n}\n\n/**\n * Load the record parts the reader may request; state all restrictions.\n *\n * `meeting` is passed in when the caller already resolved it (the slug route\n * does, through the `anon`-granted RPC) — re-reading it through the table would\n * fail for exactly the reader who most needs the page to work.\n */\nexport async function loadMeetingRecord(\n repository: MeetRepository,\n meetingId: MeetingId,\n meeting: MeetingRecord,\n reader: Pick<MeetIdentity, \"userId\"> = { userId: null },\n): Promise<MeetingRecordBundle> {\n return assembleMeetingRecord(meeting, await loadMeetingRecordParts(repository, meetingId, reader));\n}\n\n/** The four reads, each with its own verdict (see `loadMeetingRecord`). */\nexport async function loadMeetingRecordParts(\n repository: MeetRepository,\n meetingId: MeetingId,\n reader: Pick<MeetIdentity, \"userId\"> = { userId: null },\n): Promise<MeetingRecordParts> {\n const restrictions: MeetingRecordRestriction[] = [];\n\n async function attempt<T>(\n part: MeetingRecordRestriction[\"part\"],\n run: () => Promise<T>,\n empty: T,\n ): Promise<T> {\n // No anonymous policy exists for these four tables. State the known\n // restriction without probing private data or generating expected 401s.\n if (reader.userId === null) {\n restrictions.push(restrictionFor(part, { code: \"session-unavailable\" }));\n return empty;\n }\n try {\n return await run();\n } catch (error) {\n restrictions.push(restrictionFor(part, error));\n return empty;\n }\n }\n\n // Four independent reads, four independent verdicts. Deliberately NOT\n // `Promise.all` over a single try/catch — see rule 1 in the header.\n const [attendees, notes, transcript, recordings] = await Promise.all([\n attempt<readonly MeetingAttendee[]>(\"attendance\", () => repository.attendees(meetingId), []),\n attempt<readonly MeetingNote[]>(\"notes\", () => repository.notes(meetingId), []),\n attempt<readonly TranscriptSegment[]>(\n \"transcript\",\n () => repository.transcript(meetingId),\n [],\n ),\n attempt<readonly RecordingRecord[]>(\n \"recording\",\n () => repository.recordings(meetingId),\n [],\n ),\n ]);\n return { attendees, notes, transcript, recordings, restrictions };\n}\n\n/** The bundle a reader sees, from the parts. Grouping and ordering only (rule 3). */\nexport function assembleMeetingRecord(\n meeting: MeetingRecord,\n parts: MeetingRecordParts,\n): MeetingRecordBundle {\n const { attendees, notes, transcript, recordings, restrictions } = parts;\n return {\n meeting,\n attendees,\n summary: activeWrapUpSummary(notes),\n // 🚨 D20: THE WRAP-UP IS THE AUTHORITATIVE POST-MEETING SET. A window row the\n // wrap-up superseded is still in `notes` and still in the timeline below —\n // it is simply not listed a second time as a decision that was made.\n decisions: notes.filter((note) => note.kind === \"decision\" && note.supersededBy === null),\n actionItems: notes.filter(\n (note) => note.kind === \"action-item\" && note.supersededBy === null,\n ),\n liveNotes: notes\n .filter((note) => note.kind === \"note\" && note.role === null)\n // Window order, then creation order for the notes of one window.\n .slice()\n .sort((left, right) => {\n const a = left.window ?? Number.MAX_SAFE_INTEGER;\n const b = right.window ?? Number.MAX_SAFE_INTEGER;\n return a === b ? left.createdAt.localeCompare(right.createdAt) : a - b;\n }),\n transcript,\n // `recordings` is newest first; the record shows the latest attempt,\n // including a failed one — a recording that failed is a fact the reader\n // needs, not a row to hide.\n recording: recordings[0] ?? null,\n restrictions,\n names: learnNames(learnNames({}, attendees), transcript.map((line) => ({\n identity: line.identity,\n displayName: line.speaker,\n }))),\n };\n}\n\n/**\n * 🚨 **A READ THAT WAS REFUSED IS NOT AN EMPTY RESULT (MRI-A16).**\n *\n * On production 2026-09-09 a guest's record view said *\"No wrap-up was written\n * for this meeting\"* directly underneath a panel that had just told them, in\n * the same screen, that the notes are not readable from their account — and\n * *\"Nothing was recorded as a decision\"* beside a decision that exists\n * (MRI-C8). The page asserted an absence it had no way to know, which is worse\n * than saying nothing: a person who reads \"no wrap-up was written\" stops\n * looking for one.\n *\n * So no surface writes its own empty sentence any more. It asks here, and gets\n * either its absence sentence — earned, because the read SUCCEEDED and returned\n * nothing — or the refusal, said plainly.\n */\nexport const REFUSED_ABSENCE = \"This record is not shared with guests.\";\n\n/** Was this part of the record refused to this reader? */\nexport function wasRefused(\n bundle: MeetingRecordBundle,\n part: MeetingRecordRestriction[\"part\"],\n): boolean {\n return bundle.restrictions.some((restriction) => restriction.part === part);\n}\n\n/**\n * The sentence to show where a part of the record has nothing in it. NEVER an\n * absence claim over a read that was refused.\n */\nexport function absenceSentence(\n bundle: MeetingRecordBundle,\n part: MeetingRecordRestriction[\"part\"],\n absence: string,\n): string {\n return wasRefused(bundle, part) ? REFUSED_ABSENCE : absence;\n}\n\n/**\n * 🚨 **\"NO WRAP-UP WAS WRITTEN\" IS A CLAIM, AND FOR ~33 SECONDS IT WAS FALSE.**\n *\n * On production 2026-09-09 the HOST's record view said *\"No wrap-up was written\n * for this meeting\"* between 04:46:42 — the flip from the room to the record —\n * and 04:47:15, when the wrap-up landed. The read was permitted and genuinely\n * empty, so this is not the refusal class above; it is the OTHER way a screen\n * lies, by reading \"not here yet\" as \"never happened\". A person who reads *\"no\n * wrap-up was written\"* closes the tab.\n *\n * The meeting row already knows the difference, and it is not a guess: the\n * wrap-up is a durable job, `enqueue_wrap_up` stamps\n * `metadata.note_taker.intelligence.wrap_up` when it queues it, and the graph\n * action stamps `state: \"failed\"` when the pass refuses TERMINALLY. So:\n *\n * - a summary exists → `written`;\n * - the notes read was refused → `refused` (never an absence claim);\n * - the job is marked failed → `failed`, in the server's own words;\n * - the job is queued or running → `in-progress`, with a refresh affordance;\n * - nothing ever queued one → `absent`, which is the only EARNED absence.\n *\n * There is no timeout that flips `in-progress` into `absent`. A wrap-up that is\n * genuinely stuck says it is still being written — which is TRUE — rather than\n * inventing a verdict this browser has no way to reach.\n */\nexport type WrapUpStatus =\n | { readonly state: \"written\" }\n | { readonly state: \"in-progress\"; readonly message: string; readonly canRefresh: true }\n | { readonly state: \"failed\"; readonly message: string }\n /** Nobody spoke: a normal outcome, said plainly (not an alert). */\n | { readonly state: \"empty\"; readonly message: string }\n | { readonly state: \"refused\"; readonly message: string }\n | { readonly state: \"absent\"; readonly message: string };\n\n/** What `metadata.note_taker` holds, as far as this file cares. */\nfunction noteTakerMetadata(meeting: MeetingRecord): Record<string, unknown> {\n const raw = (meeting.metadata as Record<string, unknown>)[\"note_taker\"];\n return typeof raw === \"object\" && raw !== null && !Array.isArray(raw)\n ? (raw as Record<string, unknown>)\n : {};\n}\n\nconst WRAP_UP_IN_PROGRESS =\n \"The wrap-up is being written… The summary, the decisions and the action items are \" +\n \"produced from the whole transcript once the meeting ends, which takes a moment longer \" +\n \"than the meeting does.\";\n\nconst WRAP_UP_ABSENT =\n \"No wrap-up was written for this meeting. A summary is produced from the transcript \" +\n \"when the meeting ends and the note-taker was in the room.\";\n\n/** Where the wrap-up actually is. Never \"absent\" over a job that is running. */\nexport function wrapUpStatus(bundle: MeetingRecordBundle): WrapUpStatus {\n if (bundle.summary !== null) return { state: \"written\" };\n if (wasRefused(bundle, \"notes\")) return { state: \"refused\", message: REFUSED_ABSENCE };\n\n const noteTaker = noteTakerMetadata(bundle.meeting);\n const intelligenceRaw = noteTaker[\"intelligence\"];\n const intelligence: Record<string, unknown> =\n typeof intelligenceRaw === \"object\" && intelligenceRaw !== null && !Array.isArray(intelligenceRaw)\n ? (intelligenceRaw as Record<string, unknown>)\n : {};\n const wrapUpRaw = intelligence[\"wrap_up\"];\n const wrapUp: Record<string, unknown> =\n typeof wrapUpRaw === \"object\" && wrapUpRaw !== null && !Array.isArray(wrapUpRaw)\n ? (wrapUpRaw as Record<string, unknown>)\n : {};\n\n if (wrapUp[\"state\"] === \"empty\") {\n const reason = wrapUp[\"reason\"];\n return {\n state: \"empty\",\n message:\n typeof reason === \"string\" && reason.trim().length > 0\n ? reason.trim()\n : \"No one spoke in this meeting, so there is no summary.\",\n };\n }\n\n if (wrapUp[\"state\"] === \"failed\") {\n const reason = wrapUp[\"reason\"];\n return {\n state: \"failed\",\n // THE SERVER'S OWN SENTENCE, verbatim. It already names the remedy, and a\n // house sentence over it would hide the one thing worth reading.\n message:\n typeof reason === \"string\" && reason.trim().length > 0\n ? reason.trim()\n : \"The wrap-up pass failed and no summary was written. It is not retried \" +\n \"automatically; a host can end the meeting again to re-queue it.\",\n };\n }\n\n const queued =\n typeof wrapUp[\"run_id\"] === \"string\" ||\n typeof intelligence[\"wrap_up_run_id\"] === \"string\" ||\n wrapUp[\"state\"] === \"queued\" ||\n wrapUp[\"state\"] === \"running\";\n // A meeting the note-taker actually attended will get a wrap-up even if this\n // browser loaded the record before the enqueue stamp landed — that race is\n // the whole defect, so `ended + the note-taker was there` is enough.\n const attended =\n noteTaker[\"state\"] === \"ended\" ||\n noteTaker[\"state\"] === \"transcribing\" ||\n noteTaker[\"state\"] === \"handoff_pending\";\n\n if (hasEnded(bundle.meeting) && (queued || attended)) {\n return { state: \"in-progress\", message: WRAP_UP_IN_PROGRESS, canRefresh: true };\n }\n return { state: \"absent\", message: WRAP_UP_ABSENT };\n}\n\n/**\n * GROUP THE TRANSCRIPT THE WAY A PERSON READS IT: consecutive lines by one\n * speaker become one block with one timestamp. Reading thirty-two rows each\n * re-stamped with a name and a clock is how a transcript becomes unreadable.\n */\nexport function groupTranscript(\n segments: readonly TranscriptSegment[],\n names: NameBook = {},\n): readonly TranscriptBlock[] {\n const blocks: TranscriptBlock[] = [];\n for (const segment of segments) {\n const last = blocks[blocks.length - 1];\n // 🚨 GROUPED BY IDENTITY, not by the rendered name (MRI-A16). Two people\n // whose names nobody ever learned both render \"Unnamed guest\", and merging\n // their lines into one block would put words in the wrong mouth — the exact\n // failure the honest placeholder exists to avoid.\n if (last !== undefined && last.identity === segment.identity) {\n blocks[blocks.length - 1] = { ...last, lines: [...last.lines, segment] };\n continue;\n }\n blocks.push({\n id: segment.id,\n identity: segment.identity,\n // One resolution, shared with the live rail — so the record and the room\n // can never disagree about who said something.\n speaker: displayNameFor(names, segment.identity, segment.speaker),\n startedAt: segment.startedAt,\n lines: [segment],\n });\n }\n return blocks;\n}\n\n/**\n * WHAT THE RECORDING IS DOING, in a sentence — including the two states people\n * hit most and understand least. `processing` is not \"broken\" and `failed` is\n * never silence: both say what happened and what to do.\n */\nexport function recordingNotice(recording: RecordingRecord | null): string | null {\n if (recording === null) return null;\n switch (recording.state) {\n case \"available\":\n if (recording.retention?.phase !== \"warning\") return null;\n return `This recording expires on ${retentionDueDate(recording.retention.dueAt)}. The transcript and notes remain available.`;\n case \"expired\":\n return \"This recording has expired. The transcript and notes remain available.\";\n case \"processing\":\n case \"stopping\":\n return \"The recording is still being processed. It appears here on its own once the file lands — nothing is lost while it is being written.\";\n case \"recording\":\n case \"starting\":\n return \"This recording is still being written. It becomes playable once the meeting's egress finishes.\";\n case \"failed\":\n return recording.failureReason !== null && recording.failureReason.length > 0\n ? `The recording failed: ${recording.failureReason}`\n : \"The recording failed and no file was produced. The transcript below was written separately and is unaffected.\";\n default:\n return null;\n }\n}\n\nfunction retentionDueDate(value: string): string {\n const dueAt = new Date(value);\n return Number.isNaN(dueAt.getTime()) ? \"its scheduled retention date\" : dueAt.toLocaleString();\n}\n\n/** Only an `available` recording has a file to play. Nothing else pretends to. */\nexport function playableRecordingFileId(recording: RecordingRecord | null): string | null {\n if (recording === null) return null;\n return recording.state === \"available\" ? recording.fileId : null;\n}\n\n/**\n * A MEETING IS OVER WHEN THE SERVER SAYS SO. `ended_at` is stamped by\n * `POST /v1/meet/end` or by the auto-end evaluation (MRI-A6) — never by a\n * client noticing the room is empty.\n */\nexport function hasEnded(meeting: MeetingRecord | null): boolean {\n return meeting !== null && meeting.endedAt !== null;\n}\n\n/**\n * \"Thursday, 8 September 2026, 4:21 PM · 19 minutes\". One shape everywhere.\n *\n * THE SPAN IS `formatDurationMs`'s (2026-09-12). This body used to divide by\n * `60_000` and pluralise \"minute\" itself — a duration formatter living in a\n * PACKAGE, found the day `aidream/apps/shared` became a scan root. `@ai-matrx/\n * meet` already depends on `@ai-matrx/kit`, and `style: \"long\"` is precisely\n * this voice (\"19 minutes\", \"1 minute\"). Three things improve by adopting it:\n * a 40-second meeting read \"0 minutes\" and now reads \"40 seconds\" (the\n * confident zero the package refuses), a 19-minute-35-second meeting read\n * \"20 minutes\" and now reads \"19 minutes\" (prose asserts at-least, so it\n * floors), and a 90-minute meeting reads \"1 hour\" instead of \"90 minutes\".\n */\nexport function meetingWhen(meeting: MeetingRecord, locale?: string): string {\n const start = meeting.startedAt ?? meeting.scheduledFor;\n if (start === null) return \"Time not recorded\";\n const startedAt = new Date(start);\n if (Number.isNaN(startedAt.getTime())) return \"Time not recorded\";\n const when = startedAt.toLocaleString(locale, {\n weekday: \"long\",\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n if (meeting.endedAt === null || meeting.startedAt === null) return when;\n const endedAt = new Date(meeting.endedAt);\n if (Number.isNaN(endedAt.getTime())) return when;\n const elapsedMs = endedAt.getTime() - startedAt.getTime();\n return `${when} · ${formatDurationMs(elapsedMs, { style: \"long\" })}`;\n}\n\n/* ═══════════════════════════════════════════════════════════════════════════\n * MRF-1 — WHAT A GUEST MAY READ, AND HOW THEY READ IT\n *\n * 🚨 THE DEFECT THIS CLOSES. Everything above this line is correct and was\n * still the wrong product: a guest who attended a meeting, was admitted by the\n * host, spoke, and was transcribed got FOUR refusal sentences where the record\n * should have been. `anon` has no grant on the four org-scoped tables and\n * widening it is forbidden (MRI-A10) — a meeting link is not organization\n * membership — so the honest refusals were the only thing this file could say.\n *\n * Arman ruled on 2026-09-12 that guest access to the record is a CONFIGURATION\n * and never a question: a system default, an organization override, and a\n * per-meeting setting a host chooses. The server resolves it and serves the\n * shared subset at `GET /api/v1/meet/record`, authorized by the room token this\n * device already holds. So this half does three things and no more:\n *\n * 1. reads that route and turns its answer into the SAME `MeetingRecordBundle`\n * the member lane produces — so one screen renders both readers and they can\n * never drift into two products;\n * 2. states every part the level does NOT share as a `MeetingRecordRestriction`\n * with the server's own sentence, which is what keeps \"not shared\" and\n * \"nothing happened\" different things on screen;\n * 3. gives a host the control that sets it.\n *\n * What it does NOT do is decide anything. The level, the refusals and the\n * remedies are the server's words; a client that computed its own would be a\n * second authority on who may read a transcript.\n * ═══════════════════════════════════════════════════════════════════════════ */\n\n\n/** How much of a finished meeting a GUEST may read. Pinned with the server. */\nexport type GuestRecordAccess = \"none\" | \"summary\" | \"full\";\n\n/** Widest last. The ORDER is the comparison, exactly as it is on the server. */\nexport const GUEST_RECORD_ACCESS_LEVELS: readonly GuestRecordAccess[] = [\n \"none\",\n \"summary\",\n \"full\",\n];\n\n/**\n * What a HOST may choose for one meeting. `default` is not a fourth level — it\n * is the absence of a per-meeting choice, and it is the only way to un-choose.\n */\nexport type GuestRecordAccessSetting = \"default\" | GuestRecordAccess;\n\nexport const GUEST_RECORD_ACCESS_CHOICES: readonly GuestRecordAccessSetting[] = [\n \"default\",\n \"summary\",\n \"full\",\n \"none\",\n];\n\n/** `meet_meetings.metadata.guest_record_access`. No DDL; a setting on a row. */\nexport const GUEST_RECORD_ACCESS_METADATA_KEY = \"guest_record_access\";\n\n/** What a host chose for THIS meeting, read from the row they already have. */\nexport function guestRecordAccessSetting(\n meeting: MeetingRecord | null,\n): GuestRecordAccessSetting {\n const raw = (meeting?.metadata as Record<string, unknown> | undefined)?.[\n GUEST_RECORD_ACCESS_METADATA_KEY\n ];\n return GUEST_RECORD_ACCESS_LEVELS.includes(raw as GuestRecordAccess)\n ? (raw as GuestRecordAccess)\n : \"default\";\n}\n\n/** The sentence on the host's control. Plain English, never a token. */\nexport function guestRecordAccessLabel(setting: GuestRecordAccessSetting): string {\n switch (setting) {\n case \"default\":\n return \"Use my organization's setting\";\n case \"summary\":\n return \"Summary, decisions and action items\";\n case \"full\":\n return \"Everything, including the transcript\";\n case \"none\":\n return \"Nothing — keep the record internal\";\n }\n}\n\n/** What each choice actually means for the people who were in the room. */\nexport function guestRecordAccessExplanation(setting: GuestRecordAccessSetting): string {\n switch (setting) {\n case \"default\":\n return \"Guests who attended read whatever your organization shares by default. An administrator sets that once, for every meeting nobody thinks about.\";\n case \"summary\":\n return \"Guests who attended read the wrap-up summary, the decisions, the action items, the notes taken as it happened, and who was here. They do not get the word-for-word transcript or the recording.\";\n case \"full\":\n return \"Guests who attended read everything this meeting produced, including the word-for-word transcript of what each person said.\";\n case \"none\":\n return \"Guests who attended are told plainly that this meeting's record is kept inside your organization. They see nothing of it.\";\n }\n}\n\n/** What came back from the server, so a surface never has to infer a level. */\nexport interface GuestRecordAccessResolution {\n readonly level: GuestRecordAccess;\n /** `meeting` when a host chose for this one; `organization` otherwise. */\n readonly source: \"meeting\" | \"organization\";\n}\n\n/** What the parts of the record the level does not share are called, and why. */\nconst NOT_SHARED: Readonly<Record<MeetingRecordRestriction[\"part\"], string>> = {\n transcript:\n \"The word-for-word transcript of this meeting is not shared with guests.\",\n notes: \"The notes, decisions and action items are not shared with guests.\",\n attendance: \"Who attended is not shared with guests.\",\n recording: \"The recording of this meeting is not shared with guests.\",\n};\n\nconst NOT_SHARED_REMEDY =\n \"The organization that held this meeting decided how much of its record guests can read. \" +\n \"If you need more, ask whoever invited you — they can send it to you, or change the \" +\n \"setting for this meeting.\";\n\nconst RESTRICTION_PART_NAME: Readonly<Record<MeetingRecordRestriction[\"part\"], string>> = {\n attendance: \"attendance\",\n notes: \"notes and decisions\",\n transcript: \"word-for-word transcript\",\n recording: \"recording\",\n};\n\nfunction joinPartNames(parts: readonly MeetingRecordRestriction[\"part\"][]): string {\n const names = parts.map((part) => RESTRICTION_PART_NAME[part]);\n if (names.length < 2) return names[0] ?? \"this part of the record\";\n if (names.length === 2) return `${names[0]} and ${names[1]}`;\n return `${names.slice(0, -1).join(\", \")}, and ${names[names.length - 1]}`;\n}\n\n/**\n * Repeated policy refusals are one access state, not four alarming cards.\n *\n * The individual rows remain durable data for absence handling in their own\n * sections. This projection groups only rows with the exact same remedy, so a\n * distinct transport or integrity failure remains visible on its own.\n */\nexport function summarizeMeetingRecordRestrictions(\n restrictions: readonly MeetingRecordRestriction[],\n): readonly MeetingRecordRestrictionNotice[] {\n const grouped = new Map<string, MeetingRecordRestriction[]>();\n for (const restriction of restrictions) {\n const rows = grouped.get(restriction.remedy) ?? [];\n rows.push(restriction);\n grouped.set(restriction.remedy, rows);\n }\n return [...grouped.values()].map((rows) => {\n if (rows.length === 1) {\n const row = rows[0]!;\n return { parts: [row.part], message: row.message, remedy: row.remedy };\n }\n return {\n parts: rows.map((row) => row.part),\n message: `Some parts of this meeting record are not available to this reader: ${joinPartNames(rows.map((row) => row.part))}.`,\n remedy: rows[0]!.remedy,\n };\n });\n}\n\n/**\n * Read the record the server shares with THIS guest.\n *\n * `roomToken` is the pass this device kept from the meeting\n * (`TokenClient.remembered`). It is presented in a header, never appended to\n * the URL. A guest with no pass, an expired pass, or a level of `none` gets the\n * server's refusal — as restrictions on a real bundle, so the screen says what\n * it cannot show instead of collapsing into an error page.\n */\nexport async function loadGuestMeetingRecord(args: {\n api: MeetApi;\n repository: MeetRepository;\n meetingId: MeetingId;\n meeting: MeetingRecord;\n roomToken: RoomToken | null;\n /**\n * A SIGNED-IN reader the database refused (from another organization, or a\n * guest who later created an account). Their session stands in for the\n * pass: the server reads their `user:<id>` admission or the attendance they\n * claimed, and shares what guests may read (2026-10-01).\n */\n session?: boolean;\n /**\n * THE SESSION-AWARE RECORD (CORE-DESIGN §2.3): one run of a series. Omitted = the server's\n * default (a one-off meeting's whole record; a series' latest ended run).\n */\n sessionId?: string | null;\n}): Promise<MeetingRecordBundle> {\n const { api, repository, meetingId, meeting, roomToken } = args;\n const session = args.session === true;\n\n if (roomToken?.meetingPass == null && !session) {\n // NOT A GUESS AND NOT A FETCH. This browser has no pass for this meeting,\n // which the server would answer with the same sentence — asking it first\n // would only add a round trip and a 403 in the console.\n return refusedBundle(\n meeting,\n \"This meeting's record is only readable by the people who were in it.\",\n \"Open the meeting from the link you were invited with, in the same browser you used \" +\n \"to attend — that is what identifies you to this meeting. Signing in with an \" +\n \"account in the organization that held the meeting also works.\",\n );\n }\n\n let response: Response;\n try {\n response = await api.request({\n path:\n `${MEET_ROUTES.record}?meeting_id=${encodeURIComponent(meetingId)}` +\n (args.sessionId ? `&session_id=${encodeURIComponent(args.sessionId)}` : \"\"),\n method: \"GET\",\n // A GUEST HAS NO SESSION AND IS STILL A FIRST-CLASS READER (D6).\n requireAuth: session,\n operation: \"meet.record\",\n headers:\n roomToken?.meetingPass != null ? { [MEETING_PASS_HEADER]: roomToken.meetingPass } : {},\n });\n } catch (error) {\n // A transport wobble is not a permission decision and must not read as one.\n return refusedBundle(\n meeting,\n `This meeting's record could not be read (${(error as Error).message}).`,\n (error as { remedy?: string }).remedy ??\n \"This is a connection failure, not a decision about you. Reload the page.\",\n );\n }\n\n if (!response.ok) {\n const refusal = await readRefusal(response);\n return refusedBundle(meeting, refusal.message, refusal.remedy);\n }\n\n const body = (await response.json()) as Record<string, unknown>;\n const payload = (\n typeof body[\"data\"] === \"object\" && body[\"data\"] !== null ? body[\"data\"] : body\n ) as GuestRecordPayload;\n\n const rows = (key: keyof GuestRecordPayload): Record<string, unknown>[] =>\n Array.isArray(payload[key]) ? (payload[key] as Record<string, unknown>[]) : [];\n\n const attendees = rows(\"attendees\").map((row) => repository.projectAttendee(row));\n const notes = rows(\"notes\").map((row) => repository.projectNote(row));\n const transcript = rows(\"transcript\").map((row) => repository.projectSegment(row));\n const recordings = rows(\"recordings\").map((row) => repository.projectRecording(row));\n\n // 🚨 THE RESTRICTIONS COME FROM `shared`, NOT FROM AN EMPTY ARRAY. A level of\n // `summary` and a meeting where nobody spoke both produce zero transcript\n // rows, and telling the reader they are the same thing is the MRI-A16 defect.\n const shared = (payload.shared ?? {}) as Record<string, boolean>;\n const restrictions: MeetingRecordRestriction[] = [];\n for (const part of [\"attendance\", \"notes\", \"transcript\", \"recording\"] as const) {\n if (shared[part] === false) {\n restrictions.push({ part, message: NOT_SHARED[part], remedy: NOT_SHARED_REMEDY });\n }\n }\n\n return {\n meeting,\n attendees,\n summary: activeWrapUpSummary(notes),\n decisions: notes.filter((note) => note.kind === \"decision\" && note.supersededBy === null),\n actionItems: notes.filter(\n (note) => note.kind === \"action-item\" && note.supersededBy === null,\n ),\n liveNotes: notes\n .filter((note) => note.kind === \"note\" && note.role === null)\n .slice()\n .sort((left, right) => {\n const a = left.window ?? Number.MAX_SAFE_INTEGER;\n const b = right.window ?? Number.MAX_SAFE_INTEGER;\n return a === b ? left.createdAt.localeCompare(right.createdAt) : a - b;\n }),\n transcript,\n recording: recordings[0] ?? null,\n restrictions,\n names: learnNames(\n learnNames({}, attendees),\n transcript.map((line) => ({ identity: line.identity, displayName: line.speaker })),\n ),\n };\n}\n\n/** The row shapes the route returns — the same snake_case the tables use. */\ninterface GuestRecordPayload {\n readonly access?: GuestRecordAccess;\n readonly access_source?: \"meeting\" | \"organization\";\n readonly shared?: Record<string, boolean>;\n readonly attendees?: unknown;\n readonly notes?: unknown;\n readonly transcript?: unknown;\n readonly recordings?: unknown;\n readonly recording_notice?: string;\n}\n\n/**\n * THE SERVER'S OWN WORDS, verbatim. Every meet refusal carries\n * `{message, remedy}`, and a house sentence written over it would hide the one\n * thing worth reading — including the difference between \"your pass expired,\n * open the link again\" and \"this record is not shared with guests\".\n */\nasync function readRefusal(\n response: Response,\n): Promise<{ message: string; remedy: string }> {\n try {\n const body = (await response.json()) as Record<string, unknown>;\n const detail = (\n typeof body[\"detail\"] === \"object\" && body[\"detail\"] !== null ? body[\"detail\"] : body\n ) as Record<string, unknown>;\n const message = detail[\"message\"];\n const remedy = detail[\"remedy\"];\n if (typeof message === \"string\" && message.length > 0) {\n return {\n message,\n remedy:\n typeof remedy === \"string\" && remedy.length > 0\n ? remedy\n : \"Ask whoever hosted the meeting for what you need.\",\n };\n }\n } catch {\n /* falls through to the status sentence below */\n }\n return {\n message: `This meeting's record could not be read (the server answered ${response.status}).`,\n remedy:\n \"This is not a decision about you — it is a failure to reach the record. Reload the \" +\n \"page; if it repeats, tell whoever hosted the meeting.\",\n };\n}\n\n/** A real bundle whose every part is refused, with ONE reason said once. */\nfunction refusedBundle(\n meeting: MeetingRecord,\n message: string,\n remedy: string,\n): MeetingRecordBundle {\n return {\n meeting,\n attendees: [],\n summary: null,\n decisions: [],\n actionItems: [],\n liveNotes: [],\n transcript: [],\n recording: null,\n // Every empty collection below is a refusal, never evidence of absence.\n // The record renderer asks its own part before it writes an empty-state\n // sentence, so omitting attendance here made a denied roster read as\n // “Nobody attended.” `summarizeMeetingRecordRestrictions` still presents\n // this shared server reason once at the top of the record.\n restrictions: ([\"attendance\", \"notes\", \"transcript\", \"recording\"] as const).map(\n (part) => ({ part, message, remedy }),\n ),\n names: {},\n };\n}\n\n/**\n * A HOST chooses what guests may read of THIS meeting.\n *\n * Host power is the SERVER's decision and it re-checks it; this function exists\n * so the control has one call to make, and it returns what the choice RESOLVES\n * to so the screen never has to guess what \"my organization's setting\" means\n * today.\n */\nexport async function setGuestRecordAccess(args: {\n api: MeetApi;\n meetingId: MeetingId;\n organizationId: OrganizationId;\n access: GuestRecordAccessSetting;\n}): Promise<{\n readonly setting: GuestRecordAccessSetting;\n readonly effective: GuestRecordAccessResolution;\n}> {\n const response = await args.api.request({\n path: MEET_ROUTES.recordAccess,\n method: \"POST\",\n requireAuth: true,\n operation: \"meet.record.access\",\n body: {\n meeting_id: args.meetingId,\n organization_id: args.organizationId,\n access: args.access,\n },\n });\n if (!response.ok) {\n const refusal = await readRefusal(response);\n throw new MeetError(\"forbidden\", refusal.message, refusal.remedy);\n }\n const body = (await response.json()) as Record<string, unknown>;\n const payload = (\n typeof body[\"data\"] === \"object\" && body[\"data\"] !== null ? body[\"data\"] : body\n ) as {\n setting?: GuestRecordAccessSetting;\n effective?: GuestRecordAccessResolution;\n };\n return {\n setting: payload.setting ?? args.access,\n effective: payload.effective ?? { level: \"summary\", source: \"organization\" },\n };\n}\n\n/** What a guest is told when the level shares nothing. Rendered, never hidden. */\nexport const GUEST_RECORD_NOT_SHARED = NOT_SHARED;\n","/**\n * ONE MEETING RECORD PER READER PER TAB — read once, kept current by one\n * ref-counted channel (0.7.79).\n *\n * 🚨 THE DEFECT THIS CLOSES. `useMeetingRecord` held the bundle in its own\n * component state and read all four parts (`meet_participants`, `meet_notes`,\n * `meet_transcript_segments`, `meet_recordings`) in a mount effect. A host that\n * hides a view without unmounting its data — a board tile asleep under React\n * `<Activity>`, which tears every effect down and runs it again on wake — read\n * the whole record again on every wake, on a Remove + Undo, and once more for\n * every second view of the same meeting (the record tab, the meeting's agent\n * surface, five \"meeting notes\" tiles). The view owned the data.\n *\n * THE SHAPE. One entry per (reader, meeting). The first view reads; every later\n * view — the same view after a wake or a remount included — renders the entry\n * and reads nothing. A read already in flight is joined. A forced read (Retry,\n * a reconnect) supersedes it, and an answer that lands after a newer read was\n * asked is DROPPED (the late-answer guard), so an old answer can never paint\n * over a newer one.\n *\n * LIVE WHILE HELD. A member's entry (a direct read, kept as its four parts) is\n * patched row by row from the database's change feed for the four tables, then\n * assembled again — one row, never four reads. The channel opens on the first\n * holder and closes `MEETING_RECORD_GRACE_MS` after the last one lets go, so a\n * wake or an Undo inside the grace costs nothing. Past the grace nothing\n * watched the meeting, so the entry is marked stale and the next view reads it\n * once. A guest's record arrives through the server as a finished bundle; it\n * has no change feed and is kept as it is.\n *\n * Framework-free on purpose: `react/hooks.ts` is one reader of it.\n */\n\nimport { assembleMeetingRecord, type MeetingRecordBundle, type MeetingRecordParts } from \"./record\";\nimport type { MeetingRecord } from \"./types\";\n\n/** How long a meeting's channel and kept record outlive the last view of it. */\nexport const MEETING_RECORD_GRACE_MS = 60_000;\n\nexport interface MeetingRecordFailure {\n readonly message: string;\n readonly remedy: string;\n}\n\nexport interface MeetingRecordState {\n readonly loading: boolean;\n readonly record: MeetingRecordBundle | null;\n /** A failure that stopped the WHOLE record loading (never kept as an answer). */\n readonly failure: MeetingRecordFailure | null;\n}\n\n/** What a read produced: the four parts (patchable), or a finished bundle (a guest's). */\nexport type MeetingRecordAnswer =\n | { readonly kind: \"parts\"; readonly meeting: MeetingRecord; readonly parts: MeetingRecordParts }\n | { readonly kind: \"bundle\"; readonly bundle: MeetingRecordBundle };\n\n/** One changed row, already projected by the repository. `null` + `removedId` = gone. */\nexport type MeetingRecordChange =\n | { readonly part: \"transcript\"; readonly row: MeetingRecordParts[\"transcript\"][number] | null; readonly removedId?: string | undefined }\n | { readonly part: \"notes\"; readonly row: MeetingRecordParts[\"notes\"][number] | null; readonly removedId?: string | undefined }\n | { readonly part: \"attendance\"; readonly row: MeetingRecordParts[\"attendees\"][number] | null; readonly removedId?: string | undefined }\n | { readonly part: \"recording\"; readonly row: MeetingRecordParts[\"recordings\"][number] | null; readonly removedId?: string | undefined };\n\nexport interface MeetingRecordCache {\n /** The kept state for `key` (stable until it changes). */\n state(key: string | null): MeetingRecordState;\n subscribe(listener: () => void): () => void;\n /**\n * Read unless the entry is kept and current (or a read is in flight — joined).\n * `force` reads anyway and supersedes a read in flight. Never rejects: a\n * failure is in the state.\n */\n ensure(key: string, read: () => Promise<MeetingRecordAnswer>, options?: { force?: boolean }): Promise<void>;\n /** The meeting row moved (a rename, it ended): assemble again — no read. */\n setMeeting(key: string, meeting: MeetingRecord): void;\n /** Apply one changed row from the change feed — no read. */\n apply(key: string, change: MeetingRecordChange): void;\n /**\n * Hold the live half: `open` on the first holder, its close a grace after the\n * last. `owner` is what the channel runs on (the realtime manager): a new\n * owner means the old one's channels are gone, so it opens again.\n */\n hold(key: string, open: () => () => void, owner?: unknown): () => void;\n /** Tests only. */\n reset(): void;\n}\n\ninterface Entry {\n state: MeetingRecordState;\n meeting: MeetingRecord | null;\n parts: MeetingRecordParts | null;\n fresh: boolean;\n seq: number;\n inflight: Promise<void> | null;\n holders: number;\n close: (() => void) | null;\n owner: unknown;\n grace: ReturnType<typeof setTimeout> | null;\n}\n\nconst INITIAL: MeetingRecordState = { loading: true, record: null, failure: null };\n\nfunction upsert<T extends { readonly id: string }>(\n rows: readonly T[],\n row: T | null,\n removedId: string | undefined,\n order: (a: T, b: T) => number,\n): readonly T[] {\n const id = row?.id ?? removedId;\n if (id === undefined) return rows;\n const rest = rows.filter((existing) => existing.id !== id);\n return row === null ? rest : [...rest, row].sort(order);\n}\n\nconst byId = (a: { id: string }, b: { id: string }) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);\nconst ascending = (a: string | null, b: string | null) =>\n a === b ? 0 : a === null ? 1 : b === null ? -1 : a < b ? -1 : 1;\n\nexport function createMeetingRecordCache(\n options: { graceMs?: number } = {},\n): MeetingRecordCache {\n const graceMs = options.graceMs ?? MEETING_RECORD_GRACE_MS;\n const entries = new Map<string, Entry>();\n const listeners = new Set<() => void>();\n const notify = () => {\n for (const listener of listeners) listener();\n };\n const entryOf = (key: string): Entry => {\n let entry = entries.get(key);\n if (!entry) {\n entry = {\n state: INITIAL,\n meeting: null,\n parts: null,\n fresh: false,\n seq: 0,\n inflight: null,\n holders: 0,\n close: null,\n owner: null,\n grace: null,\n };\n entries.set(key, entry);\n }\n return entry;\n };\n const assemble = (entry: Entry) => {\n if (entry.meeting === null || entry.parts === null) return;\n entry.state = { loading: false, failure: null, record: assembleMeetingRecord(entry.meeting, entry.parts) };\n notify();\n };\n\n return {\n state: (key) => (key === null ? INITIAL : (entries.get(key)?.state ?? INITIAL)),\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n ensure(key, read, { force = false } = {}) {\n const entry = entryOf(key);\n if (!force) {\n if (entry.inflight !== null) return entry.inflight;\n if (entry.fresh) return Promise.resolve();\n }\n const seq = ++entry.seq;\n if (entry.state.failure !== null) {\n // Retrying after a failure says it is loading again; a kept record stays on screen.\n entry.state = { ...entry.state, loading: true, failure: null };\n notify();\n }\n const request = read().then(\n (answer) => {\n if (seq !== entry.seq) return; // a newer read was asked: this answer is old\n entry.fresh = true;\n if (answer.kind === \"parts\") {\n entry.meeting = answer.meeting;\n entry.parts = answer.parts;\n assemble(entry);\n } else {\n entry.meeting = answer.bundle.meeting;\n entry.parts = null;\n entry.state = { loading: false, failure: null, record: answer.bundle };\n notify();\n }\n },\n (thrown: unknown) => {\n if (seq !== entry.seq) return;\n // A failure is never kept as the answer: the next view (or Retry) asks again.\n entry.fresh = false;\n entry.state = {\n loading: false,\n record: entry.state.record,\n failure: {\n message: (thrown as Error)?.message ?? String(thrown),\n remedy:\n (thrown as { remedy?: string })?.remedy ??\n \"The meeting record could not be read. Reload the page; nothing was changed.\",\n },\n };\n notify();\n },\n );\n const tracked = request.finally(() => {\n if (entry.inflight === tracked) entry.inflight = null;\n });\n entry.inflight = tracked;\n return tracked;\n },\n setMeeting(key, meeting) {\n const entry = entries.get(key);\n if (!entry || entry.meeting === meeting) return;\n if (entry.parts !== null) {\n entry.meeting = meeting;\n assemble(entry);\n } else if (entry.state.record !== null) {\n entry.meeting = meeting;\n entry.state = { ...entry.state, record: { ...entry.state.record, meeting } };\n notify();\n }\n },\n apply(key, change) {\n const entry = entries.get(key);\n const parts = entry?.parts;\n if (!entry || !parts) return;\n // A part this reader was refused stays refused: a row it cannot read is not news to it.\n const refused = parts.restrictions.some((r) => r.part === change.part);\n if (refused) return;\n switch (change.part) {\n case \"transcript\":\n entry.parts = {\n ...parts,\n transcript: upsert(parts.transcript, change.row, change.removedId, (a, b) =>\n ascending(a.startedAt, b.startedAt) || byId(a, b),\n ),\n };\n break;\n case \"notes\":\n entry.parts = {\n ...parts,\n notes: upsert(parts.notes, change.row, change.removedId, (a, b) =>\n ascending(a.createdAt, b.createdAt) || byId(a, b),\n ),\n };\n break;\n case \"attendance\":\n entry.parts = {\n ...parts,\n attendees: upsert(parts.attendees, change.row, change.removedId, (a, b) =>\n ascending(a.joinedAt, b.joinedAt) || byId(a, b),\n ),\n };\n break;\n case \"recording\":\n entry.parts = {\n ...parts,\n // Newest first, as the read orders them.\n recordings: upsert(parts.recordings, change.row, change.removedId, (a, b) =>\n ascending(b.startedAt, a.startedAt) || byId(a, b),\n ),\n };\n break;\n }\n assemble(entry);\n },\n hold(key, open, owner = null) {\n const entry = entryOf(key);\n if (entry.grace !== null) {\n clearTimeout(entry.grace);\n entry.grace = null;\n }\n if (entry.close !== null && entry.owner !== owner) {\n entry.close();\n entry.close = null;\n }\n if (entry.close === null) {\n entry.close = open();\n entry.owner = owner;\n }\n entry.holders += 1;\n let released = false;\n return () => {\n if (released) return;\n released = true;\n entry.holders -= 1;\n if (entry.holders > 0) return;\n entry.grace = setTimeout(() => {\n entry.grace = null;\n if (entry.holders > 0) return;\n entry.close?.();\n entry.close = null;\n // Nothing watched the meeting from here on: the next view reads it once.\n entry.fresh = false;\n }, graceMs);\n };\n },\n reset() {\n for (const entry of entries.values()) {\n if (entry.grace !== null) clearTimeout(entry.grace);\n entry.close?.();\n }\n entries.clear();\n notify();\n },\n };\n}\n","/**\n * The reaction ids every skin offers (CORE-DESIGN §5.3). The id IS the emoji\n * the wire carries; a skin may draw its own art for each, never a different set.\n */\nexport const MEET_REACTIONS: readonly string[] = [\"💖\", \"👍\", \"🎉\", \"👏\", \"😂\", \"😮\", \"😢\", \"🤔\", \"👎\"];\n","/**\n * COLLAB AND CAPTION LOGIC, HEADLESS (CORE-DESIGN §5.3, step S1).\n *\n * What used to live inside the React hooks of the in-room activities: which\n * activities a person has, when the Activities door deserves a dot, the act /\n * move-to-breakout calls with their sentences, the caption-translation work\n * queue, and the one-shot panel and language requests on the store. Pure or\n * store-shaped, so every skin reads the same answers; the `/react` hooks are\n * thin selectors over these.\n */\n\nimport type { CollabPanel, CollabSnapshot } from \"./collab\";\nimport type { CollabRequest, CollabResult } from \"./in-room\";\nimport type { MeetSnapshot } from \"./store\";\n\n/** The activities this person has, in the order Meet lists them. */\nexport function availableActivities(collab: CollabSnapshot | null, isHost: boolean): CollabPanel[] {\n const settings = collab?.settings ?? null;\n if (settings === null) return [];\n const out: CollabPanel[] = [];\n if (settings.whiteboardEnabled) out.push(\"whiteboard\");\n // A participant sees Polls once a poll exists for them; a host always (to create one).\n if (settings.pollsEnabled && (isHost || (collab?.polls.length ?? 0) > 0)) out.push(\"polls\");\n if (settings.qaEnabled) out.push(\"qa\");\n // Breakouts: hosts run them; a participant needs the panel only when rooms are open.\n if (settings.breakoutsEnabled && (isHost || collab?.breakout?.state === \"open\")) out.push(\"breakouts\");\n return out;\n}\n\n/** Something worth a dot on the Activities button: a live poll unanswered, a new question for a host. */\nexport function activityAttention(collab: CollabSnapshot | null, isHost: boolean): boolean {\n if (collab === null) return false;\n if (collab.polls.some((poll) => poll.state === \"live\" && poll.myVote.length === 0 && !isHost)) return true;\n if (isHost && collab.questions.some((q) => q.state === \"open\")) return true;\n return false;\n}\n\nexport interface CollabRuntime {\n readonly inRoom: { collab(request: CollabRequest): Promise<CollabResult>; loadCollab?(): Promise<void> };\n readonly engine: { moveToBreakout(roomId: string | null): Promise<unknown> };\n}\n\n/** Perform one act; resolves with the server's sentence, or its refusal (never throws). */\nexport async function performCollab(\n runtime: CollabRuntime | null,\n request: CollabRequest,\n): Promise<{ ok: boolean; message: string; itemId?: string | null }> {\n if (runtime === null) return { ok: false, message: \"This meeting is not connected.\" };\n try {\n const result = await runtime.inRoom.collab(request);\n return { ok: true, message: result.message, itemId: result.itemId };\n } catch (error) {\n return { ok: false, message: (error as { message?: string }).message ?? \"That did not work.\" };\n }\n}\n\n/** Go to a breakout room (`null` = the main room), with the sentence to show. */\nexport async function moveToRoom(\n runtime: CollabRuntime | null,\n roomId: string | null,\n): Promise<{ ok: boolean; message: string }> {\n if (runtime === null) return { ok: false, message: \"This meeting is not connected.\" };\n try {\n await runtime.engine.moveToBreakout(roomId);\n // A room's packets only reach people already in it, so a person arriving mid-countdown (or\n // returning to the main room) re-reads the plan: they must see the closing countdown too.\n await runtime.inRoom.loadCollab?.().catch(() => undefined);\n return { ok: true, message: roomId === null ? \"Back in the main room.\" : \"Joined the breakout room.\" };\n } catch (error) {\n return { ok: false, message: (error as { message?: string }).message ?? \"That room could not be joined.\" };\n }\n}\n\nexport interface CollabStoreLike {\n snapshot(): MeetSnapshot;\n patch(next: Partial<MeetSnapshot>): void;\n}\n\n/** Finished caption lines that still need a translation into `language`. */\nexport function untranslatedLines(\n lines: readonly { id: string; text: string; final: boolean }[],\n translations: Readonly<Record<string, string>>,\n): { id: string; text: string }[] {\n return lines\n .filter((line) => line.final && translations[line.id] === undefined)\n .map(({ id, text }) => ({ id, text }));\n}\n\n/** The next batch to send: never a line already asked for in this language, at most 20. */\nexport function nextTranslationBatch(\n pending: readonly { id: string; text: string }[],\n language: string,\n asked: Set<string>,\n): { id: string; text: string }[] {\n const batch = pending.filter((line) => !asked.has(`${language}:${line.id}`)).slice(-20);\n for (const line of batch) asked.add(`${language}:${line.id}`);\n return batch;\n}\n\n/** The viewer chose a caption language (or `null` = as spoken): start over in it. */\nexport function setCaptionLanguage(store: CollabStoreLike, next: string | null): void {\n store.patch({\n collab: {\n ...store.snapshot().collab,\n captionLanguage: next,\n captionTranslations: {},\n captionTranslationError: null,\n },\n });\n}\n\n/** Take (and clear) the one-shot \"open this panel\" request, if any. */\nexport function takePanelRequest(store: CollabStoreLike): CollabPanel | null {\n const request = store.snapshot().collab.panelRequest;\n if (request === null) return null;\n store.patch({ collab: { ...store.snapshot().collab, panelRequest: null } });\n return request.panel;\n}\n\n/** Take (and clear) the one-shot \"move to this breakout room\" request, if any. */\nexport function takeMoveRequest(store: CollabStoreLike): { roomId: string | null } | null {\n const request = store.snapshot().collab.moveRequest;\n if (request === null) return null;\n store.patch({ collab: { ...store.snapshot().collab, moveRequest: null } });\n return { roomId: request.roomId };\n}\n","/**\n * THE OBSERVATION CONTRACT, DERIVED FROM TODAY'S STORE (CORE-DESIGN §7 step S0).\n *\n * The real-browser scenario harness reads a meeting through a small set of\n * `data-meet-*` attributes (common-docs `meet/duplicates/HARNESS-CONTRACT.md`,\n * owned by the harness lane). This module turns the store's `MeetSnapshot`\n * into exactly those values — pure, framework-free, rendered ONCE by the\n * package (`useCoreRootProps()` / `<MeetRoot>`), never per skin.\n *\n * S0 rule: TODAY'S VALUES ONLY. Every value here is what the person actually\n * sees on today's screens, including today's defects — a `failed` or `left`\n * room renders the pre-join screen, so its phase is `prejoin`, and the harness\n * records that as the defect it is. A value the store does not know (host\n * presence before joining, autoplay blocking) is OMITTED rather than guessed:\n * an absent attribute reads as \"unknown\", a guessed one would lie.\n */\n\nimport type { DevicePreview, LocalTrackState, PermissionReading } from \"./attendance\";\nimport { deviceNoticeCode, deviceUnavailableReason } from \"./media-supervisor\";\nimport type { ConnectionRegion, MeetSnapshot } from \"./store\";\nimport { isLivePhase, isWaitingPhase, reasonOf, type MeetPhase } from \"./phase\";\nimport { effectiveRole, effectiveStanding } from \"./store\";\nimport { consentPending, isLockedFor, mayAdmitNow } from \"./live-policy\";\nimport { isLinkFollower } from \"./types\";\nimport type {\n ConnectionQuality,\n MeetParticipant,\n MeetingRecord,\n ParticipantRole,\n ParticipantStanding,\n} from \"./types\";\n\n/**\n * `data-meet-role` is the HARNESS CONTRACT's vocabulary (host · cohost · participant · guest):\n * a link follower whose role is `participant` renders `guest` (CORE-DESIGN §2.1); `presenter` /\n * `viewer` render `participant` until the contract adopts them (§3.7 C3).\n */\nexport type ObservedRole = \"host\" | \"cohost\" | \"presenter\" | \"participant\" | \"guest\";\n\nexport function observedRole(role: ParticipantRole, standing: ParticipantStanding | null | undefined): ObservedRole {\n if (role === \"host\" || role === \"cohost\" || role === \"presenter\") return role;\n return isLinkFollower(standing) ? \"guest\" : \"participant\";\n}\n\n/** `data-meet-phase` — the core's phase IS the contract value (S6a: no translation layer). */\nexport type ObservedPhase = MeetPhase;\n\nexport type ObservedConnection = ConnectionRegion;\n\nexport interface MeetObservation {\n readonly phase: ObservedPhase;\n readonly reason: string | null;\n readonly role: ObservedRole | \"none\";\n readonly connection: ObservedConnection;\n /** null = this tab does not know (it is not in the room). */\n readonly hostPresent: boolean | null;\n readonly lobbyCount: number;\n readonly recording: boolean;\n /** null = today's store does not track autoplay blocking. */\n readonly audioBlocked: boolean | null;\n /** null outside `joining` / `in_call` — no device is in play there today. */\n readonly camera: string | null;\n readonly microphone: string | null;\n}\n\nexport interface ObserveInput {\n readonly snapshot: MeetSnapshot | null;\n /** The row the consumer resolved, when the store has none yet (the guest lane). */\n readonly meeting?: MeetingRecord | null | undefined;\n}\n\n/**\n * The store's device state → the contract's `camera` / `microphone` value (CORE-DESIGN §3.2).\n * The reason comes from the media supervisor (S6c), which reads the browser's permission store: a\n * kind the browser denies is `unavailable:denied` even when nothing tried to open it.\n */\nexport function observedDevice(\n track: LocalTrackState,\n permission: PermissionReading = \"unknown\",\n held: \"locked\" | \"muted_by_host\" | null = null,\n): string {\n if (held === \"locked\") return \"locked\";\n if (track.enabled) return \"on\";\n if (held === \"muted_by_host\") return \"muted_by_host\";\n const reason = deviceUnavailableReason(track, permission);\n return reason === null ? \"muted_by_me\" : `unavailable:${reason}`;\n}\n\n/**\n * The pre-join check → the contract's `camera` / `microphone` value (P1 `perm-*`, `device-none-found`,\n * `device-busy`). `prompting` = the browser is asking; null = nothing was tried and nothing is known.\n */\nexport function observedPreview(preview: DevicePreview, permission: PermissionReading = \"unknown\"): string | null {\n if (permission === \"denied\") return \"unavailable:denied\";\n switch (preview.state) {\n case \"on\":\n return \"on\";\n case \"off\":\n return \"muted_by_me\";\n case \"prompting\":\n return \"prompting\";\n case \"unavailable\":\n return `unavailable:${preview.unavailable?.reason ?? \"failed\"}`;\n default:\n return null;\n }\n}\n\n/** Does this snapshot's meeting row say it is over? */\nfunction endedRecord(snapshot: MeetSnapshot | null, given: MeetingRecord | null): boolean {\n const record = snapshot?.meeting ?? given;\n return record !== null && record.endedAt !== null;\n}\n\n/**\n * The phase the person is looking at: the store's phase, verbatim (CORE-DESIGN §3.1). The one\n * derivation left is the ended meeting: a row that says it is over renders `ended` for anyone not\n * in a live connection, whichever carrier delivered the ending first.\n */\nexport function observedPhase(input: ObserveInput): ObservedPhase {\n const { snapshot } = input;\n if (snapshot === null) return \"resolving\";\n const phase = snapshot.phase;\n if (endedRecord(snapshot, input.meeting ?? null) && !isLivePhase(phase) && !isCallPhase(phase)) return \"ended\";\n return phase;\n}\n\nfunction isCallPhase(phase: MeetPhase): boolean {\n return phase.startsWith(\"ringing_\") || phase.startsWith(\"call_\") || phase === \"answered_elsewhere\";\n}\n\nconst ADMITTER_ROLES: ReadonlySet<ParticipantRole> = new Set([\"host\", \"cohost\"]);\n\nexport function observe(input: ObserveInput): MeetObservation {\n const { snapshot } = input;\n const phase = observedPhase(input);\n const inCall = phase === \"in_call\";\n const actual: ParticipantRole | \"none\" = inCall && snapshot !== null ? effectiveRole(snapshot) : \"none\";\n const canAdmit = snapshot !== null && mayAdmitNow(actual, snapshot.participants, snapshot.livePolicy);\n const role: ObservedRole | \"none\" =\n actual === \"none\" || snapshot === null ? \"none\" : observedRole(actual, effectiveStanding(snapshot));\n const deviceInPlay = phase === \"joining\" || inCall;\n const region = snapshot?.mediaRegion;\n const atPrejoin = phase === \"prejoin\" && region !== undefined;\n return {\n phase,\n reason: phase === \"ended\" ? (snapshot?.reason ?? null) : (snapshot?.reason ?? reasonOf(phase)),\n role,\n // `net-blip`: in the call, a drop shorter than `reconnect_banner_delay_ms` is not something the\n // person sees — the contract says what is on screen, so it reads `stable` until the banner shows.\n connection: snapshot === null\n ? \"stable\"\n : inCall && snapshot.connection !== \"stable\" && !snapshot.connectionBanner\n ? \"stable\"\n : snapshot.connection,\n hostPresent: inCall && snapshot !== null\n ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role))\n : snapshot !== null && isWaitingPhase(snapshot.phase)\n ? (snapshot.waiting?.admittersPresent ?? null)\n : null,\n lobbyCount: canAdmit && snapshot !== null\n ? snapshot.lobby.filter((entry) => entry.state === \"knocking\").length\n : 0,\n recording: snapshot?.recording === \"recording\" || snapshot?.recording === \"stopping\",\n // S6c: the autoplay region is known only inside the room (no remote audio exists elsewhere).\n audioBlocked: inCall && snapshot !== null ? snapshot.mediaRegion.audioOutput === \"blocked\" : null,\n camera: deviceInPlay && snapshot !== null\n ? observedDevice(snapshot.media.camera, snapshot.mediaRegion.permission.camera, lockedFor(snapshot, \"camera\", inCall))\n : atPrejoin ? observedPreview(region.preview.camera, region.permission.camera) : null,\n microphone: deviceInPlay && snapshot !== null\n ? observedDevice(snapshot.media.microphone, snapshot.mediaRegion.permission.microphone, lockedFor(snapshot, \"microphone\", inCall))\n : atPrejoin ? observedPreview(region.preview.microphone, region.permission.microphone) : null,\n };\n}\n\n/** The organizer's lock (or a host's mute) as the person's own device state. */\nfunction lockedFor(snapshot: MeetSnapshot, kind: \"microphone\" | \"camera\", inCall: boolean): \"locked\" | \"muted_by_host\" | null {\n if (!inCall) return null;\n const role = effectiveRole(snapshot);\n if (isLockedFor(snapshot.livePolicy, role, kind)) return \"locked\";\n if (\n snapshot.livePolicy.recordingConsent === \"view_only_until_agree\" &&\n consentPending(snapshot.livePolicy, role, snapshot.recording === \"recording\", snapshot.recordingConsentGiven)\n ) {\n return \"locked\";\n }\n if (kind === \"microphone\" && snapshot.mutedByHost) return \"muted_by_host\";\n return null;\n}\n\n/** A phase the consumer renders itself (link resolution, the guest-name step, a dead link). */\nexport function staticObservation(phase: ObservedPhase, reason: string | null = null): MeetObservation {\n return {\n phase,\n reason,\n role: \"none\",\n connection: phase === \"disconnected\" ? \"server_unreachable\" : \"stable\",\n hostPresent: null,\n lobbyCount: 0,\n recording: false,\n audioBlocked: null,\n camera: null,\n microphone: null,\n };\n}\n\n/** The root element's attributes. Unknown values are omitted, never guessed. */\nexport function rootAttributes(o: MeetObservation): Record<string, string> {\n const attrs: Record<string, string> = {\n \"data-meet-root\": \"\",\n \"data-meet-phase\": o.phase,\n \"data-meet-role\": o.role,\n \"data-meet-connection\": o.connection,\n \"data-meet-lobby-count\": String(o.lobbyCount),\n \"data-meet-recording\": String(o.recording),\n };\n if (o.reason !== null) attrs[\"data-meet-reason\"] = o.reason;\n if (o.hostPresent !== null) attrs[\"data-meet-host-present\"] = String(o.hostPresent);\n if (o.audioBlocked !== null) attrs[\"data-meet-audio-blocked\"] = String(o.audioBlocked);\n if (o.camera !== null) attrs[\"data-meet-camera\"] = o.camera;\n if (o.microphone !== null) attrs[\"data-meet-microphone\"] = o.microphone;\n attrs[\"data-meet-regions\"] = JSON.stringify({\n connection: o.connection,\n camera: o.camera,\n mic: o.microphone,\n audio_output: o.audioBlocked === null ? null : o.audioBlocked ? \"blocked\" : \"playing\",\n recording: o.recording,\n });\n return attrs;\n}\n\n/** The contract's per-person connection vocabulary. `unknown` is not a reading. */\nexport function observedQuality(quality: ConnectionQuality): string | null {\n switch (quality) {\n case \"excellent\":\n case \"good\":\n return \"good\";\n case \"poor\":\n return \"poor\";\n case \"lost\":\n return \"lost\";\n default:\n return null;\n }\n}\n\n/** `[data-meet-participant]` attributes for one person's tile or roster row. */\nexport function participantAttributes(p: MeetParticipant): Record<string, string> {\n const attrs: Record<string, string> = {\n \"data-meet-participant\": \"\",\n \"data-meet-participant-identity\": p.identity,\n \"data-meet-participant-name\": p.displayName,\n \"data-meet-participant-role\": observedRole(p.role, p.standing),\n \"data-meet-participant-self\": String(p.isLocal),\n };\n // A person who dropped without leaving is `reconnecting` (S6b), whatever their last quality was.\n const quality = p.reconnecting === true ? \"reconnecting\" : observedQuality(p.quality);\n if (quality !== null) attrs[\"data-meet-participant-connection\"] = quality;\n return attrs;\n}\n\n/** The listen-only notice's contract code, from why the microphone is off. */\nexport function microphoneNoticeCode(track: LocalTrackState, permission: PermissionReading = \"unknown\"): \"mic-blocked\" | \"device-lost\" | null {\n const reason = deviceUnavailableReason(track, permission);\n if (reason === null) return null;\n return deviceNoticeCode(\"microphone\", reason) === \"mic-blocked\" ? \"mic-blocked\" : \"device-lost\";\n}\n","/**\n * MS — SPEAKING WHILE MUTED (CORE-DESIGN §11 `speaking-while-muted`). A pure detector fed the local\n * microphone level while the person muted themselves: sound that lasts `holdMs` (gaps shorter than\n * `gapMs` do not break it) fires ONCE, then stays quiet for `cooldownMs` so the nudge never nags.\n * The level source is the platform's (a browser analyser in the web skin); the rule lives here.\n */\n\nexport interface SpeakingWhileMutedOptions {\n /** Peak level 0..1 that counts as speech. */\n readonly threshold?: number;\n /** How long speech must last before the nudge. */\n readonly holdMs?: number;\n /** A pause shorter than this does not end the speech. */\n readonly gapMs?: number;\n /** After a nudge, no second one for this long. */\n readonly cooldownMs?: number;\n}\n\nexport interface SpeakingWhileMutedDetector {\n /** One level reading at time `at` (ms). True exactly when the nudge should show now. */\n feed(level: number, at: number): boolean;\n /** The person unmuted (or left): start over, cooldown included. */\n reset(): void;\n}\n\nexport function createSpeakingWhileMutedDetector(options: SpeakingWhileMutedOptions = {}): SpeakingWhileMutedDetector {\n const threshold = options.threshold ?? 0.06;\n const holdMs = options.holdMs ?? 600;\n const gapMs = options.gapMs ?? 400;\n const cooldownMs = options.cooldownMs ?? 20_000;\n let speechStart: number | null = null;\n let lastLoud: number | null = null;\n let firedAt: number | null = null;\n return {\n feed(level, at) {\n if (level >= threshold) {\n if (speechStart === null || lastLoud === null || at - lastLoud > gapMs) speechStart = at;\n lastLoud = at;\n } else if (lastLoud !== null && at - lastLoud > gapMs) {\n speechStart = null;\n lastLoud = null;\n }\n if (speechStart === null || at - speechStart < holdMs) return false;\n if (firedAt !== null && at - firedAt < cooldownMs) return false;\n firedAt = at;\n speechStart = null;\n lastLoud = null;\n return true;\n },\n reset() {\n speechStart = null;\n lastLoud = null;\n firedAt = null;\n },\n };\n}\n"],"mappings":"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","names":["text","row","refusal","text","refusal","text","byId","ms","pending","options","num","text","count","existing","text","current","count","count","byId","text","ms","readState","refusal","media","now","text","refusal","text","formatDurationMs","when","formatDurationMs","refusal","upsert","text"]}