@fayz-ai/plugin-scribe 0.11.2 → 0.12.1
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/components/ScribeAssistantBridge.d.ts +5 -3
- package/dist/components/ScribeAssistantBridge.d.ts.map +1 -1
- package/dist/components/ScribeHeaderAction.d.ts +3 -2
- package/dist/components/ScribeHeaderAction.d.ts.map +1 -1
- package/dist/components/ScribeRecordingPill.d.ts +1 -1
- package/dist/components/ScribeRecordingPill.d.ts.map +1 -1
- package/dist/components/ScribeSessionPage.d.ts +5 -2
- package/dist/components/ScribeSessionPage.d.ts.map +1 -1
- package/dist/components/ScribeShellMount.d.ts +3 -10
- package/dist/components/ScribeShellMount.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +230 -317
- package/dist/index.js.map +1 -1
- package/dist/lib/start.d.ts +47 -0
- package/dist/lib/start.d.ts.map +1 -0
- package/dist/migrations/index.d.ts +2 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -5
- package/dist/components/ScribeConsentDialog.d.ts +0 -12
- package/dist/components/ScribeConsentDialog.d.ts.map +0 -1
- package/dist/components/ScribeRecoveryBanner.d.ts +0 -8
- package/dist/components/ScribeRecoveryBanner.d.ts.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/locales/en.ts","../src/locales/pt-BR.ts","../src/locales/index.ts","../src/runtime/idb.ts","../src/runtime/capture.ts","../src/data/tables.ts","../src/runtime/pump.ts","../src/runtime/index.ts","../src/store.ts","../src/components/ScribeRecordingPill.tsx","../src/components/ScribeRecoveryBanner.tsx","../src/components/ScribeConsentDialog.tsx","../src/data/supabase.ts","../src/lib/config.ts","../src/lib/transport.ts","../src/lib/transcript-clean.ts","../src/lib/drain.ts","../src/lib/live-transcript.ts","../src/components/ScribeAssistantBridge.tsx","../src/components/ScribeShellMount.tsx","../src/lib/prompt.ts","../src/components/TranscriptPanel.tsx","../src/components/DraftPanel.tsx","../src/lib/generate.ts","../src/components/ScribeSessionPage.tsx","../src/components/ScribeHeaderAction.tsx","../src/migrations/index.ts","../src/lib/timeline-source.ts","../src/components/ScribeDevicePicker.tsx","../src/components/ScribeLivePanel.tsx","../src/types.ts","../src/index.ts"],"names":["running","timer","getSupabaseClientOptional","orphans","React","elapsedMs","React2","live","jsxs","jsx","Button","React3","Mic","CONCURRENCY","client","create","React4","retention","React5","createPortal","main","Fragment","AlertTriangle","React6","toast","Loader2","Badge","draining","useAgentSurface","openAssistant","React8","tick","load","Modal","ModalContent","ModalHeader","ModalTitle","ModalBody","Checkbox","ModalFooter","React9","Pause","Play","Square"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;ACDzC,IAAM,a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","file":"index.js","sourcesContent":["export const en: Record<string, string> = {\n 'scribe.nav.sessions': 'Sessions',\n 'scribe.settings.title': 'Session recording',\n 'scribe.action.start': 'Start session',\n 'scribe.action.pause': 'Pause',\n 'scribe.action.resume': 'Resume',\n 'scribe.action.finish': 'Finish',\n 'scribe.action.discard': 'Discard',\n 'scribe.action.generate': 'Generate document',\n 'scribe.transcript': 'Transcript',\n 'scribe.recovery.title': 'Recording interrupted',\n 'scribe.consent.title': 'Start recording',\n}\n","export const ptBR: Record<string, string> = {\n 'scribe.nav.sessions': 'Atendimentos',\n 'scribe.settings.title': 'Gravação de atendimentos',\n 'scribe.action.start': 'Iniciar atendimento',\n 'scribe.action.pause': 'Pausar',\n 'scribe.action.resume': 'Retomar',\n 'scribe.action.finish': 'Finalizar',\n 'scribe.action.discard': 'Descartar',\n 'scribe.action.generate': 'Gerar documento',\n 'scribe.transcript': 'Transcrição',\n 'scribe.recovery.title': 'Gravação interrompida',\n 'scribe.consent.title': 'Iniciar gravação',\n}\n","import { en } from './en'\nimport { ptBR } from './pt-BR'\n\nexport const scribeLocales: Record<string, Record<string, string>> = {\n en,\n 'pt-BR': ptBR,\n}\n","// ---------------------------------------------------------------------------\n// IndexedDB — onde o áudio fica seguro antes de existir no servidor.\n//\n// Esta é a primeira camada de persistência local do SDK, e ela existe por um\n// motivo só: entre o microfone capturar um som e esse som chegar ao Storage\n// existe uma janela onde ele só mora na RAM de uma aba. Fechar a aba nessa\n// janela apaga a consulta. IndexedDB reduz essa janela para 5 segundos e a\n// torna sobrevivente a reload, crash e queda de rede.\n//\n// localStorage não serve (síncrono, ~5 MB, só string). OPFS seria elegante mas\n// não tem suporte parelho. IndexedDB guarda Blob por referência de arquivo nos\n// três engines, então 7 MB de consulta não viram 7 MB de string serializada.\n//\n// Zero dependência de propósito: `idb-keyval` resolveria a ergonomia, mas a\n// regra deste repo é não somar dependência para o que cabe em 200 linhas.\n// ---------------------------------------------------------------------------\n\nimport type { CaptureState } from '../types'\n\nconst DB_NAME = 'fayz.scribe'\nconst DB_VERSION = 1\n\nexport const STORE_SESSIONS = 'sessions'\nexport const STORE_PIECES = 'pieces'\nexport const STORE_SEGMENTS = 'segments'\nexport const STORE_TRANSCRIPT = 'transcript'\n\n/** Índice usado pelo pump para achar o que ainda precisa subir. */\nexport const IDX_SEGMENT_UPLOAD = 'by_upload'\n\n// ---------------------------------------------------------------------------\n// Formas\n// ---------------------------------------------------------------------------\n\nexport interface IdbSession {\n /** uuid gerado no cliente. É o mesmo id da linha no servidor. */\n id: string\n tenantId: string\n subjectId?: string\n subjectName?: string\n appointmentId?: string\n userId: string\n mimeType: string\n locale: string\n startedAtEpochMs: number\n /** null enquanto pausado. Ver `elapsedMs` — o tempo é calculado, nunca contado. */\n resumedAtEpochMs: number | null\n /** Relógio de parede acumulado através de pausas. */\n accumulatedMs: number\n /** Heartbeat de 2 s. Sessão com tick velho no boot = órfã de um crash. */\n lastTickEpochMs: number\n state: CaptureState\n /** Próximo segmento a abrir. */\n segIndex: number\n templateKeys: string[]\n}\n\nexport interface IdbPiece {\n sessionId: string\n segIndex: number\n pieceIndex: number\n blob: Blob\n bytes: number\n createdAt: number\n}\n\nexport interface IdbSegment {\n sessionId: string\n segIndex: number\n /** Vira null depois do upload — é o que devolve os bytes ao disco do usuário. */\n blob: Blob | null\n bytes: number\n startOffsetMs: number\n /** Duração NOMINAL (contada pelo cliente). A real vem do STT, no servidor. */\n nominalDurationMs: number\n partial: boolean\n gap: boolean\n /** No signal above the noise floor. Not the same as `gap`: the audio exists\n * and is kept, it just is not worth a transcription request. */\n silent?: boolean\n uploadState: 'pending' | 'uploading' | 'uploaded' | 'failed'\n attempts: number\n nextAttemptAt: number\n storagePath: string | null\n}\n\nexport interface IdbTranscriptPart {\n sessionId: string\n segIndex: number\n text: string\n startOffsetMs: number\n endOffsetMs: number\n provider: string\n confidence: number\n}\n\n// ---------------------------------------------------------------------------\n// Abertura\n// ---------------------------------------------------------------------------\n\nlet dbPromise: Promise<IDBDatabase> | null = null\n\nexport function isIdbAvailable(): boolean {\n return typeof indexedDB !== 'undefined'\n}\n\nexport function openScribeDb(): Promise<IDBDatabase> {\n if (dbPromise) return dbPromise\n dbPromise = new Promise((resolve, reject) => {\n if (!isIdbAvailable()) {\n reject(new Error('IndexedDB indisponível neste navegador'))\n return\n }\n const req = indexedDB.open(DB_NAME, DB_VERSION)\n\n req.onupgradeneeded = () => {\n const db = req.result\n if (!db.objectStoreNames.contains(STORE_SESSIONS)) {\n db.createObjectStore(STORE_SESSIONS, { keyPath: 'id' })\n }\n if (!db.objectStoreNames.contains(STORE_PIECES)) {\n // Chave composta: os pieces de um segmento saem juntos e em ordem num\n // único range, que é exatamente como a montagem os consome.\n db.createObjectStore(STORE_PIECES, { keyPath: ['sessionId', 'segIndex', 'pieceIndex'] })\n }\n if (!db.objectStoreNames.contains(STORE_SEGMENTS)) {\n const store = db.createObjectStore(STORE_SEGMENTS, { keyPath: ['sessionId', 'segIndex'] })\n // O pump varre por [estado, quando-tentar-de-novo]. Sem este índice ele\n // leria a tabela inteira a cada volta do loop.\n store.createIndex(IDX_SEGMENT_UPLOAD, ['uploadState', 'nextAttemptAt'])\n }\n if (!db.objectStoreNames.contains(STORE_TRANSCRIPT)) {\n db.createObjectStore(STORE_TRANSCRIPT, { keyPath: ['sessionId', 'segIndex'] })\n }\n }\n\n req.onsuccess = () => resolve(req.result)\n req.onerror = () => reject(req.error ?? new Error('falha ao abrir o IndexedDB'))\n req.onblocked = () => reject(new Error('IndexedDB bloqueado por outra aba'))\n })\n return dbPromise\n}\n\n// ---------------------------------------------------------------------------\n// Helpers de transação\n// ---------------------------------------------------------------------------\n\nfunction promisify<T>(req: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n req.onsuccess = () => resolve(req.result)\n req.onerror = () => reject(req.error ?? new Error('falha na operação do IndexedDB'))\n })\n}\n\nasync function withStore<T>(\n name: string,\n mode: IDBTransactionMode,\n fn: (store: IDBObjectStore) => IDBRequest<T> | Promise<T>,\n): Promise<T> {\n const db = await openScribeDb()\n const tx = db.transaction(name, mode)\n const store = tx.objectStore(name)\n const out = fn(store)\n const value = out instanceof Promise ? await out : await promisify(out)\n // Esperar o `complete` e não só o request: em modo readwrite o request pode\n // ter sucesso e a transação abortar depois (quota, por exemplo). Resolver\n // antes disso reportaria \"gravado\" para algo que não foi.\n await new Promise<void>((resolve, reject) => {\n tx.oncomplete = () => resolve()\n tx.onerror = () => reject(tx.error ?? new Error('transação abortada'))\n tx.onabort = () => reject(tx.error ?? new Error('transação abortada'))\n })\n return value\n}\n\n// ---------------------------------------------------------------------------\n// Sessões\n// ---------------------------------------------------------------------------\n\nexport function putSession(session: IdbSession): Promise<IDBValidKey> {\n return withStore(STORE_SESSIONS, 'readwrite', (s) => s.put(session))\n}\n\nexport function getSession(id: string): Promise<IdbSession | undefined> {\n return withStore(STORE_SESSIONS, 'readonly', (s) => s.get(id) as IDBRequest<IdbSession | undefined>)\n}\n\nexport function getAllSessions(): Promise<IdbSession[]> {\n return withStore(STORE_SESSIONS, 'readonly', (s) => s.getAll() as IDBRequest<IdbSession[]>)\n}\n\nexport function deleteSession(id: string): Promise<undefined> {\n return withStore(STORE_SESSIONS, 'readwrite', (s) => s.delete(id) as IDBRequest<undefined>)\n}\n\n/**\n * Sessões que o boot precisa olhar: estavam vivas e o heartbeat morreu. Só\n * `lastTickEpochMs` distingue \"a aba fechou agora\" de \"isto está órfão desde\n * ontem\" — o estado sozinho não distingue, porque a aba que morreu não teve\n * chance de escrever o estado final.\n */\nexport async function findOrphanSessions(staleAfterMs = 15_000): Promise<IdbSession[]> {\n const now = Date.now()\n const all = await getAllSessions()\n return all.filter(\n (s) =>\n (s.state === 'recording' || s.state === 'paused' || s.state === 'interrupted') &&\n now - s.lastTickEpochMs > staleAfterMs,\n )\n}\n\n// ---------------------------------------------------------------------------\n// Pieces — a unidade de durabilidade (5 s)\n// ---------------------------------------------------------------------------\n\nexport function putPiece(piece: IdbPiece): Promise<IDBValidKey> {\n return withStore(STORE_PIECES, 'readwrite', (s) => s.put(piece))\n}\n\n/** Todos os pieces de um segmento, em ordem de pieceIndex. */\nexport function getPieces(sessionId: string, segIndex: number): Promise<IdbPiece[]> {\n const range = IDBKeyRange.bound(\n [sessionId, segIndex, -Infinity],\n [sessionId, segIndex, Infinity],\n )\n return withStore(STORE_PIECES, 'readonly', (s) => s.getAll(range) as IDBRequest<IdbPiece[]>)\n}\n\n/** Pieces soltos de uma sessão inteira — o que a recuperação encontra depois de um crash. */\nexport function getAllPiecesForSession(sessionId: string): Promise<IdbPiece[]> {\n const range = IDBKeyRange.bound(\n [sessionId, -Infinity, -Infinity],\n [sessionId, Infinity, Infinity],\n )\n return withStore(STORE_PIECES, 'readonly', (s) => s.getAll(range) as IDBRequest<IdbPiece[]>)\n}\n\nexport function deletePieces(sessionId: string, segIndex: number): Promise<void> {\n const range = IDBKeyRange.bound(\n [sessionId, segIndex, -Infinity],\n [sessionId, segIndex, Infinity],\n )\n return withStore(STORE_PIECES, 'readwrite', (s) => promisify(s.delete(range) as IDBRequest<undefined>).then(() => undefined))\n}\n\n// ---------------------------------------------------------------------------\n// Segments — a unidade de upload + STT (30 s)\n// ---------------------------------------------------------------------------\n\nexport function putSegment(segment: IdbSegment): Promise<IDBValidKey> {\n return withStore(STORE_SEGMENTS, 'readwrite', (s) => s.put(segment))\n}\n\nexport function getSegment(sessionId: string, segIndex: number): Promise<IdbSegment | undefined> {\n return withStore(STORE_SEGMENTS, 'readonly', (s) =>\n s.get([sessionId, segIndex]) as IDBRequest<IdbSegment | undefined>,\n )\n}\n\nexport function getSegmentsForSession(sessionId: string): Promise<IdbSegment[]> {\n const range = IDBKeyRange.bound([sessionId, -Infinity], [sessionId, Infinity])\n return withStore(STORE_SEGMENTS, 'readonly', (s) => s.getAll(range) as IDBRequest<IdbSegment[]>)\n}\n\n/**\n * O que o pump deve tentar agora: pendente ou falho, com `nextAttemptAt` já\n * vencido. `uploading` fica de fora — se uma volta anterior travou nesse estado,\n * o `reclaimStuckUploads` do boot é quem devolve, não este método.\n */\nexport async function claimUploadable(limit: number): Promise<IdbSegment[]> {\n const now = Date.now()\n const out: IdbSegment[] = []\n for (const state of ['pending', 'failed'] as const) {\n if (out.length >= limit) break\n const range = IDBKeyRange.bound([state, -Infinity], [state, now])\n const rows = await withStore(STORE_SEGMENTS, 'readonly', (s) =>\n s.index(IDX_SEGMENT_UPLOAD).getAll(range, limit - out.length) as IDBRequest<IdbSegment[]>,\n )\n out.push(...rows)\n }\n // Ordem (sessão, índice) para o servidor ver os segmentos chegarem em ordem\n // cronológica no caso comum, o que deixa a transcrição parcial legível\n // enquanto ainda está subindo.\n return out.sort((a, b) =>\n a.sessionId === b.sessionId ? a.segIndex - b.segIndex : a.sessionId.localeCompare(b.sessionId),\n )\n}\n\n/**\n * Segmentos parados em `uploading` de uma aba que morreu no meio do upload.\n * Sem isso eles ficariam invisíveis para sempre: `claimUploadable` só olha\n * `pending` e `failed`.\n */\nexport async function reclaimStuckUploads(): Promise<number> {\n const range = IDBKeyRange.bound(['uploading', -Infinity], ['uploading', Infinity])\n const stuck = await withStore(STORE_SEGMENTS, 'readonly', (s) =>\n s.index(IDX_SEGMENT_UPLOAD).getAll(range) as IDBRequest<IdbSegment[]>,\n )\n for (const seg of stuck) {\n await putSegment({ ...seg, uploadState: 'pending', nextAttemptAt: 0 })\n }\n return stuck.length\n}\n\nexport function deleteSegmentsForSession(sessionId: string): Promise<void> {\n const range = IDBKeyRange.bound([sessionId, -Infinity], [sessionId, Infinity])\n return withStore(STORE_SEGMENTS, 'readwrite', (s) =>\n promisify(s.delete(range) as IDBRequest<undefined>).then(() => undefined),\n )\n}\n\n// ---------------------------------------------------------------------------\n// Transcrição (cache local, para a tela renderizar offline)\n// ---------------------------------------------------------------------------\n\nexport function putTranscriptPart(part: IdbTranscriptPart): Promise<IDBValidKey> {\n return withStore(STORE_TRANSCRIPT, 'readwrite', (s) => s.put(part))\n}\n\nexport function getTranscriptForSession(sessionId: string): Promise<IdbTranscriptPart[]> {\n const range = IDBKeyRange.bound([sessionId, -Infinity], [sessionId, Infinity])\n return withStore(STORE_TRANSCRIPT, 'readonly', (s) =>\n s.getAll(range) as IDBRequest<IdbTranscriptPart[]>,\n )\n}\n\n// ---------------------------------------------------------------------------\n// Quota\n// ---------------------------------------------------------------------------\n\n/** Consulta de 40 min a 24 kbps mono ≈ 7,2 MB. 50 MB de folga cobre um dia ruim. */\nexport const MIN_FREE_BYTES = 50 * 1024 * 1024\n\nexport interface StorageHealth {\n ok: boolean\n persisted: boolean\n freeBytes: number | null\n reason?: string\n}\n\n/**\n * Pré-voo antes do primeiro byte. Duas coisas distintas:\n *\n * `persist()` pede ao browser para NÃO despejar este origin quando o disco\n * apertar. Sem isso o navegador pode limpar o IndexedDB no meio da consulta e a\n * gravação some sem erro nenhum — o pior modo de falha possível.\n *\n * `estimate()` é a checagem de espaço. Recusar a gravar com o disco cheio é\n * infinitamente melhor que gravar 12 minutos e descobrir no minuto 13.\n */\nexport async function checkStorageHealth(): Promise<StorageHealth> {\n if (!isIdbAvailable()) {\n return { ok: false, persisted: false, freeBytes: null, reason: 'IndexedDB indisponível' }\n }\n let persisted = false\n try {\n if (navigator.storage?.persist) persisted = await navigator.storage.persist()\n } catch {\n // Firefox pode pedir permissão ao usuário e rejeitar. Não é fatal: sem\n // persistência a gravação ainda funciona, só fica sujeita a eviction.\n }\n try {\n if (!navigator.storage?.estimate) return { ok: true, persisted, freeBytes: null }\n const { quota = 0, usage = 0 } = await navigator.storage.estimate()\n const free = quota - usage\n if (free < MIN_FREE_BYTES) {\n return {\n ok: false,\n persisted,\n freeBytes: free,\n reason: `Espaço insuficiente no navegador (${Math.round(free / 1024 / 1024)} MB livres, mínimo ${MIN_FREE_BYTES / 1024 / 1024} MB)`,\n }\n }\n return { ok: true, persisted, freeBytes: free }\n } catch {\n return { ok: true, persisted, freeBytes: null }\n }\n}\n\n/** Só para testes — força a próxima chamada a reabrir o banco. */\nexport function __resetDbForTests(): void {\n dbPromise = null\n}\n","// ---------------------------------------------------------------------------\n// A captura. Um singleton de módulo, deliberadamente fora do React.\n//\n// A premissa inteira da feature é \"grave enquanto navego\". Num SPA isso já é\n// quase de graça — mas só se o dono da gravação NÃO for um componente. Um hook\n// morre no unmount; a gravação não pode. Então o recorder, o relógio, os\n// listeners e a máquina de estados vivem aqui, em escopo de módulo, e o React\n// apenas assina uma store para desenhar o pill.\n//\n// ---------------------------------------------------------------------------\n// Por que o recorder é ROTACIONADO a cada 30 s\n// ---------------------------------------------------------------------------\n// Em WebM/Opus com `start(timeslice)`, só o PRIMEIRO blob carrega o header\n// EBML; os seguintes são clusters nus. Consequência prática: não dá para pegar\n// o chunk 37 e mandar para o STT — isoladamente ele não é um arquivo.\n//\n// Rotacionar (parar o recorder e criar outro sobre o MESMO MediaStream) faz\n// cada segmento começar com header próprio. Aí `new Blob([p0..pk])` é um WebM\n// válido MESMO TRUNCADO no meio — e é isso que dá recuperação de segmento\n// parcial de graça depois de um crash.\n//\n// O preço é o gap da troca: ~15 ms por rotação, ~80 rotações em 40 minutos,\n// ~1,2 s de 2400 s (0,05%). A alternativa sem gap exigiria montar bytes de\n// container à mão e torcer para Chrome, Safari e Firefox decodificarem igual.\n// ---------------------------------------------------------------------------\n\nimport type { CaptureState, ConsentMode } from '../types'\nimport {\n checkStorageHealth,\n deletePieces,\n getPieces,\n putPiece,\n putSegment,\n putSession,\n getSession,\n type IdbSession,\n} from './idb'\n\n/** Unidade de durabilidade: no máximo isto de áudio vive só em RAM. */\nexport const PIECE_MS = 5_000\n/** Unidade de upload + STT. 6 pieces por segmento. */\nexport const SEGMENT_MS = 30_000\n/** Heartbeat. É o que distingue \"aba fechou agora\" de \"isto está órfão\". */\nconst HEARTBEAT_MS = 2_000\n\n/** Ordem de preferência. Safari só tem mp4; os outros preferem Opus. */\nconst MIME_CANDIDATES = [\n 'audio/webm;codecs=opus',\n 'audio/ogg;codecs=opus',\n 'audio/mp4',\n 'audio/webm',\n]\n\nexport function pickMimeType(): string | null {\n if (typeof MediaRecorder === 'undefined') return null\n for (const mime of MIME_CANDIDATES) {\n try {\n if (MediaRecorder.isTypeSupported(mime)) return mime\n } catch {\n // isTypeSupported pode lançar em engines antigas; seguir para a próxima.\n }\n }\n return null\n}\n\nexport function isCaptureSupported(): boolean {\n return (\n typeof navigator !== 'undefined' &&\n !!navigator.mediaDevices?.getUserMedia &&\n typeof MediaRecorder !== 'undefined' &&\n pickMimeType() !== null\n )\n}\n\n// ---------------------------------------------------------------------------\n// Estado vivo (não persistido — o persistido está no IndexedDB)\n// ---------------------------------------------------------------------------\n\ninterface LiveCapture {\n session: IdbSession\n stream: MediaStream\n recorder: MediaRecorder | null\n /** Pieces do segmento em aberto, na ordem em que chegaram. */\n buffer: Blob[]\n pieceIndex: number\n /** Offset do começo do segmento atual desde o início da sessão. */\n segStartOffsetMs: number\n wakeLock: WakeLockSentinel | null\n heartbeat: ReturnType<typeof setInterval> | null\n /** Fecha o segmento em aberto quando ele completa 30 s. */\n rotateTimer: ReturnType<typeof setTimeout> | null\n /** True enquanto uma rotação está em curso, para o `onstop` saber o motivo. */\n rotating: boolean\n /** Quando a captura foi interrompida (mic perdido), para dimensionar o gap. */\n interruptedAtMs: number | null\n /** Medidor de nível do segmento em aberto — ver `attachLevelMeter`. */\n meter: { context: AudioContext; stop: () => void; peak: () => number; reset: () => void } | null\n}\n\nlet live: LiveCapture | null = null\n\ntype Listener = (snapshot: CaptureSnapshot) => void\nconst listeners = new Set<Listener>()\n\nexport interface CaptureSnapshot {\n state: CaptureState\n sessionId: string | null\n subjectId?: string\n subjectName?: string\n /** Relógio de parede. Move o cronômetro; NÃO é o registro. */\n elapsedMs: number\n segIndex: number\n error?: string\n}\n\nlet lastError: string | undefined\n\n// Microphone choice.\n//\n// In a consulting room the system default is rarely the right input, and\n// recording the whole encounter on the wrong one is discovered later — when the\n// audio no longer exists. The choice lives in localStorage and NOT in the\n// session's IndexedDB: it is an operator preference on that machine, not a fact\n// about that recording.\n\nconst DEVICE_KEY = 'fayz.scribe.inputDevice'\n\n/** The remembered input, if the operator picked one. */\nexport function getPreferredDeviceId(): string | null {\n try {\n return localStorage.getItem(DEVICE_KEY)\n } catch {\n return null\n }\n}\n\nexport function setPreferredDeviceId(deviceId: string | null): void {\n try {\n if (deviceId) localStorage.setItem(DEVICE_KEY, deviceId)\n else localStorage.removeItem(DEVICE_KEY)\n } catch {\n // Private mode without storage: the choice lasts this session only.\n }\n}\n\n/** In use now — the remembered one, or what the running capture opened. */\nlet activeDeviceId: string | null = null\n\nexport function getActiveDeviceId(): string | null {\n return activeDeviceId ?? getPreferredDeviceId()\n}\n\nfunction audioConstraints(): MediaTrackConstraints {\n const deviceId = getActiveDeviceId()\n return {\n channelCount: 1,\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n sampleRate: 16_000,\n // `exact` would be worse: a remembered device may be unplugged, and then\n // recording would not start at all instead of falling back.\n ...(deviceId ? { deviceId: { ideal: deviceId } } : {}),\n }\n}\n\n/** Swaps the input of a RUNNING capture. The open clip closes first — swapping\n * without closing would put two sources inside one file. */\nexport async function switchInputDevice(deviceId: string): Promise<boolean> {\n activeDeviceId = deviceId\n if (!live) return true\n const cap = live\n try {\n const next = await navigator.mediaDevices.getUserMedia({ audio: audioConstraints() })\n const wasRecording = cap.session.state === 'recording'\n if (wasRecording) await pauseCapture()\n cap.stream.getTracks().forEach((track) => track.stop())\n cap.stream = next\n cap.meter?.stop()\n cap.meter = attachLevelMeter(next)\n if (wasRecording) await resumeCapture()\n emit()\n return true\n } catch {\n return false\n }\n}\n\n/**\n * O MediaStream que está sendo gravado, para quem só quer OLHAR para ele —\n * hoje o medidor de nível do composer do assistente.\n *\n * Emprestar o stream em vez de deixar a shell abrir o seu: um segundo\n * `getUserMedia` sobre o mesmo microfone durante uma gravação é uma segunda\n * superfície de permissão, mais bateria, e em alguns aparelhos um ganho\n * diferente — tudo para desenhar barras de um áudio que já está aqui.\n *\n * Quem recebe NUNCA para estas tracks: elas são a gravação.\n */\nexport function getCaptureStream(): MediaStream | null {\n return live?.stream ?? null\n}\n\nexport function getSnapshot(): CaptureSnapshot {\n if (!live) return { state: 'idle', sessionId: null, elapsedMs: 0, segIndex: 0, error: lastError }\n return {\n state: live.session.state,\n sessionId: live.session.id,\n subjectId: live.session.subjectId,\n subjectName: live.session.subjectName,\n elapsedMs: elapsedMs(live.session),\n segIndex: live.session.segIndex,\n error: lastError,\n }\n}\n\nfunction emit(): void {\n const snap = getSnapshot()\n for (const fn of listeners) {\n try {\n fn(snap)\n } catch {\n // Um assinante quebrado não pode derrubar a gravação.\n }\n }\n}\n\nexport function subscribeCapture(fn: Listener): () => void {\n listeners.add(fn)\n fn(getSnapshot())\n return () => listeners.delete(fn)\n}\n\n// ---------------------------------------------------------------------------\n// Tempo: calcular, nunca contar\n// ---------------------------------------------------------------------------\n\n/**\n * Timers throttlam. Uma aba em segundo plano clampa `setInterval` em ≥1/s, e\n * uma aba congelada não roda nada. Qualquer cronômetro que ACUMULE ticks vai\n * mentir — e mentir para menos, que é o modo de falha silencioso.\n *\n * `Date.now()` sobrevive a throttle e a sono do sistema operacional. O preço é\n * ficar sujeito a salto de relógio de parede (NTP, fuso), o que é raro e\n * detectável, enquanto o throttle é garantido e invisível.\n *\n * Nada disso é o registro: a duração que vale é SUM(segments.duration_ms), do\n * áudio que de fato existe. Este número move o pill e para por aí.\n */\nexport function elapsedMs(session: IdbSession): number {\n const running = session.state === 'recording' && session.resumedAtEpochMs !== null\n return session.accumulatedMs + (running ? Date.now() - session.resumedAtEpochMs! : 0)\n}\n\n// ---------------------------------------------------------------------------\n// Segmentos\n// ---------------------------------------------------------------------------\n\n/**\n * Fecha o segmento em aberto: junta os pieces num Blob e o enfileira para\n * upload. `partial` marca o que foi montado de um segmento cortado no meio —\n * vale transcrever mesmo assim, 22 segundos é melhor que zero.\n */\n// Noise floor.\n//\n// Whisper HALLUCINATES over silence, returning phrases it learned to produce\n// when there is nothing to transcribe. In a consultation with pauses those are\n// invented sentences entering a clinical record.\n//\n// Measuring the level during capture solves both at once: a silent clip never\n// becomes a request (no cost, no latency) and cannot hallucinate. The audio is\n// still stored — what is dropped is the REQUEST, not the record.\n\n/** Below this, 30s of audio holds no speech — only room tone. */\nconst SILENCE_PEAK = 0.02\n\nfunction attachLevelMeter(stream: MediaStream): LiveCapture['meter'] {\n const AudioCtor: typeof AudioContext | undefined =\n typeof window === 'undefined'\n ? undefined\n : window.AudioContext ??\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext\n if (!AudioCtor) return null\n\n try {\n const context = new AudioCtor()\n const analyser = context.createAnalyser()\n analyser.fftSize = 512\n context.createMediaStreamSource(stream).connect(analyser)\n const buffer = new Uint8Array(analyser.frequencyBinCount)\n let peak = 0\n\n // 10Hz is enough: a syllable lasts ~150ms, so nothing audible slips\n // between two samples.\n const timer = setInterval(() => {\n analyser.getByteFrequencyData(buffer)\n let sum = 0\n for (const v of buffer) sum += v\n const level = sum / buffer.length / 255\n if (level > peak) peak = level\n }, 100)\n\n return {\n context,\n stop: () => {\n clearInterval(timer)\n void context.close().catch(() => {})\n },\n peak: () => peak,\n reset: () => {\n peak = 0\n },\n }\n } catch {\n return null\n }\n}\n\nasync function closeSegment(cap: LiveCapture, partial: boolean): Promise<void> {\n const { session, buffer, segStartOffsetMs } = cap\n if (buffer.length === 0) return\n\n const segIndex = session.segIndex\n const blob = new Blob(buffer, { type: session.mimeType })\n const nominalDurationMs = buffer.length * PIECE_MS\n // No meter (browser without AudioContext) means transcribe anyway: when in\n // doubt, transcribing is the safe behaviour.\n const silent = cap.meter ? cap.meter.peak() < SILENCE_PEAK : false\n cap.meter?.reset()\n\n await putSegment({\n sessionId: session.id,\n segIndex,\n blob,\n bytes: blob.size,\n startOffsetMs: segStartOffsetMs,\n nominalDurationMs,\n partial,\n gap: false,\n silent,\n uploadState: 'pending',\n attempts: 0,\n nextAttemptAt: 0,\n storagePath: null,\n })\n\n // Pieces já cumpriram o papel: existem só para o intervalo entre \"capturado\"\n // e \"montado num segmento\". Apagar aqui é o que impede o IndexedDB de guardar\n // o mesmo áudio duas vezes durante uma consulta inteira.\n await deletePieces(session.id, segIndex)\n\n cap.buffer = []\n cap.pieceIndex = 0\n cap.segStartOffsetMs = segStartOffsetMs + nominalDurationMs\n session.segIndex = segIndex + 1\n await putSession(session)\n}\n\n/**\n * Grava uma linha SINTÉTICA marcando um buraco. Não tem áudio nem upload: existe\n * para a transcrição admitir a costura.\n *\n * Emendar dois momentos separados por 8 minutos como se fossem contínuos produz\n * um registro que MENTE — e num registro clínico ou jurídico isso é pior que um\n * registro com furo declarado.\n */\nasync function writeGapSegment(cap: LiveCapture, durationMs: number): Promise<void> {\n const { session } = cap\n const segIndex = session.segIndex\n await putSegment({\n sessionId: session.id,\n segIndex,\n blob: null,\n bytes: 0,\n startOffsetMs: cap.segStartOffsetMs,\n nominalDurationMs: durationMs,\n partial: false,\n gap: true,\n uploadState: 'pending',\n attempts: 0,\n nextAttemptAt: 0,\n storagePath: null,\n })\n cap.segStartOffsetMs += durationMs\n session.segIndex = segIndex + 1\n await putSession(session)\n}\n\n// ---------------------------------------------------------------------------\n// Recorder\n// ---------------------------------------------------------------------------\n\nfunction startRecorder(cap: LiveCapture): void {\n const recorder = new MediaRecorder(cap.stream, {\n mimeType: cap.session.mimeType,\n // 24 kbps mono é transparente para fala e mantém 40 min em ~7 MB — o número\n // que torna toda a história de durabilidade barata.\n audioBitsPerSecond: 24_000,\n })\n\n recorder.ondataavailable = (ev: BlobEvent) => {\n if (!ev.data || ev.data.size === 0) return\n cap.buffer.push(ev.data)\n const pieceIndex = cap.pieceIndex++\n // Fire-and-forget de propósito: o `ondataavailable` não pode aguardar I/O,\n // ou o próximo piece encavala. A ordem é garantida pelo pieceIndex.\n void putPiece({\n sessionId: cap.session.id,\n segIndex: cap.session.segIndex,\n pieceIndex,\n blob: ev.data,\n bytes: ev.data.size,\n createdAt: Date.now(),\n }).catch(() => {\n // Escrita local falhou (quota?). O piece ainda está em `buffer`, então o\n // segmento fecha inteiro — só perdemos a rede de segurança do crash.\n })\n }\n\n recorder.onstop = () => {\n const wasRotating = cap.rotating\n cap.rotating = false\n void (async () => {\n await closeSegment(cap, !wasRotating)\n // Rotação: o segmento fechou porque completou 30 s, então segue gravando.\n if (wasRotating && cap.session.state === 'recording') {\n startRecorder(cap)\n armRotation(cap)\n }\n emit()\n })()\n }\n\n recorder.onerror = () => {\n void handleInterruption('erro do gravador')\n }\n\n cap.recorder = recorder\n recorder.start(PIECE_MS)\n}\n\nfunction armRotation(cap: LiveCapture): void {\n if (cap.rotateTimer) clearTimeout(cap.rotateTimer)\n cap.rotateTimer = setTimeout(() => {\n if (!cap.recorder || cap.recorder.state !== 'recording') return\n cap.rotating = true\n // `stop()` dispara um último `ondataavailable` com o resto do buffer e\n // depois `onstop`, que fecha o segmento e reabre. O MediaStream continua\n // vivo o tempo todo — é isso que mantém o gap na casa dos milissegundos.\n cap.recorder.stop()\n }, SEGMENT_MS)\n}\n\n// ---------------------------------------------------------------------------\n// Wake lock, beforeunload, ciclo de vida da página\n// ---------------------------------------------------------------------------\n\nasync function acquireWakeLock(cap: LiveCapture): Promise<void> {\n try {\n if (!('wakeLock' in navigator)) return\n cap.wakeLock = await navigator.wakeLock.request('screen')\n } catch {\n // Negado ou indisponível. Não é fatal — só significa que a tela pode apagar.\n }\n}\n\nasync function releaseWakeLock(cap: LiveCapture): Promise<void> {\n try {\n await cap.wakeLock?.release()\n } catch {\n // Já liberado pelo browser ao esconder a página.\n }\n cap.wakeLock = null\n}\n\nfunction onBeforeUnload(ev: BeforeUnloadEvent): void {\n if (!live || live.session.state !== 'recording') return\n // Grosseiro, mas é o único freio que o browser oferece contra o Cmd+W\n // acidental no meio de uma consulta.\n ev.preventDefault()\n ev.returnValue = ''\n}\n\nfunction onVisibilityChange(): void {\n if (!live) return\n // O wake lock é solto automaticamente quando a página esconde; reaver ao\n // voltar, senão a tela apaga no meio da consulta seguinte.\n if (document.visibilityState === 'visible' && live.session.state === 'recording' && !live.wakeLock) {\n void acquireWakeLock(live)\n }\n emit()\n}\n\nfunction onFreeze(): void {\n if (!live) return\n live.interruptedAtMs = Date.now()\n live.session.lastTickEpochMs = Date.now()\n void putSession(live.session)\n}\n\nfunction onResume(): void {\n if (!live) return\n // A track pode ter morrido enquanto congelado. Verificar em vez de supor.\n const track = live.stream.getAudioTracks()[0]\n if (!track || track.readyState === 'ended') {\n void handleInterruption('microfone perdido enquanto a aba estava suspensa')\n }\n}\n\nfunction attachLifecycle(): void {\n window.addEventListener('beforeunload', onBeforeUnload)\n document.addEventListener('visibilitychange', onVisibilityChange)\n document.addEventListener('freeze', onFreeze)\n document.addEventListener('resume', onResume)\n}\n\nfunction detachLifecycle(): void {\n window.removeEventListener('beforeunload', onBeforeUnload)\n document.removeEventListener('visibilitychange', onVisibilityChange)\n document.removeEventListener('freeze', onFreeze)\n document.removeEventListener('resume', onResume)\n}\n\nfunction watchTrack(cap: LiveCapture): void {\n const track = cap.stream.getAudioTracks()[0]\n if (!track) return\n track.onended = () => void handleInterruption('microfone desconectado')\n track.onmute = () => void handleInterruption('microfone silenciado pelo sistema')\n}\n\n// ---------------------------------------------------------------------------\n// Transições\n// ---------------------------------------------------------------------------\n\nasync function handleInterruption(reason: string): Promise<void> {\n if (!live || live.session.state !== 'recording') return\n const cap = live\n cap.interruptedAtMs = Date.now()\n cap.session.state = 'interrupted'\n cap.session.accumulatedMs = elapsedMs(cap.session)\n cap.session.resumedAtEpochMs = null\n lastError = reason\n\n if (cap.rotateTimer) clearTimeout(cap.rotateTimer)\n try {\n if (cap.recorder?.state === 'recording') cap.recorder.stop()\n else await closeSegment(cap, true)\n } catch {\n await closeSegment(cap, true)\n }\n await releaseWakeLock(cap)\n await putSession(cap.session)\n emit()\n}\n\nexport interface StartOptions {\n tenantId: string\n userId: string\n subjectId?: string\n subjectName?: string\n appointmentId?: string\n locale?: string\n templateKeys?: string[]\n consentMode?: ConsentMode\n /** Chamado assim que a linha do servidor deve existir. */\n onSessionCreated?: (session: IdbSession, consentMode?: ConsentMode) => Promise<void> | void\n}\n\n/**\n * Começa a gravar. A ordem aqui importa: checagem de suporte → saúde do\n * armazenamento → permissão do microfone → linha local → PRIMEIRO BYTE. A linha\n * no servidor é notificada depois, porque uma gravação que depende da rede para\n * COMEÇAR já nasce frágil — e o id é gerado aqui, então não há round-trip.\n */\nexport async function startCapture(opts: StartOptions): Promise<{ ok: true; sessionId: string } | { ok: false; reason: string }> {\n if (live) return { ok: false, reason: 'já existe uma gravação em andamento' }\n if (!isCaptureSupported()) return { ok: false, reason: 'este navegador não suporta gravação de áudio' }\n\n const health = await checkStorageHealth()\n if (!health.ok) return { ok: false, reason: health.reason ?? 'armazenamento local indisponível' }\n\n const mimeType = pickMimeType()!\n let stream: MediaStream\n try {\n stream = await navigator.mediaDevices.getUserMedia({\n audio: audioConstraints(),\n })\n } catch (err) {\n const name = (err as DOMException)?.name\n return {\n ok: false,\n reason:\n name === 'NotAllowedError'\n ? 'permissão de microfone negada'\n : name === 'NotFoundError'\n ? 'nenhum microfone encontrado'\n : 'não foi possível acessar o microfone',\n }\n }\n\n const now = Date.now()\n const session: IdbSession = {\n id: crypto.randomUUID(),\n tenantId: opts.tenantId,\n subjectId: opts.subjectId,\n subjectName: opts.subjectName,\n appointmentId: opts.appointmentId,\n userId: opts.userId,\n mimeType,\n locale: opts.locale ?? 'pt-BR',\n startedAtEpochMs: now,\n resumedAtEpochMs: now,\n accumulatedMs: 0,\n lastTickEpochMs: now,\n state: 'recording',\n segIndex: 0,\n templateKeys: opts.templateKeys ?? [],\n }\n await putSession(session)\n\n const cap: LiveCapture = {\n session,\n stream,\n recorder: null,\n buffer: [],\n pieceIndex: 0,\n segStartOffsetMs: 0,\n wakeLock: null,\n heartbeat: null,\n rotateTimer: null,\n rotating: false,\n interruptedAtMs: null,\n meter: attachLevelMeter(stream),\n }\n live = cap\n lastError = undefined\n\n startRecorder(cap)\n armRotation(cap)\n watchTrack(cap)\n attachLifecycle()\n void acquireWakeLock(cap)\n\n cap.heartbeat = setInterval(() => {\n if (!live) return\n live.session.lastTickEpochMs = Date.now()\n // The accumulated time too: it was only written on pause/stop/end, so a\n // tab closed mid-encounter left the session at 0 while half an hour of\n // audio sat on disk — and the recovery banner offered \"00:00\" for it.\n live.session.accumulatedMs = elapsedMs(live.session)\n live.session.resumedAtEpochMs = Date.now()\n void putSession(live.session)\n emit()\n }, HEARTBEAT_MS)\n\n // A linha do servidor entra depois do primeiro byte, de propósito.\n try {\n await opts.onSessionCreated?.(session, opts.consentMode)\n } catch {\n // Rede indisponível no start não pode abortar a gravação: o pump reconcilia\n // depois, e áudio no IndexedDB sem linha no servidor ainda é recuperável.\n }\n\n emit()\n return { ok: true, sessionId: session.id }\n}\n\n/**\n * Pausa. A TRACK CONTINUA VIVA de propósito: soltar o stream e chamar\n * `getUserMedia` de novo no resume arrisca um segundo prompt de permissão no\n * meio da consulta, e um prompt no meio de uma consulta é uma gravação perdida.\n */\nexport async function pauseCapture(): Promise<void> {\n if (!live || live.session.state !== 'recording') return\n const cap = live\n cap.session.accumulatedMs = elapsedMs(cap.session)\n cap.session.resumedAtEpochMs = null\n cap.session.state = 'paused'\n\n if (cap.rotateTimer) clearTimeout(cap.rotateTimer)\n if (cap.recorder?.state === 'recording') {\n cap.rotating = false\n cap.recorder.stop() // fecha o segmento em aberto como parcial\n }\n await releaseWakeLock(cap)\n await putSession(cap.session)\n emit()\n}\n\nexport async function resumeCapture(): Promise<void> {\n if (!live || (live.session.state !== 'paused' && live.session.state !== 'interrupted')) return\n const cap = live\n\n // Retomar depois de uma INTERRUPÇÃO grava o buraco. Retomar de uma PAUSA não:\n // pausa é intenção declarada e todo mundo sabe que houve um intervalo.\n if (cap.session.state === 'interrupted' && cap.interruptedAtMs) {\n await writeGapSegment(cap, Date.now() - cap.interruptedAtMs)\n }\n cap.interruptedAtMs = null\n\n const track = cap.stream.getAudioTracks()[0]\n if (!track || track.readyState === 'ended') {\n try {\n cap.stream = await navigator.mediaDevices.getUserMedia({ audio: audioConstraints() })\n cap.meter?.stop()\n cap.meter = attachLevelMeter(cap.stream)\n watchTrack(cap)\n } catch {\n lastError = 'não foi possível reabrir o microfone'\n emit()\n return\n }\n }\n\n cap.session.state = 'recording'\n cap.session.resumedAtEpochMs = Date.now()\n lastError = undefined\n await putSession(cap.session)\n\n startRecorder(cap)\n armRotation(cap)\n void acquireWakeLock(cap)\n emit()\n}\n\n/**\n * Encerra. Devolve o id para a tela navegar para a sessão. O upload NÃO espera\n * por isto — o pump já subiu quase tudo durante a gravação; o que sobra aqui é\n * o último segmento.\n */\nexport async function stopCapture(): Promise<string | null> {\n if (!live) return null\n const cap = live\n const sessionId = cap.session.id\n\n cap.session.accumulatedMs = elapsedMs(cap.session)\n cap.session.resumedAtEpochMs = null\n cap.session.state = 'stopping'\n await putSession(cap.session)\n emit()\n\n if (cap.rotateTimer) clearTimeout(cap.rotateTimer)\n if (cap.heartbeat) clearInterval(cap.heartbeat)\n\n if (cap.recorder?.state === 'recording') {\n cap.rotating = false\n await new Promise<void>((resolve) => {\n const rec = cap.recorder!\n const prev = rec.onstop\n rec.onstop = (ev) => {\n // Deixa o handler original fechar o segmento, depois resolve.\n if (typeof prev === 'function') prev.call(rec, ev)\n setTimeout(resolve, 0)\n }\n rec.stop()\n })\n } else {\n await closeSegment(cap, true)\n }\n\n for (const track of cap.stream.getTracks()) track.stop()\n await releaseWakeLock(cap)\n detachLifecycle()\n\n const finished = await getSession(sessionId)\n if (finished) {\n finished.state = 'idle'\n await putSession(finished)\n }\n\n live?.meter?.stop()\n live = null\n emit()\n return sessionId\n}\n\n/**\n * Reata a UI a uma sessão órfã sem religar o microfone. Um browser não permite\n * reabrir o mic sem gesto do usuário depois de um reload, então \"retomar\" é\n * sempre um clique — o que esta função faz é deixar o pill e o banner\n * enxergarem a sessão de novo.\n */\nexport async function adoptOrphan(session: IdbSession): Promise<void> {\n if (live) return\n const pieces = await getPieces(session.id, session.segIndex)\n\n live = {\n session: { ...session, state: 'interrupted' },\n stream: new MediaStream(),\n recorder: null,\n buffer: pieces.map((p) => p.blob),\n pieceIndex: pieces.length,\n segStartOffsetMs: 0,\n wakeLock: null,\n heartbeat: null,\n rotateTimer: null,\n rotating: false,\n interruptedAtMs: session.lastTickEpochMs,\n // An adopted orphan has no open mic — nothing to measure.\n meter: null,\n }\n // O rabo que sobrou vira segmento parcial agora, não quando o usuário decidir:\n // recuperar BYTES nunca espera decisão humana.\n const adopted = live\n if (pieces.length > 0) await closeSegment(adopted, true)\n adopted.session.state = 'interrupted'\n await putSession(adopted.session)\n lastError = 'gravação interrompida'\n emit()\n}\n\n/** Descarta a sessão viva sem apagar nada — o servidor decide o que fazer com o áudio. */\nexport async function releaseCapture(): Promise<void> {\n if (!live) return\n if (live.rotateTimer) clearTimeout(live.rotateTimer)\n if (live.heartbeat) clearInterval(live.heartbeat)\n for (const track of live.stream.getTracks()) track.stop()\n await releaseWakeLock(live)\n detachLifecycle()\n live?.meter?.stop()\n live = null\n lastError = undefined\n emit()\n}\n\nexport function getLiveSessionId(): string | null {\n return live?.session.id ?? null\n}\n","// Nomes físicos das tabelas do scribe (prefixo plg_scribe_*). Importe T e use\n// T.<chave> no provider, para um rename cair em exatamente um lugar.\nexport const T = {\n sessions: 'plg_scribe_sessions',\n segments: 'plg_scribe_segments',\n generations: 'plg_scribe_generations',\n} as const\n\n/** Bucket privado do áudio. A gramática do caminho está em `storagePath()`. */\nexport const AUDIO_BUCKET = 'scribe-audio'\n\n/**\n * {tenant_id}/{yyyy}/{mm}/{session_id}/{seg_index:05d}.{ext}\n *\n * Segmento 1 é a chave do isolamento (toda policy pendura nele); 2-3 dão\n * varredura por prefixo para retenção; 4 agrupa a sessão e é inadivinhável;\n * 5 ordena lexicograficamente = cronologicamente, o que faz `list()` já voltar\n * na ordem certa sem sort.\n */\nexport function storagePath(args: {\n tenantId: string\n sessionId: string\n segIndex: number\n startedAt: Date\n ext: string\n}): string {\n const yyyy = String(args.startedAt.getUTCFullYear())\n const mm = String(args.startedAt.getUTCMonth() + 1).padStart(2, '0')\n const seg = String(args.segIndex).padStart(5, '0')\n return `${args.tenantId}/${yyyy}/${mm}/${args.sessionId}/${seg}.${args.ext}`\n}\n\n/** Prefixo de uma sessão inteira — é o que \"apagar o áudio\" varre. */\nexport function sessionPrefix(args: { tenantId: string; sessionId: string; startedAt: Date }): string {\n const yyyy = String(args.startedAt.getUTCFullYear())\n const mm = String(args.startedAt.getUTCMonth() + 1).padStart(2, '0')\n return `${args.tenantId}/${yyyy}/${mm}/${args.sessionId}`\n}\n\n/** Extensão a partir do mime negociado — o regex da policy só aceita estas quatro. */\nexport function extForMime(mimeType: string): string {\n if (mimeType.includes('ogg')) return 'ogg'\n if (mimeType.includes('mp4')) return 'm4a'\n if (mimeType.includes('mpeg')) return 'mp3'\n return 'webm'\n}\n","// ---------------------------------------------------------------------------\n// O pump — o que leva os bytes do disco do usuário para o servidor.\n//\n// Regras que ele nunca quebra, porque cada uma corresponde a um jeito real de\n// perder uma consulta:\n//\n// 1. NUNCA descarta um segmento. Não existe caminho de código que remova um\n// segmento não enviado. Backoff cresce até o teto e fica lá tentando.\n// 2. NUNCA para de tentar. Não há contador máximo de tentativas — a rede que\n// caiu às 14h volta às 15h e o segmento sobe.\n// 3. Roda em TODO boot, não só durante gravação. É assim que o rabo de uma\n// sessão que morreu ontem chega hoje.\n// 4. 409 do Storage é SUCESSO. `upsert:false` faz o re-envio do mesmo caminho\n// falhar com \"Duplicate\" — e \"já está lá\" é precisamente o que queríamos.\n// Tratar isso como erro faria o replay girar para sempre.\n//\n// Singleton de módulo, como a captura, e pelo mesmo motivo: nada aqui pode\n// depender de um componente estar montado.\n// ---------------------------------------------------------------------------\n\nimport { getSupabaseClientOptional } from '@fayz-ai/core'\nimport {\n claimUploadable,\n getSegment,\n putSegment,\n putSession,\n getSession,\n reclaimStuckUploads,\n type IdbSegment,\n} from './idb'\nimport { AUDIO_BUCKET, T, extForMime, storagePath } from '../data/tables'\n\n/** Quantos segmentos sobem em paralelo. 2 é suficiente: um segmento tem ~90 KB. */\nconst CONCURRENCY = 2\n/** Intervalo ocioso. Um segmento novo aparece a cada 30 s; olhar a cada 3 s é folgado. */\nconst IDLE_POLL_MS = 3_000\nconst BACKOFF_BASE_MS = 500\nconst BACKOFF_CEILING_MS = 30_000\n\nconst CHANNEL_NAME = 'fayz.scribe'\n\nlet running = false\nlet timer: ReturnType<typeof setTimeout> | null = null\nlet channel: BroadcastChannel | null = null\n/** Id desta aba. Menor id ganha a eleição de líder. */\nconst tabId = typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : String(Math.random())\nlet isLeader = true\nlet peerIds = new Set<string>()\n\n// ---------------------------------------------------------------------------\n// Eleição de líder\n// ---------------------------------------------------------------------------\n\n/**\n * Duas abas subindo o mesmo segmento é SEGURO (`upsert:false` + 409-como-sucesso\n * garantem isso), só desperdiça banda. O canal existe mesmo assim porque a aba\n * B precisa saber que há gravação em curso na aba A — senão ela oferece um\n * segundo botão \"Iniciar\" e o usuário grava a mesma consulta duas vezes.\n */\nfunction setupChannel(): void {\n if (typeof BroadcastChannel === 'undefined' || channel) return\n channel = new BroadcastChannel(CHANNEL_NAME)\n channel.onmessage = (ev: MessageEvent) => {\n const msg = ev.data as { type?: string; tabId?: string }\n if (msg?.type === 'hello' && msg.tabId) {\n peerIds.add(msg.tabId)\n channel?.postMessage({ type: 'ack', tabId })\n recomputeLeader()\n } else if (msg?.type === 'ack' && msg.tabId) {\n peerIds.add(msg.tabId)\n recomputeLeader()\n } else if (msg?.type === 'bye' && msg.tabId) {\n peerIds.delete(msg.tabId)\n recomputeLeader()\n }\n }\n channel.postMessage({ type: 'hello', tabId })\n window.addEventListener('pagehide', () => channel?.postMessage({ type: 'bye', tabId }))\n}\n\nfunction recomputeLeader(): void {\n const all = [tabId, ...peerIds].sort()\n isLeader = all[0] === tabId\n}\n\n// ---------------------------------------------------------------------------\n// Upload de um segmento\n// ---------------------------------------------------------------------------\n\nfunction isDuplicateError(err: unknown): boolean {\n const e = err as { statusCode?: string | number; status?: number; message?: string; error?: string }\n const status = Number(e?.statusCode ?? e?.status)\n if (status === 409) return true\n const text = `${e?.message ?? ''} ${e?.error ?? ''}`.toLowerCase()\n return text.includes('duplicate') || text.includes('already exists') || text.includes('resource already')\n}\n\nfunction nextBackoff(attempts: number): number {\n const base = Math.min(BACKOFF_CEILING_MS, BACKOFF_BASE_MS * 2 ** attempts)\n // Jitter para N abas/segmentos não baterem no servidor no mesmo milissegundo\n // depois que a rede volta.\n return Date.now() + base + Math.floor(Math.random() * 500)\n}\n\nasync function uploadOne(segment: IdbSegment): Promise<void> {\n const supabase = getSupabaseClientOptional() as any\n if (!supabase) return\n\n const session = await getSession(segment.sessionId)\n if (!session) {\n // Sessão sumiu do IndexedDB mas o segmento ficou: não há como montar o\n // caminho (falta tenant e data). Marcar falho e deixar o boot limpar.\n await putSegment({ ...segment, uploadState: 'failed', nextAttemptAt: nextBackoff(segment.attempts + 1), attempts: segment.attempts + 1 })\n return\n }\n\n await putSegment({ ...segment, uploadState: 'uploading' })\n\n // Buraco não tem áudio: só registra a linha e segue. É por isso que ele passa\n // pelo pump em vez de ir direto ao servidor — assim o manifesto recebe os\n // segmentos na ordem, com ou sem bytes.\n if (segment.gap || !segment.blob) {\n await recordSegment(supabase, session.id, segment, null)\n await putSegment({ ...segment, blob: null, uploadState: 'uploaded', storagePath: null })\n return\n }\n\n const path = storagePath({\n tenantId: session.tenantId,\n sessionId: session.id,\n segIndex: segment.segIndex,\n startedAt: new Date(session.startedAtEpochMs),\n ext: extForMime(session.mimeType),\n })\n\n try {\n const { error } = await supabase.storage.from(AUDIO_BUCKET).upload(path, segment.blob, {\n contentType: session.mimeType,\n // Sem upsert de propósito: combina com a ausência de policy de UPDATE no\n // bucket. Um caminho gravado nunca é trocado por outro conteúdo.\n upsert: false,\n })\n if (error && !isDuplicateError(error)) throw error\n\n await recordSegment(supabase, session.id, segment, path)\n\n // A speechless clip never becomes a transcription request: `skipped` is\n // terminal, so the drain walks past it. Saves the call AND prevents the\n // hallucination that would enter the record as the patient's words.\n if (segment.silent) {\n await supabase\n .from(T.segments)\n .update({ stt_state: 'skipped' })\n .eq('session_id', session.id)\n .eq('seg_index', segment.segIndex)\n }\n\n // Só agora o blob local pode sumir. Fazer isso antes do RPC deixaria uma\n // janela em que o áudio não está nem aqui nem lá.\n await putSegment({ ...segment, blob: null, uploadState: 'uploaded', storagePath: path })\n } catch (err) {\n const attempts = segment.attempts + 1\n await putSegment({\n ...segment,\n uploadState: 'failed',\n attempts,\n nextAttemptAt: nextBackoff(attempts),\n })\n }\n}\n\nasync function recordSegment(\n supabase: any,\n sessionId: string,\n segment: IdbSegment,\n path: string | null,\n): Promise<void> {\n const { error } = await supabase.rpc('scribe_record_segment', {\n p_payload: {\n session_id: sessionId,\n seg_index: segment.segIndex,\n storage_path: path,\n bytes: segment.bytes,\n start_offset_ms: segment.startOffsetMs,\n // Duração NOMINAL. O servidor sobrescreve com a medida do STT quando ela\n // chega — o cliente conta tempo, o STT conta amostras.\n duration_ms: segment.nominalDurationMs,\n partial: segment.partial,\n gap: segment.gap,\n },\n })\n if (error) throw error\n}\n\n// ---------------------------------------------------------------------------\n// Loop\n// ---------------------------------------------------------------------------\n\nasync function tick(): Promise<void> {\n if (!running) return\n\n try {\n // Offline: não gasta tentativa (que só inflaria o backoff). O listener de\n // 'online' religa o loop na hora em que a rede volta.\n if (typeof navigator !== 'undefined' && navigator.onLine === false) return\n if (!isLeader) return\n if (!getSupabaseClientOptional()) return\n\n const batch = await claimUploadable(CONCURRENCY)\n if (batch.length === 0) return\n\n await Promise.all(\n batch.map(async (seg) => {\n // Reler antes de subir: outra volta do loop (ou outra aba) pode ter\n // resolvido este segmento desde o `claim`.\n const fresh = await getSegment(seg.sessionId, seg.segIndex)\n if (!fresh || fresh.uploadState === 'uploaded') return\n await uploadOne(fresh)\n }),\n )\n } catch {\n // Um erro no loop nunca pode matar o loop.\n } finally {\n if (running) timer = setTimeout(() => void tick(), IDLE_POLL_MS)\n }\n}\n\nfunction onOnline(): void {\n if (!running) return\n if (timer) clearTimeout(timer)\n void tick()\n}\n\n/** Acorda o pump agora, sem esperar o poll. Chamado ao fechar um segmento. */\nexport function kickPump(): void {\n if (!running) return\n if (timer) clearTimeout(timer)\n timer = setTimeout(() => void tick(), 0)\n}\n\nexport function startPump(): void {\n if (running) return\n running = true\n setupChannel()\n if (typeof window !== 'undefined') window.addEventListener('online', onOnline)\n // Devolve à fila o que uma aba morta deixou preso em `uploading` — sem isto\n // esses segmentos ficariam invisíveis para sempre.\n void reclaimStuckUploads().finally(() => void tick())\n}\n\nexport function stopPump(): void {\n running = false\n if (timer) clearTimeout(timer)\n timer = null\n if (typeof window !== 'undefined') window.removeEventListener('online', onOnline)\n channel?.postMessage({ type: 'bye', tabId })\n channel?.close()\n channel = null\n peerIds = new Set()\n}\n\nexport function isPumpLeader(): boolean {\n return isLeader\n}\n\n/**\n * Marca a sessão como encerrada no servidor. Separado do `stopCapture` porque a\n * captura não pode depender da rede para terminar.\n */\nexport async function finalizeSession(sessionId: string, wallDurationMs: number): Promise<void> {\n const supabase = getSupabaseClientOptional() as any\n if (!supabase) return\n const local = await getSession(sessionId)\n await supabase\n .from('plg_scribe_sessions')\n .update({\n status: 'uploading',\n ended_at: new Date().toISOString(),\n wall_duration_ms: wallDurationMs,\n updated_at: new Date().toISOString(),\n })\n .eq('id', sessionId)\n if (local) {\n local.state = 'idle'\n await putSession(local)\n }\n}\n","// ---------------------------------------------------------------------------\n// initScribeRuntime — o único ponto de entrada do lado durável.\n//\n// Chamado uma vez pela factory do plugin. Depois disso a gravação existe\n// independente de qualquer árvore React: o React apenas assina.\n//\n// A ordem do boot não é arbitrária. Primeiro o pump (recuperar BYTES nunca\n// espera decisão de usuário), depois a varredura de órfãs, e só então a UI\n// pergunta o que fazer. Um usuário que ignore o banner por uma hora ainda tem\n// todo o áudio no servidor quando voltar.\n// ---------------------------------------------------------------------------\n\nimport { getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core'\nimport {\n findOrphanSessions,\n getSession,\n isIdbAvailable,\n putSession,\n type IdbSession,\n} from './idb'\nimport {\n adoptOrphan,\n elapsedMs,\n getSnapshot,\n getLiveSessionId,\n pauseCapture,\n releaseCapture,\n resumeCapture,\n startCapture,\n stopCapture,\n subscribeCapture,\n type StartOptions,\n} from './capture'\nimport { finalizeSession, kickPump, startPump } from './pump'\nimport type { ConsentMode, ScribeRetentionConfig } from '../types'\n\nexport * from './idb'\nexport * from './capture'\nexport { startPump, stopPump, kickPump, isPumpLeader, finalizeSession } from './pump'\n\nlet initialized = false\nlet retention: ScribeRetentionConfig = {\n audioDays: 90,\n requireConsent: true,\n defaultConsentMode: 'verbal',\n}\n\n/** Sessões órfãs achadas no boot — a UI lê isto para desenhar o banner. */\n/** Below this an interrupted session has nothing worth recovering. */\nconst TRIVIAL_ORPHAN_MS = 5_000\n\n/**\n * How long an orphan session actually ran.\n *\n * `accumulatedMs` is written by the heartbeat, but sessions recorded BEFORE\n * that fix sit at 0 with audio on disk; the wall clock is the reliable floor\n * there. The larger of the two is the only reading that neither discards a real\n * recording nor inflates an empty one.\n */\nexport function orphanDurationMs(session: IdbSession): number {\n const wall = Math.max(0, session.lastTickEpochMs - session.startedAtEpochMs)\n return Math.max(session.accumulatedMs, wall)\n}\n\nlet orphans: IdbSession[] = []\nconst orphanListeners = new Set<(list: IdbSession[]) => void>()\n\nexport function subscribeOrphans(fn: (list: IdbSession[]) => void): () => void {\n orphanListeners.add(fn)\n fn(orphans)\n return () => orphanListeners.delete(fn)\n}\n\nfunction emitOrphans(): void {\n for (const fn of orphanListeners) {\n try {\n fn(orphans)\n } catch {\n /* um assinante quebrado não derruba a recuperação */\n }\n }\n}\n\nexport interface ScribeRuntimeOptions {\n retention?: Partial<ScribeRetentionConfig>\n}\n\nexport function initScribeRuntime(options?: ScribeRuntimeOptions): void {\n if (initialized) return\n if (typeof window === 'undefined' || !isIdbAvailable()) return\n initialized = true\n retention = { ...retention, ...options?.retention }\n\n // O pump primeiro, e sem await: ele começa a drenar enquanto o resto do boot\n // acontece. Um segmento de ontem já está subindo antes de a tela existir.\n startPump()\n\n void (async () => {\n try {\n const found = await findOrphanSessions()\n // A session with no real audio is not recovery, it is litter: offering\n // three choices for a 00:00 recording spends attention on nothing.\n const worthless = found.filter((s) => orphanDurationMs(s) < TRIVIAL_ORPHAN_MS)\n for (const session of worthless) {\n try {\n await discardSession(session.id)\n } catch {\n // Could not clean now — the next boot tries again.\n }\n }\n orphans = found.filter((s) => orphanDurationMs(s) >= TRIVIAL_ORPHAN_MS)\n if (orphans.length > 0) {\n // Reata a mais recente para o pill enxergá-la; as outras ficam listadas\n // no banner. Adotar já converte pieces soltos em segmento parcial.\n const latest = orphans.reduce((a, b) => (a.lastTickEpochMs > b.lastTickEpochMs ? a : b))\n await adoptOrphan(latest)\n kickPump()\n }\n emitOrphans()\n } catch {\n // Boot da recuperação nunca pode derrubar o app.\n }\n })()\n}\n\n// ---------------------------------------------------------------------------\n// Ações de alto nível (o que a UI chama)\n// ---------------------------------------------------------------------------\n\nexport interface BeginSessionArgs {\n subjectId?: string\n subjectName?: string\n appointmentId?: string\n userId: string\n locale?: string\n templateKeys?: string[]\n consentMode?: ConsentMode\n}\n\n/**\n * Começa uma sessão: grava primeiro, avisa o servidor depois. Se o INSERT\n * falhar (offline), a gravação segue — o áudio está no IndexedDB e o pump\n * reconcilia. O caso contrário, exigir rede para começar, transformaria uma\n * queda de wifi em consulta perdida.\n */\nexport async function beginSession(\n args: BeginSessionArgs,\n): Promise<{ ok: true; sessionId: string } | { ok: false; reason: string }> {\n const tenantId = getActiveTenantId()\n if (!tenantId) return { ok: false, reason: 'nenhum tenant ativo' }\n\n const opts: StartOptions = {\n tenantId,\n userId: args.userId,\n subjectId: args.subjectId,\n subjectName: args.subjectName,\n appointmentId: args.appointmentId,\n locale: args.locale,\n templateKeys: args.templateKeys,\n consentMode: args.consentMode ?? retention.defaultConsentMode,\n onSessionCreated: async (session, consentMode) => {\n const supabase = getSupabaseClientOptional() as any\n if (!supabase) return\n const retentionUntil = new Date(Date.now() + retention.audioDays * 86_400_000)\n await supabase.from('plg_scribe_sessions').insert({\n id: session.id,\n tenant_id: session.tenantId,\n subject_id: session.subjectId ?? null,\n appointment_id: session.appointmentId ?? null,\n context_kind: session.appointmentId ? 'appointment' : session.subjectId ? 'person' : 'standalone',\n owner_user_id: session.userId,\n status: 'recording',\n locale: session.locale,\n mime_type: session.mimeType,\n started_at: new Date(session.startedAtEpochMs).toISOString(),\n // O consentimento é carimbado no INSERT, não depois: a linha nunca\n // existe sem a base legal do que ela guarda.\n consent_at: retention.requireConsent ? new Date().toISOString() : null,\n consent_mode: retention.requireConsent ? (consentMode ?? retention.defaultConsentMode) : null,\n retention_until: retentionUntil.toISOString().slice(0, 10),\n })\n },\n }\n\n const result = await startCapture(opts)\n if (result.ok) kickPump()\n return result\n}\n\nexport async function pauseSession(): Promise<void> {\n await pauseCapture()\n const id = getLiveSessionId()\n if (!id) return\n kickPump()\n await patchStatus(id, 'paused')\n}\n\nexport async function resumeSession(): Promise<void> {\n await resumeCapture()\n const id = getLiveSessionId()\n if (id) await patchStatus(id, 'recording')\n}\n\n/** Encerra e devolve o id, para a tela navegar até a sessão. */\nexport async function endSession(): Promise<string | null> {\n const snapshot = getSnapshot()\n const wall = snapshot.elapsedMs\n const id = await stopCapture()\n if (!id) return null\n kickPump()\n await finalizeSession(id, wall)\n orphans = orphans.filter((o) => o.id !== id)\n emitOrphans()\n return id\n}\n\n/**\n * Descarte SUAVE. `abandoned`, não delete: o áudio fica até `retention_until`.\n * Hard-delete de sessão de saúde por clique errado é um dano que não tem\n * desfazer, e \"descartar\" quase sempre significa \"não quero o documento\", não\n * \"apague a consulta\".\n */\nexport async function discardSession(sessionId?: string): Promise<void> {\n const id = sessionId ?? getLiveSessionId()\n if (!id) return\n await releaseCapture()\n await patchStatus(id, 'abandoned')\n const local = await getSession(id)\n if (local) {\n local.state = 'idle'\n await putSession(local)\n }\n orphans = orphans.filter((o) => o.id !== id)\n emitOrphans()\n}\n\n/** Dispensa o banner sem mexer no estado da sessão — ela continua no servidor. */\nexport function dismissOrphan(sessionId: string): void {\n orphans = orphans.filter((o) => o.id !== sessionId)\n emitOrphans()\n}\n\nasync function patchStatus(sessionId: string, status: string): Promise<void> {\n const supabase = getSupabaseClientOptional() as any\n if (!supabase) return\n try {\n await supabase\n .from('plg_scribe_sessions')\n .update({ status, updated_at: new Date().toISOString() })\n .eq('id', sessionId)\n } catch {\n // Status é conveniência de UI; o manifesto de segmentos é a verdade. Falhar\n // aqui não pode interromper a gravação.\n }\n}\n\nexport { subscribeCapture, getSnapshot, elapsedMs, getLiveSessionId }\n","// ---------------------------------------------------------------------------\n// A ponte entre o runtime (que vive fora do React) e os componentes.\n//\n// O runtime é a fonte da verdade; esta store é uma projeção dele. Ela nunca\n// decide nada — quem decide é `runtime/capture.ts`. Se um componente pudesse\n// escrever estado de gravação aqui, teríamos duas verdades e a que sobrevive à\n// navegação seria a errada.\n// ---------------------------------------------------------------------------\n\nimport { create } from 'zustand'\nimport type { CaptureState } from './types'\nimport type { IdbSession } from './runtime/idb'\nimport { getSnapshot, subscribeCapture, subscribeOrphans } from './runtime'\n\nexport interface ScribeUiState {\n state: CaptureState\n sessionId: string | null\n subjectId?: string\n subjectName?: string\n elapsedMs: number\n segIndex: number\n error?: string\n /** Sessões interrompidas encontradas no boot. Alimentam o banner. */\n orphans: IdbSession[]\n /** Sessão cuja tela está aberta — não necessariamente a que está gravando. */\n viewingSessionId: string | null\n setViewing: (id: string | null) => void\n}\n\nexport const useScribeStore = create<ScribeUiState>((set) => ({\n ...getSnapshot(),\n orphans: [],\n viewingSessionId: null,\n setViewing: (id) => set({ viewingSessionId: id }),\n}))\n\nlet wired = false\n\n/**\n * Liga a store aos assinantes do runtime. Idempotente: a factory do plugin\n * chama uma vez, e chamar de novo (HMR) não duplica assinatura.\n */\nexport function wireScribeStore(): void {\n if (wired) return\n wired = true\n subscribeCapture((snap) => {\n useScribeStore.setState({\n state: snap.state,\n sessionId: snap.sessionId,\n subjectId: snap.subjectId,\n subjectName: snap.subjectName,\n elapsedMs: snap.elapsedMs,\n segIndex: snap.segIndex,\n error: snap.error,\n })\n })\n subscribeOrphans((orphans) => useScribeStore.setState({ orphans }))\n}\n\n/** `mm:ss`, ou `h:mm:ss` quando passa da hora. */\nexport function formatElapsed(ms: number): string {\n const total = Math.max(0, Math.floor(ms / 1000))\n const h = Math.floor(total / 3600)\n const m = Math.floor((total % 3600) / 60)\n const s = total % 60\n const mm = String(m).padStart(2, '0')\n const ss = String(s).padStart(2, '0')\n return h > 0 ? `${h}:${mm}:${ss}` : `${mm}:${ss}`\n}\n","import * as React from 'react'\nimport { createPortal } from 'react-dom'\nimport { Mic, Pause, Play, Square } from 'lucide-react'\nimport { Button } from '@fayz-ai/ui/primitives'\nimport { useScribeStore, formatElapsed } from '../store'\nimport { endSession, getSnapshot, pauseSession, resumeSession } from '../runtime'\nimport type { ScribeLabels } from '../types'\n\n// ---------------------------------------------------------------------------\n// O pill. Sempre montado, nunca remontado.\n//\n// Ele mora em `shell.topbar.end`, que é a única zona global do shell que de fato\n// é renderizada (`AdminShell.tsx:979-980`) e que fica FORA do switch de rota —\n// só o conteúdo da página troca dentro do PageTransition. Por isso navegar não\n// desmonta o pill nem interrompe nada.\n//\n// (`shell.floating` e `PluginManifest.floatingUI` estão declarados nos tipos do\n// core mas nada os renderiza. Construir neles daria silêncio.)\n//\n// Pausar e parar vivem AQUI e em nenhum outro lugar. A tela onde a gravação\n// começou desapareceu no instante em que o usuário navegou — que é o ponto\n// inteiro da feature. Um botão de parar que só existe na tela de origem seria\n// um botão que some quando é mais necessário.\n//\n// Ele também é o artefato de LGPD: a prova de que o titular podia ver, o tempo\n// todo, que a gravação estava em curso.\n// ---------------------------------------------------------------------------\n\nexport interface ScribeRecordingPillProps {\n labels: ScribeLabels\n onOpenSession?: (sessionId: string) => void\n /**\n * Render only on small screens. On desktop the live state belongs to the\n * assistant (FAB pill + panel bar) — two timers for one recording is the app\n * telling you the same thing twice. The mobile portal stays because the FAB\n * is opt-in there, and the visible proof of an ongoing recording is a legal\n * obligation, not decoration.\n */\n mobileOnly?: boolean\n}\n\n/** Abaixo disto o topbar do shell é `hidden md:flex` — ver comentário no portal. */\nconst MOBILE_BREAKPOINT = 768\n\nfunction useIsMobile(): boolean {\n const [isMobile, setIsMobile] = React.useState(\n () => typeof window !== 'undefined' && window.innerWidth < MOBILE_BREAKPOINT,\n )\n React.useEffect(() => {\n const mq = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)\n const onChange = (e: MediaQueryListEvent) => setIsMobile(e.matches)\n setIsMobile(mq.matches)\n mq.addEventListener('change', onChange)\n return () => mq.removeEventListener('change', onChange)\n }, [])\n return isMobile\n}\n\n/**\n * Repinta só quando a aba está visível. `requestAnimationFrame` em vez de\n * `setInterval` porque aba escondida throttla o interval para 1/min — e o\n * cronômetro que volta errado ao focar é pior que um que não anda.\n *\n * O tempo em si nunca é acumulado aqui: `elapsedMs` é recalculado de\n * `Date.now()` no runtime. Este hook só decide QUANDO redesenhar.\n */\nfunction useTicker(active: boolean): number {\n const [, force] = React.useReducer((n: number) => n + 1, 0)\n React.useEffect(() => {\n if (!active) return\n let raf = 0\n let last = 0\n const loop = (t: number) => {\n if (t - last >= 250) {\n last = t\n if (document.visibilityState === 'visible') force()\n }\n raf = requestAnimationFrame(loop)\n }\n raf = requestAnimationFrame(loop)\n const onVis = () => force()\n document.addEventListener('visibilitychange', onVis)\n return () => {\n cancelAnimationFrame(raf)\n document.removeEventListener('visibilitychange', onVis)\n }\n }, [active])\n return 0\n}\n\nexport function ScribeRecordingPill({ labels, onOpenSession, mobileOnly }: ScribeRecordingPillProps) {\n const state = useScribeStore((s) => s.state)\n const sessionId = useScribeStore((s) => s.sessionId)\n const subjectName = useScribeStore((s) => s.subjectName)\n const isMobile = useIsMobile()\n const [busy, setBusy] = React.useState(false)\n\n const active = state === 'recording' || state === 'paused' || state === 'interrupted'\n useTicker(state === 'recording')\n\n // `getSnapshot()` e não o campo da store: aquele só muda quando o runtime\n // emite (transição de estado, giro de segmento), então o cronômetro andava\n // aos pulos. Este recalcula do relógio de parede na hora da leitura.\n //\n // Fora do selector de propósito: o valor muda a cada frame e um selector\n // sobre ele faria a store notificar todo assinante 4x por segundo.\n const elapsedMs = active ? getSnapshot().elapsedMs : 0\n\n const handleToggle = React.useCallback(async () => {\n setBusy(true)\n try {\n if (state === 'recording') await pauseSession()\n else await resumeSession()\n } finally {\n setBusy(false)\n }\n }, [state])\n\n const handleStop = React.useCallback(async () => {\n setBusy(true)\n try {\n const id = await endSession()\n if (id) onOpenSession?.(id)\n } finally {\n setBusy(false)\n }\n }, [onOpenSession])\n\n if (!active || !sessionId) return null\n if (mobileOnly && !isMobile) return null\n\n const tone =\n state === 'recording'\n ? 'bg-destructive/10 text-destructive border-destructive/30'\n : state === 'interrupted'\n ? 'bg-amber-500/10 text-amber-700 border-amber-500/30 dark:text-amber-400'\n : 'bg-muted text-muted-foreground border-border'\n\n const content = (\n <div\n className={`flex items-center gap-2 rounded-full border px-2.5 py-1 ${tone}`}\n role=\"status\"\n aria-live=\"polite\"\n aria-label={`${labels.sessionSingular}: ${formatElapsed(elapsedMs)}`}\n >\n <button\n type=\"button\"\n onClick={() => onOpenSession?.(sessionId)}\n className=\"flex items-center gap-1.5 text-sm font-medium tabular-nums\"\n title={subjectName ?? labels.sessionSingular}\n >\n <span className=\"relative flex h-2 w-2 shrink-0\">\n {state === 'recording' && (\n <span className=\"absolute inline-flex h-full w-full animate-ping rounded-full bg-current opacity-60\" />\n )}\n <span className=\"relative inline-flex h-2 w-2 rounded-full bg-current\" />\n </span>\n {formatElapsed(elapsedMs)}\n {subjectName && <span className=\"hidden max-w-[12ch] truncate sm:inline\">· {subjectName}</span>}\n </button>\n\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-6 w-6 p-0\"\n disabled={busy}\n onClick={handleToggle}\n title={state === 'recording' ? labels.pause : labels.resume}\n aria-label={state === 'recording' ? labels.pause : labels.resume}\n >\n {state === 'recording' ? <Pause className=\"h-3.5 w-3.5\" /> : <Play className=\"h-3.5 w-3.5\" />}\n </Button>\n\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-6 w-6 p-0\"\n disabled={busy}\n onClick={handleStop}\n title={labels.finish}\n aria-label={labels.finish}\n >\n <Square className=\"h-3.5 w-3.5\" />\n </Button>\n </div>\n )\n\n // No mobile o topbar do shell é `hidden md:flex` (AppShell.tsx:317, porque\n // `mobileHeader` default é 'minimal'), então o pill sumiria justamente onde\n // ele deixa de ser conveniência e vira o ÚNICO botão de pausa.\n //\n // Um portal fixo resolve dentro do plugin, sem mudar o SDK.\n if (isMobile && typeof document !== 'undefined') {\n return createPortal(\n <div className=\"fixed inset-x-0 top-0 z-50 flex justify-center bg-background/80 p-1.5 backdrop-blur md:hidden\">\n {content}\n </div>,\n document.body,\n )\n }\n\n return content\n}\n\n// ---------------------------------------------------------------------------\n// Botão de iniciar\n// ---------------------------------------------------------------------------\n\nexport interface ScribeStartButtonProps {\n labels: ScribeLabels\n disabled?: boolean\n onStart: () => void\n variant?: 'default' | 'outline'\n size?: 'sm' | 'default'\n}\n\nexport function ScribeStartButton({ labels, disabled, onStart, variant = 'default', size = 'sm' }: ScribeStartButtonProps) {\n const state = useScribeStore((s) => s.state)\n const busy = state === 'recording' || state === 'paused' || state === 'interrupted' || state === 'requesting'\n\n return (\n <Button variant={variant} size={size} disabled={disabled || busy} onClick={onStart} title={labels.start}>\n <Mic className=\"mr-1.5 h-4 w-4\" />\n {labels.start}\n </Button>\n )\n}\n","import * as React from 'react'\nimport { AlertTriangle } from 'lucide-react'\nimport { Button } from '@fayz-ai/ui/primitives'\nimport { useScribeStore, formatElapsed } from '../store'\nimport { discardSession, dismissOrphan, endSession, orphanDurationMs, resumeSession } from '../runtime'\nimport type { ScribeLabels } from '../types'\n\n// The recovery banner is NOT the recovery mechanism: by the time it shows, the\n// pump has been uploading for seconds. Recovering audio never waits for a human\n// decision, because the decision may never come.\n//\n// What the banner decides is what to do with the SESSION — continue, finish and\n// generate, or drop it — so it is non-blocking: ignore it for an hour and the\n// audio is still on the server.\n//\n// \"Resume\" expires. After ~30 minutes the encounter is over, and continuing the\n// same recording would join two distinct moments as if they were one.\n\nconst RESUMABLE_WINDOW_MS = 30 * 60 * 1000\n\nexport interface ScribeRecoveryBannerProps {\n labels: ScribeLabels\n onOpenSession?: (sessionId: string) => void\n}\n\nexport function ScribeRecoveryBanner({ labels, onOpenSession }: ScribeRecoveryBannerProps) {\n const orphans = useScribeStore((s) => s.orphans)\n const state = useScribeStore((s) => s.state)\n const [busy, setBusy] = React.useState<string | null>(null)\n\n // Silent while a recording is live: offering resume/finish/discard for an old\n // session next to the running one is asking someone to end the wrong one.\n const live = state === 'recording' || state === 'paused' || state === 'requesting'\n if (live) return null\n if (orphans.length === 0) return null\n const session = orphans[0]\n\n const interruptedAgoMs = Date.now() - session.lastTickEpochMs\n const canResume = interruptedAgoMs < RESUMABLE_WINDOW_MS\n // Not raw `accumulatedMs` — see orphanDurationMs: older sessions sit at 0 and\n // the banner announced \"00:00\" for half an hour of audio.\n const durationLabel = formatElapsed(orphanDurationMs(session))\n\n const run = async (fn: () => Promise<unknown>) => {\n setBusy(session.id)\n try {\n await fn()\n } finally {\n setBusy(null)\n }\n }\n\n return (\n <div className=\"mx-3 mb-3 flex flex-col gap-2 rounded-lg border border-amber-500/40 bg-amber-500/10 p-3 md:mx-6 md:flex-row md:items-center md:justify-between\">\n <div className=\"flex items-start gap-2\">\n <AlertTriangle className=\"mt-0.5 h-4 w-4 shrink-0 text-amber-600 dark:text-amber-400\" />\n <div className=\"text-sm\">\n <p className=\"font-medium\">\n {labels.sessionSingular} de {durationLabel} foi interrompido\n {session.subjectName ? ` — ${session.subjectName}` : ''}.\n </p>\n <p className=\"text-muted-foreground\">\n O áudio já gravado está seguro e continua sendo enviado.\n </p>\n </div>\n </div>\n\n <div className=\"flex shrink-0 flex-wrap gap-1.5\">\n {canResume && (\n <Button size=\"sm\" disabled={busy === session.id} onClick={() => void run(resumeSession)}>\n {labels.resume}\n </Button>\n )}\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={busy === session.id}\n onClick={() =>\n void run(async () => {\n const id = await endSession()\n dismissOrphan(session.id)\n if (id) onOpenSession?.(id)\n })\n }\n >\n {labels.finish}\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={busy === session.id}\n // Descarte SUAVE: `abandoned`, não delete. O áudio fica até a data de\n // retenção. \"Descartar\" quase sempre quer dizer \"não quero o\n // documento\", e apagar uma consulta por clique errado não tem desfazer.\n onClick={() => void run(() => discardSession(session.id))}\n >\n {labels.discard}\n </Button>\n </div>\n </div>\n )\n}\n","import * as React from 'react'\nimport { Mic, ShieldCheck } from 'lucide-react'\nimport {\n Button,\n Checkbox,\n Modal,\n ModalContent,\n ModalHeader,\n ModalBody,\n ModalFooter,\n ModalTitle,\n} from '@fayz-ai/ui/primitives'\nimport type { ConsentMode, ScribeLabels } from '../types'\n\n// ---------------------------------------------------------------------------\n// O gate de consentimento — antes do primeiro byte, não depois.\n//\n// Áudio de um atendimento é dado pessoal, e numa clínica é dado pessoal\n// SENSÍVEL (LGPD art. 5º II). O tratamento precisa de base legal, e a base\n// precisa ser demonstrável depois. Guardar `consent_at` e `consent_mode` na\n// mesma linha da gravação é o que torna a prova barata — carimbar depois seria\n// documentar uma intenção, não um fato.\n//\n// O diálogo também é onde o profissional é lembrado de PEDIR o consentimento em\n// voz alta. Uma caixa marcada não consente por ninguém: quem consente é o\n// titular. O que a caixa faz é registrar que o profissional afirma tê-lo obtido.\n//\n// A gravação em si nunca cai no reconhecedor nativo do browser\n// (`webkitSpeechRecognition`), que envia o áudio para servidores do Google.\n// Isso adicionaria um operador não declarado, sem DPA e sem base documentada de\n// transferência internacional — e tornaria um pedido de eliminação impossível\n// de honrar. O plugin recusa a subir sem um endpoint de STT próprio; ver o\n// invariante em `createScribePlugin`.\n// ---------------------------------------------------------------------------\n\nexport interface ScribeConsentDialogProps {\n open: boolean\n labels: ScribeLabels\n subjectName?: string\n defaultConsentMode: ConsentMode\n onCancel: () => void\n onConfirm: (mode: ConsentMode) => void\n}\n\nexport function ScribeConsentDialog({\n open,\n labels,\n subjectName,\n defaultConsentMode,\n onCancel,\n onConfirm,\n}: ScribeConsentDialogProps) {\n const [acknowledged, setAcknowledged] = React.useState(false)\n\n React.useEffect(() => {\n if (open) setAcknowledged(false)\n }, [open])\n\n return (\n <Modal open={open} onOpenChange={(next: boolean) => !next && onCancel()}>\n <ModalContent size=\"sm\">\n <ModalHeader>\n <ModalTitle className=\"flex items-center gap-2\">\n <Mic className=\"h-4 w-4\" />\n Iniciar gravação\n </ModalTitle>\n </ModalHeader>\n\n <ModalBody className=\"space-y-3 text-sm\">\n <p className=\"text-muted-foreground\">\n O áudio {subjectName ? `de ${subjectName}` : `deste ${labels.sessionSingular.toLowerCase()}`} será\n gravado e transcrito para gerar um documento. A gravação fica visível na barra superior enquanto durar.\n </p>\n\n <label className=\"flex cursor-pointer items-start gap-2 rounded-md border p-3\">\n <Checkbox checked={acknowledged} onChange={setAcknowledged} className=\"mt-0.5\" />\n <span>\n Informei {subjectName ?? 'o titular'} de que este atendimento será gravado e obtive o consentimento.\n </span>\n </label>\n\n <p className=\"flex items-start gap-2 text-xs text-muted-foreground\">\n <ShieldCheck className=\"mt-0.5 h-3.5 w-3.5 shrink-0\" />\n O áudio é armazenado de forma privada e pode ser apagado a qualquer momento — a transcrição e o\n documento assinado permanecem.\n </p>\n </ModalBody>\n\n <ModalFooter>\n <Button variant=\"outline\" size=\"sm\" onClick={onCancel}>\n Cancelar\n </Button>\n <Button size=\"sm\" disabled={!acknowledged} onClick={() => onConfirm(defaultConsentMode)}>\n {labels.start}\n </Button>\n </ModalFooter>\n </ModalContent>\n </Modal>\n )\n}\n","// The READ side: the screen reads what the SERVER knows, never what local\n// IndexedDB thinks. That is what makes a session recoverable from another\n// device — laptop dies, open the phone, the transcript is there. The WRITE side\n// of the audio is the pump.\n\nimport { getSupabaseClientOptional } from '@fayz-ai/core'\nimport { T, AUDIO_BUCKET, sessionPrefix } from './tables'\nimport type { ScribeGeneration, ScribeSegment, ScribeSession } from '../types'\n\nfunction client(): any {\n const supabase = getSupabaseClientOptional() as any\n if (!supabase) throw new Error('Supabase não inicializado')\n return supabase\n}\n\nfunction mapSession(r: any): ScribeSession {\n return {\n id: r.id,\n tenantId: r.tenant_id,\n subjectId: r.subject_id ?? undefined,\n contextKind: r.context_kind,\n appointmentId: r.appointment_id ?? undefined,\n ownerUserId: r.owner_user_id,\n status: r.status,\n locale: r.locale,\n mimeType: r.mime_type ?? undefined,\n startedAt: r.started_at,\n endedAt: r.ended_at ?? undefined,\n wallDurationMs: r.wall_duration_ms ?? 0,\n audioDurationMs: r.audio_duration_ms ?? 0,\n segmentCount: r.segment_count ?? 0,\n uploadedSegmentCount: r.uploaded_segment_count ?? 0,\n transcribedSegmentCount: r.transcribed_segment_count ?? 0,\n transcriptChars: r.transcript_chars ?? 0,\n consentAt: r.consent_at ?? undefined,\n consentMode: r.consent_mode ?? undefined,\n retentionUntil: r.retention_until ?? undefined,\n audioDeletedAt: r.audio_deleted_at ?? undefined,\n error: r.error ?? undefined,\n metadata: r.metadata ?? {},\n }\n}\n\nfunction mapSegment(r: any): ScribeSegment {\n return {\n sessionId: r.session_id,\n segIndex: r.seg_index,\n tenantId: r.tenant_id,\n storagePath: r.storage_path ?? undefined,\n bytes: r.bytes ?? undefined,\n startOffsetMs: r.start_offset_ms ?? 0,\n durationMs: r.duration_ms ?? undefined,\n partial: !!r.partial,\n gap: !!r.gap,\n uploadState: r.upload_state,\n uploadedAt: r.uploaded_at ?? undefined,\n sttState: r.stt_state,\n sttProvider: r.stt_provider ?? undefined,\n sttAttempts: r.stt_attempts ?? 0,\n sttError: r.stt_error ?? undefined,\n text: r.text ?? undefined,\n words: r.words ?? undefined,\n confidence: r.confidence ?? undefined,\n }\n}\n\nfunction mapGeneration(r: any): ScribeGeneration {\n return {\n id: r.id,\n tenantId: r.tenant_id,\n sessionId: r.session_id,\n templateId: r.template_id ?? undefined,\n templateKey: r.template_key ?? undefined,\n tabOrder: r.tab_order ?? 0,\n title: r.title ?? undefined,\n status: r.status,\n markdown: r.markdown ?? undefined,\n markdownEdited: r.markdown_edited ?? undefined,\n facts: r.facts ?? undefined,\n model: r.model ?? undefined,\n promptVersion: r.prompt_version ?? undefined,\n inputTokens: r.input_tokens ?? undefined,\n outputTokens: r.output_tokens ?? undefined,\n error: r.error ?? undefined,\n documentId: r.document_id ?? undefined,\n createdBy: r.created_by ?? undefined,\n createdAt: r.created_at,\n }\n}\n\nexport async function fetchSession(sessionId: string): Promise<ScribeSession | null> {\n const { data, error } = await client().from(T.sessions).select('*').eq('id', sessionId).maybeSingle()\n if (error) throw error\n return data ? mapSession(data) : null\n}\n\nexport async function fetchSegments(sessionId: string): Promise<ScribeSegment[]> {\n const { data, error } = await client()\n .from(T.segments)\n .select('*')\n .eq('session_id', sessionId)\n .order('seg_index', { ascending: true })\n if (error) throw error\n return (data ?? []).map(mapSegment)\n}\n\nexport async function fetchGenerations(sessionId: string): Promise<ScribeGeneration[]> {\n const { data, error } = await client()\n .from(T.generations)\n .select('*')\n .eq('session_id', sessionId)\n .neq('status', 'discarded')\n .order('tab_order', { ascending: true })\n if (error) throw error\n return (data ?? []).map(mapGeneration)\n}\n\n/** Sessões abertas de qualquer dispositivo — a recuperação cross-device. */\nexport async function fetchOpenSessions(userId: string): Promise<ScribeSession[]> {\n const { data, error } = await client()\n .from(T.sessions)\n .select('*')\n .eq('owner_user_id', userId)\n .in('status', ['recording', 'paused', 'interrupted', 'uploading', 'transcribing', 'ready', 'generating'])\n .order('started_at', { ascending: false })\n .limit(20)\n if (error) throw error\n return (data ?? []).map(mapSession)\n}\n\nexport async function fetchSessionsForSubject(subjectId: string): Promise<ScribeSession[]> {\n const { data, error } = await client()\n .from(T.sessions)\n .select('*')\n .eq('subject_id', subjectId)\n .order('started_at', { ascending: false })\n .limit(50)\n if (error) throw error\n return (data ?? []).map(mapSession)\n}\n\n/**\n * Autosave do rascunho. Escreve SÓ `markdown_edited` — `markdown` é a saída do\n * modelo e nunca é tocada. É a diferença entre as duas que prova a revisão e\n * que alimenta o ajuste de prompt.\n */\nexport async function saveDraft(generationId: string, markdownEdited: string): Promise<void> {\n const { error } = await client()\n .from(T.generations)\n .update({ markdown_edited: markdownEdited, updated_at: new Date().toISOString() })\n .eq('id', generationId)\n if (error) throw error\n}\n\nexport async function discardGeneration(generationId: string): Promise<void> {\n const { error } = await client()\n .from(T.generations)\n .update({ status: 'discarded', updated_at: new Date().toISOString() })\n .eq('id', generationId)\n if (error) throw error\n}\n\n/**\n * Grava o documento na ficha. Três escritas encadeadas:\n *\n * documents o arquétipo — é o que o PersonDocumentsWidget lista\n * plg_forms_documents a extensão com o markdown e as seções parseadas\n * generations fecha o ciclo apontando para o documento criado\n *\n * `kind` fica `'form'` de propósito: assim a lista de documentos da pessoa\n * mostra este documento SEM nenhuma mudança no plugin-forms. O que distingue um\n * documento de scribe é `metadata.scribe`, que é o filtro de qualquer visão\n * específica.\n */\n/**\n * The forms template id for a generation, creating it on first use.\n *\n * `plg_forms_documents.template_id` is NOT NULL — forms models a document as an\n * instance of a tenant template — but a generation from a CODE preset had no\n * row, so saving died on the constraint after recording, transcribing and\n * generating. Loosening the constraint would weaken all of forms; materialising\n * the preset matches the truth: \"Anamnese\" IS a document type of that tenant.\n */\nasync function ensureFormsTemplate(args: {\n tenantId: string\n key: string\n name: string\n category?: string\n}): Promise<string> {\n const supabase = client()\n const { tenantId, key, name, category } = args\n\n const { data: found } = await supabase\n .from('plg_forms_templates')\n .select('id')\n .eq('tenant_id', tenantId)\n .eq('metadata->>scribePresetKey', key)\n .maybeSingle()\n if (found?.id) return found.id as string\n\n const { data: created, error } = await supabase\n .from('plg_forms_templates')\n .insert({\n tenant_id: tenantId,\n name,\n category: category ?? 'general',\n // A narrative document has no fields: empty schema so the builder opens\n // without breaking, provenance in metadata.\n schema: { fields: [], layout: { columns: 12 } },\n metadata: { scribePresetKey: key, narrative: true },\n })\n .select('id')\n .single()\n if (error || !created) throw error ?? new Error('falha ao criar o modelo do documento')\n return created.id as string\n}\n\nexport async function commitGeneration(args: {\n generation: ScribeGeneration\n session: ScribeSession\n markdown: string\n title: string\n sections: Record<string, string>\n edited: boolean\n /** Document type name (\"Anamnese\") — becomes the template name. */\n templateName?: string\n /** Preset category, so the document lands in the right forms drawer. */\n templateCategory?: string\n}): Promise<string> {\n const supabase = client()\n const { generation, session, markdown, title, sections, edited } = args\n\n // No template_id, no insert — see ensureFormsTemplate.\n const templateId =\n generation.templateId ??\n (generation.templateKey\n ? await ensureFormsTemplate({\n tenantId: session.tenantId,\n key: generation.templateKey,\n name: args.templateName ?? title,\n category: args.templateCategory,\n })\n : null)\n if (!templateId) throw new Error('geração sem modelo de documento associado')\n\n const { data: doc, error: docErr } = await supabase\n .from('documents')\n .insert({\n tenant_id: session.tenantId,\n kind: 'form',\n person_id: session.subjectId ?? null,\n title,\n status: 'completed',\n metadata: {\n scribe: {\n sessionId: session.id,\n generationId: generation.id,\n model: generation.model,\n promptVersion: generation.promptVersion,\n edited,\n audioDurationMs: session.audioDurationMs,\n },\n },\n })\n .select('id')\n .single()\n if (docErr || !doc) throw docErr ?? new Error('falha ao criar o documento')\n\n const { error: extErr } = await supabase.from('plg_forms_documents').insert({\n document_id: doc.id,\n tenant_id: session.tenantId,\n template_id: templateId,\n data: {\n markdown,\n sections,\n sourceSessionId: session.id,\n generationId: generation.id,\n },\n })\n if (extErr) throw extErr\n\n const { error: genErr } = await supabase\n .from(T.generations)\n .update({ status: 'committed', document_id: doc.id, updated_at: new Date().toISOString() })\n .eq('id', generation.id)\n if (genErr) throw genErr\n\n await maybeCompleteSession(session.id)\n return doc.id\n}\n\n/** A sessão fecha quando toda geração virou documento ou foi descartada. */\nasync function maybeCompleteSession(sessionId: string): Promise<void> {\n const supabase = client()\n const { data } = await supabase.from(T.generations).select('status').eq('session_id', sessionId)\n if (!data || data.length === 0) return\n const open = data.filter((g: any) => !['committed', 'discarded'].includes(g.status))\n if (open.length > 0) return\n await supabase\n .from(T.sessions)\n .update({ status: 'completed', updated_at: new Date().toISOString() })\n .eq('id', sessionId)\n}\n\n/**\n * Apaga o ÁUDIO e nada mais. Transcrição e documento assinado ficam — é o que\n * torna o direito de eliminação (art. 18 VI) exercível sem destruir o registro\n * clínico, que tem prazo de guarda próprio.\n */\nexport async function deleteSessionAudio(session: ScribeSession): Promise<number> {\n const supabase = client()\n const prefix = sessionPrefix({\n tenantId: session.tenantId,\n sessionId: session.id,\n startedAt: new Date(session.startedAt),\n })\n\n const { data: files } = await supabase.storage.from(AUDIO_BUCKET).list(prefix)\n const paths = (files ?? []).map((f: any) => `${prefix}/${f.name}`)\n if (paths.length > 0) await supabase.storage.from(AUDIO_BUCKET).remove(paths)\n\n await supabase\n .from(T.sessions)\n .update({ audio_deleted_at: new Date().toISOString(), updated_at: new Date().toISOString() })\n .eq('id', session.id)\n\n // Path cleared on the segment rows too: a path pointing at a deleted object\n // would make the STT retry download nothing, forever.\n await supabase\n .from(T.segments)\n .update({ storage_path: null, upload_state: 'missing' })\n .eq('session_id', session.id)\n\n return paths.length\n}\n\n// ---------------------------------------------------------------------------\n// Invocação das edge functions\n// ---------------------------------------------------------------------------\n\nexport async function invokeTranscribe(args: {\n endpoint: string\n sessionId: string\n provider: string\n model?: string\n diarize?: boolean\n}): Promise<{ transcribed: number; failed: number; remaining: number }> {\n const { data, error } = await client().functions.invoke(args.endpoint, {\n body: {\n action: 'drain',\n sessionId: args.sessionId,\n provider: args.provider,\n model: args.model,\n diarize: args.diarize,\n },\n })\n if (error) throw error\n if (data?.error) throw new Error(data.error)\n return { transcribed: data?.transcribed ?? 0, failed: data?.failed ?? 0, remaining: data?.remaining ?? 0 }\n}\n\nexport async function invokeGenerate(args: {\n endpoint: string\n sessionId: string\n templateId?: string\n templateKey?: string\n schema?: unknown\n tabOrder?: number\n}): Promise<{ generationId: string; markdown: string }> {\n const { data, error } = await client().functions.invoke(args.endpoint, {\n body: {\n sessionId: args.sessionId,\n templateId: args.templateId,\n templateKey: args.templateKey,\n schema: args.schema,\n tabOrder: args.tabOrder ?? 0,\n },\n })\n if (error) throw error\n if (data?.error) throw new Error(data.error)\n return { generationId: data.generationId, markdown: data.markdown }\n}\n","import type { ScribeSttConfig } from '../types'\n\n// ---------------------------------------------------------------------------\n// Validação da configuração de STT\n// ---------------------------------------------------------------------------\n//\n// Separada de `transport.ts` de propósito: aquele arquivo importa a conexão da\n// plataforma e só existe no browser, e esta regra precisa ser exercitável num\n// teste puro. É a regra que impede o modo de falha mais caro do plugin, então\n// ela não pode depender de alguém abrir uma tela para ser verificada.\n\n/** A configuração pede o broker da plataforma? É o default. */\nexport function usesFayzTransport(stt: Pick<ScribeSttConfig, 'transport'>): boolean {\n return (stt.transport ?? 'fayz') === 'fayz'\n}\n\n/**\n * Lança se a configuração não descreve um caminho de transcrição próprio.\n *\n * Por que lançar na construção do plugin, e não degradar: sem transporte\n * configurado a única alternativa a mão seria o reconhecedor nativo do browser,\n * que envia o áudio do microfone para servidores do Google. Num atendimento\n * isso é um operador não declarado processando dado sensível — sem DPA, sem\n * base documentada de transferência internacional, e sem como honrar um pedido\n * de eliminação, porque não dá para pedir a deleção do que você nunca soube que\n * foi recebido.\n *\n * Falhar alto no boot é infinitamente melhor que degradar em silêncio para o\n * caminho que ninguém escolheu.\n */\nexport function assertSttConfig(stt: ScribeSttConfig | undefined): asserts stt is ScribeSttConfig {\n if (!stt) {\n throw new Error('[plugin-scribe] a opção `stt` é obrigatória.')\n }\n\n if (!usesFayzTransport(stt)) {\n if (!stt.endpoint) {\n throw new Error(\n '[plugin-scribe] com transport: \"edge\" o stt.endpoint é obrigatório. Sem um endpoint ' +\n 'próprio de transcrição o áudio do atendimento sairia para um terceiro não declarado — ' +\n 'o plugin recusa a subir.',\n )\n }\n return\n }\n\n // Deepgram não passa pelo broker da plataforma. Cair para OpenAI em silêncio\n // mudaria custo, diarização e qualidade em pt-BR sem ninguém pedir — então é\n // erro de configuração, alto e no boot.\n if (stt.provider !== 'openai') {\n throw new Error(\n `[plugin-scribe] transport: \"fayz\" só fala com provider: \"openai\" (recebido: \"${stt.provider}\"). ` +\n 'Para Deepgram use transport: \"edge\" com uma edge function própria.',\n )\n }\n}\n","import { resolveFayzAgentConnection } from '@fayz-ai/admin'\nimport type { ScribeSttConfig, ScribeTransportKind } from '../types'\nimport { usesFayzTransport } from './config'\n\n// ---------------------------------------------------------------------------\n// Where the model lives\n// ---------------------------------------------------------------------------\n//\n// Audio→text and transcript→document both need a provider credential, so\n// neither can run in the browser. There are two legitimate homes for that\n// credential, and this file is the seam that makes the choice configuration\n// rather than a rewrite:\n//\n// 'fayz' — the platform API, the SAME broker the app assistant already uses.\n// Authenticates with the project's publishable key and holds the\n// provider key on its side: no secret, no deploy. The default.\n//\n// 'edge' — the app's own Supabase edge function, key in the project secrets.\n// For running off-platform, or for a provider the broker does not\n// offer (today: Deepgram).\n//\n// There is deliberately no third path: with no transport configured the plugin\n// refuses to boot (see the invariant in `createScribePlugin`). The silent\n// alternative would be the browser's native recognizer, which ships session\n// audio to an undeclared third party.\n\nexport interface SttClipResult {\n text: string\n words: Array<{ w: string; s: number; e: number; c?: number; sp?: number }>\n /** 0 when the provider does not report it — callers read 0 as unknown. */\n durationMs: number\n confidence: number\n /** `provider:model`, so the stored row says what produced it. */\n provider: string\n}\n\nexport interface CompletionResult {\n content: string\n model: string\n tokensIn?: number\n tokensOut?: number\n}\n\nexport interface ScribeTransport {\n kind: ScribeTransportKind\n /**\n * Transcribes ONE clip. The caller owns the queue, the retry and the state;\n * the transport only knows how to talk to a model.\n */\n transcribeClip(args: {\n audio: Blob\n mimeType: string\n locale: string\n model?: string\n diarize?: boolean\n /** Tail of the previous clip; rescues the word cut at the seam. */\n context?: string\n }): Promise<SttClipResult>\n /** One completion: no conversation, no history, nothing persisted. */\n complete(args: {\n system: string\n user: string\n model?: string\n temperature?: number\n maxTokens?: number\n }): Promise<CompletionResult>\n}\n\n// ---------------------------------------------------------------------------\n// fayz — the platform broker\n// ---------------------------------------------------------------------------\n\nconst KEY_HEADER = 'x-fayz-agent-key'\n\nfunction createFayzTransport(): ScribeTransport | null {\n const connection = resolveFayzAgentConnection()\n if (!connection) return null\n\n const base = `${connection.apiBaseUrl.replace(/\\/+$/, '')}/public/projects/${connection.projectId}/agents`\n\n async function readError(res: Response): Promise<never> {\n let detail = ''\n try {\n const body = (await res.json()) as { error?: string }\n detail = body?.error ?? ''\n } catch {\n detail = await res.text().catch(() => '')\n }\n throw new Error(`fayz ${res.status}${detail ? `: ${detail}` : ''}`)\n }\n\n return {\n kind: 'fayz',\n\n async transcribeClip({ audio, mimeType, locale, model, context }) {\n const form = new FormData()\n // The filename carries the extension, and the provider picks its demuxer\n // from it. Sending `blob` makes a perfectly valid webm come back as\n // \"unsupported format\".\n form.append('file', audio, `clip.${extOf(mimeType)}`)\n if (model) form.append('model', model)\n form.append('language', locale)\n if (context) form.append('prompt', context)\n\n const res = await fetch(`${base}/transcribe`, {\n method: 'POST',\n headers: { [KEY_HEADER]: connection.publishableKey },\n body: form,\n })\n if (!res.ok) await readError(res)\n\n const json = (await res.json()) as {\n text?: string\n words?: Array<{ w: string; s: number; e: number }>\n durationMs?: number\n provider?: string\n }\n return {\n text: json.text ?? '',\n words: json.words ?? [],\n durationMs: json.durationMs ?? 0,\n // The broker returns no aggregate confidence; 0 means \"not reported\",\n // same as durationMs.\n confidence: 0,\n provider: json.provider ?? 'fayz',\n }\n },\n\n async complete({ system, user, model, temperature, maxTokens }) {\n const res = await fetch(`${base}/complete`, {\n method: 'POST',\n headers: { [KEY_HEADER]: connection.publishableKey, 'Content-Type': 'application/json' },\n body: JSON.stringify({ system, user, model, temperature, maxTokens }),\n })\n if (!res.ok) await readError(res)\n\n const json = (await res.json()) as {\n content?: string\n model?: string\n tokensIn?: number\n tokensOut?: number\n }\n return {\n content: json.content ?? '',\n model: json.model ?? (model ?? 'auto'),\n tokensIn: json.tokensIn,\n tokensOut: json.tokensOut,\n }\n },\n }\n}\n\nfunction extOf(mimeType: string): string {\n const type = mimeType.split(';')[0]?.trim().toLowerCase() ?? ''\n if (type === 'audio/ogg') return 'ogg'\n if (type === 'audio/mp4' || type === 'audio/mpeg') return 'm4a'\n if (type === 'audio/mpga' || type === 'audio/mp3') return 'mp3'\n if (type === 'audio/wav' || type === 'audio/x-wav') return 'wav'\n return 'webm'\n}\n\n// ---------------------------------------------------------------------------\n// Resolution\n// ---------------------------------------------------------------------------\n\n/**\n * The transport actually in use, given what is configured AND what runtime\n * offers.\n *\n * `transport: 'fayz'` with no resolved connection returns null rather than\n * throwing: an app built outside the platform container has no `VITE_FAYZ_*`,\n * and the right answer there is a screen saying transcription is unavailable —\n * not the whole app failing to boot over a tab nobody opened. `'edge'` never\n * reaches here.\n */\nexport function resolveScribeTransport(stt: ScribeSttConfig): ScribeTransport | null {\n if (!usesFayzTransport(stt)) return null\n return createFayzTransport()\n}\n\nexport { usesFayzTransport } from './config'\n","// Strips what the STT invents when there is nothing to transcribe.\n//\n// Whisper learned from internet subtitles and kept their habits: over silence\n// it returns the phrases that end videos — \"Obrigado por assistir\", \"Legendas\n// pela comunidade\", a stray \"e aí\". In a clinical record that is not noise, it\n// is a sentence attributed to a patient who never said it.\n//\n// The main defence is not asking at all (`SILENCE_PEAK` in runtime/capture.ts);\n// this is the second layer, for clips with a little room tone.\n//\n// RULE: only remove when the hallucination is the WHOLE clip. Cutting \"e aí\"\n// out of real speech would mutilate the record, and a missing sentence is worse\n// than noise — noise the reader discounts, absence they cannot guess.\n\n/** Alone in a clip, these are model artifacts and not speech. */\nconst ARTIFACTS = [\n 'obrigado por assistir',\n 'obrigado por assistirem',\n 'obrigada por assistir',\n 'legendas pela comunidade amara.org',\n 'legendas pela comunidade',\n 'inscreva-se no canal',\n 'até o próximo vídeo',\n 'tchau tchau',\n 'thanks for watching',\n 'subtitles by the amara.org community',\n 'thank you',\n]\n\n/** Interjections that carry no content on their own. */\nconst FILLERS = ['e aí', 'e ai', 'aí', 'ai', 'ah', 'ahn', 'hmm', 'hm', 'uhum', 'ué', 'né', 'ó', 'ok', 'tá']\n\n/** Word-level fillers, for the repetition test only. Includes \"e\" because the\n * typical hallucination is \"e aí, e aí, e aí\". */\nconst WORD_FILLERS = new Set([...FILLERS.flatMap((f) => f.split(' ')), 'e'])\n\nfunction normalize(text: string): string {\n return text\n .toLowerCase()\n .normalize('NFD')\n .replace(/[̀-ͯ]/g, '')\n .replace(/[.,!?;:…\"'\\s]+/g, ' ')\n .trim()\n}\n\n/** `null` when the whole clip is artifact — the caller stores empty rather\n * than a sentence nobody said. */\nexport function cleanTranscriptText(text: string | null | undefined): string | null {\n if (!text) return null\n const trimmed = text.trim()\n if (!trimmed) return null\n\n const flat = normalize(trimmed)\n if (!flat) return null\n\n if (ARTIFACTS.some((a) => flat === normalize(a))) return null\n if (FILLERS.includes(flat)) return null\n\n // Pure repetition is the same artifact in disguise: when every word is\n // filler, there is no content.\n const words = flat.split(' ').filter(Boolean)\n if (words.length > 0 && words.length <= 8 && words.every((w) => WORD_FILLERS.has(w))) return null\n\n return trimmed\n}\n","import { getSupabaseClientOptional } from '@fayz-ai/core'\nimport { T, AUDIO_BUCKET } from '../data/tables'\nimport type { ScribeSttConfig } from '../types'\nimport { resolveScribeTransport, type ScribeTransport } from './transport'\nimport { cleanTranscriptText } from './transcript-clean'\n\n// ---------------------------------------------------------------------------\n// Client-side transcription drain\n// ---------------------------------------------------------------------------\n//\n// On the `fayz` transport this runs INSTEAD of the edge function, because that\n// function did two independent things: orchestrate (claim the pending segment,\n// download the audio, mark state, recompute counters, decide `ready`) and talk\n// to the STT provider. Only the second needed a credential. The browser could\n// always do the first: RLS scopes every row and bucket object to the user's\n// tenant, and this segment's audio was recorded by this very tab.\n//\n// What it buys: no secret in the app project and no edge function to deploy —\n// a real block, not a hypothetical one (plugin-agenda's `google-calendar-sync`\n// sits in the repo and was never deployed, for exactly that reason).\n//\n// What it costs: transcription only advances with a tab open. Less than it\n// sounds, because the UPLOAD was already the browser's job and\n// `stt_state='pending'` is durable — any later boot drains what is left.\n//\n// Concurrency: `stt_state='running'` is written BEFORE the download, so two\n// tabs looking at the same session do not pay to transcribe it twice.\n\n/** How many clips transcribe in parallel. */\nconst CONCURRENCY = 3\n/** Attempts before giving up on a clip and moving on. */\nconst MAX_ATTEMPTS = 4\n/** How much of the previous clip is passed as a context hint. */\nconst CONTEXT_TAIL_CHARS = 200\n\nexport interface DrainResult {\n transcribed: number\n failed: number\n remaining: number\n}\n\nfunction client(): any {\n const supabase = getSupabaseClientOptional() as any\n if (!supabase) throw new Error('Supabase not initialized')\n return supabase\n}\n\n/**\n * Transcribes everything pending in a session. Returns `null` when the\n * configured transport is not the browser's — the caller falls back to the edge\n * function.\n */\nexport async function drainSessionViaTransport(\n sessionId: string,\n stt: ScribeSttConfig,\n): Promise<DrainResult | null> {\n const transport = resolveScribeTransport(stt)\n if (!transport) return null\n\n const supabase = client()\n\n const { data: session } = await supabase\n .from(T.sessions)\n .select('id, locale, mime_type, status, ended_at')\n .eq('id', sessionId)\n .maybeSingle()\n if (!session) throw new Error('session not found')\n\n const { data: pending } = await supabase\n .from(T.segments)\n .select('session_id, seg_index, storage_path, stt_attempts')\n .eq('session_id', sessionId)\n .eq('upload_state', 'uploaded')\n .in('stt_state', ['pending', 'failed'])\n .lt('stt_attempts', MAX_ATTEMPTS)\n .order('seg_index', { ascending: true })\n\n if (!pending || pending.length === 0) {\n // Recount only after something changed, or once the session ended and the\n // last clip may have landed on an earlier pass. Doing it on every idle pass\n // was two queries per poll, forever, to reconfirm an untouched number.\n if (session.ended_at) {\n await recomputeCounters(supabase, sessionId)\n await maybeMarkReady(supabase, sessionId)\n }\n return { transcribed: 0, failed: 0, remaining: 0 }\n }\n\n let transcribed = 0\n let failed = 0\n\n for (let i = 0; i < pending.length; i += CONCURRENCY) {\n const batch = pending.slice(i, i + CONCURRENCY)\n const results = await Promise.all(\n batch.map((seg: any) =>\n transcribeOne(supabase, transport, seg, {\n locale: session.locale ?? stt.locale ?? 'pt-BR',\n mimeType: session.mime_type ?? 'audio/webm',\n model: stt.model,\n }),\n ),\n )\n transcribed += results.filter(Boolean).length\n failed += results.filter((ok) => !ok).length\n }\n\n await recomputeCounters(supabase, sessionId)\n await maybeMarkReady(supabase, sessionId)\n\n const { count } = await supabase\n .from(T.segments)\n .select('seg_index', { count: 'exact', head: true })\n .eq('session_id', sessionId)\n .eq('upload_state', 'uploaded')\n .in('stt_state', ['pending', 'failed'])\n .lt('stt_attempts', MAX_ATTEMPTS)\n\n return { transcribed, failed, remaining: count ?? 0 }\n}\n\nasync function transcribeOne(\n supabase: any,\n transport: ScribeTransport,\n seg: { session_id: string; seg_index: number; storage_path: string | null; stt_attempts: number | null },\n opts: { locale: string; mimeType: string; model?: string },\n): Promise<boolean> {\n if (!seg.storage_path) return false\n\n const where = (q: any) => q.eq('session_id', seg.session_id).eq('seg_index', seg.seg_index)\n\n try {\n await where(\n supabase.from(T.segments).update({\n stt_state: 'running',\n stt_attempts: (seg.stt_attempts ?? 0) + 1,\n updated_at: new Date().toISOString(),\n }),\n )\n\n const { data: file, error: dlErr } = await supabase.storage\n .from(AUDIO_BUCKET)\n .download(seg.storage_path)\n if (dlErr || !file) throw new Error(`download failed: ${dlErr?.message ?? 'no body'}`)\n\n const context = await previousTail(supabase, seg.session_id, seg.seg_index)\n const result = await transport.transcribeClip({\n audio: file as Blob,\n mimeType: opts.mimeType,\n locale: opts.locale,\n model: opts.model,\n context,\n })\n\n await where(\n supabase.from(T.segments).update({\n stt_state: 'done',\n stt_provider: result.provider,\n stt_error: null,\n // A clip that came back as pure silence artifact stores empty, not as\n // something the patient said. See lib/transcript-clean.ts.\n text: cleanTranscriptText(result.text),\n words: result.words,\n confidence: result.confidence,\n // AUTHORITATIVE duration: measured in the audio, never counted in JS.\n // A provider that does not report it returns 0, and 0 becomes NULL\n // rather than a zero-minute session.\n duration_ms: result.durationMs > 0 ? result.durationMs : null,\n updated_at: new Date().toISOString(),\n }),\n )\n return true\n } catch (err) {\n await where(\n supabase.from(T.segments).update({\n stt_state: 'failed',\n stt_error: String((err as Error)?.message ?? err).slice(0, 500),\n updated_at: new Date().toISOString(),\n }),\n )\n return false\n }\n}\n\n/** Tail of the previous clip — the context hint that rescues the word at the seam. */\nasync function previousTail(supabase: any, sessionId: string, segIndex: number): Promise<string | undefined> {\n if (segIndex === 0) return undefined\n const { data } = await supabase\n .from(T.segments)\n .select('text')\n .eq('session_id', sessionId)\n .eq('seg_index', segIndex - 1)\n .maybeSingle()\n const text = data?.text as string | undefined\n return text ? text.slice(-CONTEXT_TAIL_CHARS) : undefined\n}\n\n/**\n * Recomputes counters from the segments — same reason as the pump's RPC:\n * derive, never increment, so an idempotent replay converges.\n */\nasync function recomputeCounters(supabase: any, sessionId: string): Promise<void> {\n const { data: segs } = await supabase\n .from(T.segments)\n .select('stt_state, duration_ms, text, gap')\n .eq('session_id', sessionId)\n if (!segs) return\n\n await supabase\n .from(T.sessions)\n .update({\n transcribed_segment_count: segs.filter((s: any) => s.stt_state === 'done').length,\n audio_duration_ms: segs\n .filter((s: any) => !s.gap)\n .reduce((acc: number, s: any) => acc + (s.duration_ms ?? 0), 0),\n transcript_chars: segs.reduce((acc: number, s: any) => acc + (s.text?.length ?? 0), 0),\n updated_at: new Date().toISOString(),\n })\n .eq('id', sessionId)\n}\n\n/**\n * `ready` covers clips that exhausted their attempts: 78 of 80 transcribed is\n * usable, and pinning the session in `transcribing` forever over the other two\n * would be the worse of both worlds.\n */\nasync function maybeMarkReady(supabase: any, sessionId: string): Promise<void> {\n const { data: session } = await supabase\n .from(T.sessions)\n .select('status, ended_at')\n .eq('id', sessionId)\n .maybeSingle()\n if (!session?.ended_at) return\n if (!['uploading', 'transcribing', 'recording', 'paused', 'interrupted'].includes(session.status)) return\n\n const { data: segs } = await supabase\n .from(T.segments)\n .select('stt_state, stt_attempts, upload_state')\n .eq('session_id', sessionId)\n if (!segs || segs.length === 0) return\n\n const outstanding = segs.filter(\n (s: any) =>\n s.upload_state === 'uploaded' &&\n ['pending', 'running', 'failed'].includes(s.stt_state) &&\n (s.stt_attempts ?? 0) < MAX_ATTEMPTS,\n )\n await supabase\n .from(T.sessions)\n .update({\n status: outstanding.length === 0 ? 'ready' : 'transcribing',\n updated_at: new Date().toISOString(),\n })\n .eq('id', sessionId)\n}\n","import { create } from 'zustand'\nimport { fetchSegments } from '../data/supabase'\nimport { drainSessionViaTransport } from './drain'\nimport type { ScribeSegment, ScribeSttConfig } from '../types'\n\n// The running session's transcript, in one place.\n//\n// Two consumers need it and neither can own it: the panel (alive only while its\n// tab is open) and the agent context (needed with the tab closed, or asking\n// \"what did he say so far?\" answers about nothing).\n//\n// THE DRAIN RUNS HERE TOO, which is the difference between text appearing and\n// nothing appearing: on the `fayz` transport the browser is the transcriber\n// (lib/drain.ts), and that caller only existed inside the session PAGE — so\n// during a live encounter clips uploaded and sat there, pending, forever.\n//\n// Cadence is per CLIP, not per word: 30s chunks transcribed after upload.\n// Word-by-word needs a streaming STT transport, not a smaller interval.\n\n// Adaptive: a clip closes every 30s, so most of the time there is nothing to\n// fetch. A fixed 6s poll spent five passes discovering that nothing changed.\nconst POLL_BUSY_MS = 3000\nconst POLL_IDLE_MS = 15000\n\ninterface LiveTranscriptState {\n sessionId: string | null\n segments: ScribeSegment[]\n}\n\nexport const useLiveTranscript = create<LiveTranscriptState>(() => ({\n sessionId: null,\n segments: [],\n}))\n\nlet timer: ReturnType<typeof setTimeout> | null = null\nlet polling: string | null = null\nlet sttConfig: ScribeSttConfig | null = null\nlet draining = false\n/** Last result's signature — avoids rewriting the store with no change. */\nlet lastSignature = ''\n\n/** @returns quanto esperar até a próxima volta. */\nasync function load(sessionId: string): Promise<number> {\n let pendingWork = false\n\n // Drain before reading. One pass at a time: two concurrent rounds would have\n // two clients transcribing (and paying for) the same clip.\n if (sttConfig && !draining) {\n draining = true\n try {\n const result = await drainSessionViaTransport(sessionId, sttConfig)\n // Pending work means come back soon; nothing pending means the next clip\n // is still being recorded.\n pendingWork = !!result && (result.transcribed > 0 || result.remaining > 0)\n } catch {\n // A próxima volta tenta de novo; `stt_state` é durável.\n } finally {\n draining = false\n }\n }\n\n try {\n const segments = await fetchSegments(sessionId)\n // A late response from a previous session must not repaint this one.\n if (polling === sessionId) {\n const signature = segments.map((s) => `${s.segIndex}:${s.text ? s.text.length : 0}`).join('|')\n // No change, no setState: each write wakes the panel, the agent context\n // and the composer to repaint identical text.\n if (signature !== lastSignature) {\n lastSignature = signature\n useLiveTranscript.setState({ sessionId, segments })\n }\n }\n } catch {\n // A failed read never clears the screen — the previous text is still the\n // best thing we have.\n }\n\n return pendingWork ? POLL_BUSY_MS : POLL_IDLE_MS\n}\n\n/** Self-scheduled rather than `setInterval`: the delay depends on what the\n * previous pass found. */\nfunction schedule(sessionId: string, delay: number): void {\n timer = setTimeout(() => {\n if (polling !== sessionId) return\n void load(sessionId).then((next) => {\n if (polling === sessionId) schedule(sessionId, next)\n })\n }, delay)\n}\n\n/** Start (or switch) watching. `null` stops. */\nexport function watchTranscript(sessionId: string | null, stt?: ScribeSttConfig): void {\n if (stt) sttConfig = stt\n if (polling === sessionId) return\n polling = sessionId\n if (timer) {\n clearTimeout(timer)\n timer = null\n }\n lastSignature = ''\n if (!sessionId) {\n useLiveTranscript.setState({ sessionId: null, segments: [] })\n return\n }\n useLiveTranscript.setState({ sessionId, segments: [] })\n void load(sessionId).then((next) => {\n if (polling === sessionId) schedule(sessionId, next)\n })\n}\n\n/** Flat text for the agent context. Holes are declared: text that reads as\n * continuous when it is not is worth less than an admitted gap. */\nexport function transcriptText(): string {\n return useLiveTranscript\n .getState()\n .segments.map((s) => (s.gap ? '[trecho perdido]' : (s.text ?? '').trim()))\n .filter(Boolean)\n .join(' ')\n}\n","import * as React from 'react'\nimport {\n openAssistant,\n useAgentSurface,\n usePublishAssistantActivity,\n useRegisterAssistantLauncher,\n type AssistantActivity,\n type AssistantActivityAction,\n} from '@fayz-ai/admin'\nimport { transcriptText, watchTranscript } from '../lib/live-transcript'\nimport { useScribeStore, formatElapsed } from '../store'\nimport { endSession, getCaptureStream, getSnapshot, isCaptureSupported, pauseSession, resumeSession } from '../runtime'\nimport { requestScribeStart } from './ScribeShellMount'\nimport type { ScribeLabels, ScribeSttConfig } from '../types'\n\n// The recording, seen from inside the assistant.\n//\n// This is the only thing scribe needs to know about the shell: it publishes\n// what is happening (label, elapsed, state) plus the actions, and the shell\n// draws the FAB pill and the panel bar. A long import or an agent task\n// publishes the same contract and gets the same face.\n//\n// `elapsedMs` is rounded to the second on purpose: the runtime recomputes it\n// every frame, and publishing every frame would wake every subscriber 60x a\n// second to paint a number that changes once.\n\n/** Repaint driver — the elapsed value lives in the runtime, not in React. */\nfunction useSecondTicker(active: boolean): void {\n const [, force] = React.useReducer((n: number) => n + 1, 0)\n React.useEffect(() => {\n if (!active) return\n // Sampled at 250ms to paint a 1s clock: a 1000ms interval drifts against\n // the second boundary, and a sample that lands just the wrong side of it\n // reads the same second twice and then skips one. Publishing still happens\n // once per second — the activity key only changes when the rounded value\n // does.\n const timer = setInterval(() => {\n if (document.visibilityState === 'visible') force()\n }, 250)\n const onVisible = () => force()\n document.addEventListener('visibilitychange', onVisible)\n return () => {\n clearInterval(timer)\n document.removeEventListener('visibilitychange', onVisible)\n }\n }, [active])\n}\n\nexport interface ScribeAssistantBridgeProps {\n labels: ScribeLabels\n stt: ScribeSttConfig\n onOpenSession?: (sessionId: string) => void\n}\n\nexport function ScribeAssistantBridge({ labels, stt, onOpenSession }: ScribeAssistantBridgeProps) {\n const state = useScribeStore((s) => s.state)\n const sessionId = useScribeStore((s) => s.sessionId)\n const subjectName = useScribeStore((s) => s.subjectName)\n\n const live = state === 'recording' || state === 'paused' || state === 'interrupted'\n useSecondTicker(state === 'recording')\n\n // Watching runs in the mount that never unmounts, not in the panel: tied to\n // an open tab, \"what did he say so far?\" would answer about nothing.\n React.useEffect(() => {\n // The STT config rides along: on the `fayz` transport this browser is the\n // transcriber, so watching and transcribing are the same tick.\n watchTranscript(live ? sessionId : null, stt)\n }, [live, sessionId, stt])\n\n // `getSnapshot()` and not the store's `elapsedMs`: that field only changes\n // when the runtime emits, so a per-second read saw the same number twice and\n // the clock jumped 2s. Outside a selector — a continuously moving value would\n // wake every consumer of the store.\n const elapsedMs = live ? Math.floor(getSnapshot().elapsedMs / 1000) * 1000 : 0\n\n // A just-ended session stays on the bar until acted on: the document is the\n // point of recording, and burying it is how it becomes a file nobody opens.\n const [finished, setFinished] = React.useState<{ id: string; label: string } | null>(null)\n\n const activity: AssistantActivity | null = React.useMemo(() => {\n if (live) {\n const actions: AssistantActivityAction[] = [\n state === 'paused'\n ? { intent: 'resume', label: labels.resume, run: () => void resumeSession() }\n : { intent: 'pause', label: labels.pause, run: () => void pauseSession() },\n {\n intent: 'stop',\n label: labels.finish,\n primary: true,\n run: async () => {\n const id = sessionId\n await endSession()\n if (id) setFinished({ id, label: subjectName ? `${labels.sessionSingular} · ${subjectName}` : labels.sessionSingular })\n },\n },\n ]\n return {\n id: 'scribe.session',\n kind: 'recording',\n label: subjectName ? `${labels.sessionSingular} · ${subjectName}` : labels.sessionSingular,\n state: state === 'paused' ? 'paused' : 'running',\n elapsedMs,\n actions,\n // Lent, not duplicated: the composer meter draws THIS audio.\n stream: getCaptureStream,\n }\n }\n\n if (finished) {\n return {\n id: 'scribe.session',\n kind: 'recording',\n label: `${finished.label} — ${labels.generate.toLowerCase()}?`,\n state: 'done',\n actions: [\n {\n intent: 'generate',\n label: labels.generate,\n primary: true,\n run: () => {\n onOpenSession?.(finished.id)\n setFinished(null)\n },\n },\n { intent: 'discard', label: 'Depois', run: () => setFinished(null) },\n ],\n }\n }\n\n return null\n }, [live, state, elapsedMs, subjectName, sessionId, finished, labels, onOpenSession])\n\n usePublishAssistantActivity(activity)\n\n // The recording is CONTEXT, not just an indicator: without this, asking whose\n // session it is — in the panel showing its own running clock — answers \"I\n // don't know\". Stable identity + ref reads, per the useAgentSurface contract:\n // describe() runs at send time, so the current second arrives without\n // re-registering on every tick.\n const liveRef = React.useRef({ live, state, subjectName, sessionId })\n liveRef.current = { live, state, subjectName, sessionId }\n const surface = React.useMemo(\n () => ({\n id: 'scribe.session',\n pluginId: 'scribe',\n describe: () => {\n const snap = liveRef.current\n if (!snap.live) return {}\n const text = transcriptText()\n return {\n title: `${labels.sessionSingular} em gravação`,\n state: {\n sessionId: snap.sessionId,\n subject: snap.subjectName ?? null,\n status: snap.state,\n elapsed: formatElapsed(getSnapshot().elapsedMs),\n // Tail-clipped: during an encounter the questions are about what\n // was just said.\n transcript: text ? text.slice(-6000) : null,\n },\n }\n },\n get instructions() {\n const snap = liveRef.current\n if (!snap.live) return undefined\n const hasText = transcriptText().length > 0\n return (\n `Há um ${labels.sessionSingular.toLowerCase()} sendo gravado agora` +\n (snap.subjectName ? ` com ${snap.subjectName}` : '') +\n '. ' +\n (hasText\n ? `A transcrição parcial está em state.transcript — use SÓ o que está lá e diga que o ` +\n `atendimento ainda está em curso quando responder sobre ele.`\n : `Nenhum trecho foi transcrito ainda: os primeiros saem cerca de 30s depois do início. ` +\n `Não invente o conteúdo.`) +\n ` O documento final fica pronto quando a gravação for finalizada.`\n )\n },\n }),\n [labels.sessionSingular],\n )\n useAgentSurface(surface)\n\n // Starting from here binds no subject: the session attaches to a person\n // later, which is what `standalone` exists for.\n const supported = isCaptureSupported()\n useRegisterAssistantLauncher(\n live\n ? null\n : {\n id: 'scribe.start',\n kind: 'recording',\n label: labels.start,\n disabled: !supported,\n disabledReason: 'Este navegador não suporta gravação de áudio',\n run: () => {\n openAssistant()\n requestScribeStart({})\n },\n },\n )\n\n return null\n}\n","import * as React from 'react'\nimport { createPortal } from 'react-dom'\nimport { useAuth, useRightRailStore } from '@fayz-ai/admin'\nimport { toast } from '@fayz-ai/ui/primitives'\nimport { ScribeRecordingPill } from './ScribeRecordingPill'\nimport { ScribeRecoveryBanner } from './ScribeRecoveryBanner'\nimport { ScribeConsentDialog } from './ScribeConsentDialog'\nimport { ScribeAssistantBridge } from './ScribeAssistantBridge'\nimport { beginSession } from '../runtime'\nimport { wireScribeStore } from '../store'\nimport type { ConsentMode, ScribeLabels, ScribeRetentionConfig, ScribeSttConfig } from '../types'\n\n// One topbar widget carrying everything that must be global. `shell.topbar.end`\n// is the only zone verifiably rendered and always mounted — spreading pill,\n// banner and dialog across zones would depend on zones nothing renders.\n//\n// `scribe:start` is the same decoupling the agenda uses with\n// `agenda:open-booking`: any screen fires it, this mount listens. The agenda\n// gets a record button without gaining a dependency on scribe.\n\nexport interface ScribeStartEventDetail {\n subjectId?: string\n subjectName?: string\n appointmentId?: string\n templateKeys?: string[]\n}\n\nexport const SCRIBE_START_EVENT = 'scribe:start'\n\nexport interface ScribeShellMountProps {\n labels: ScribeLabels\n retention: ScribeRetentionConfig\n stt: ScribeSttConfig\n locale: string\n onOpenSession?: (sessionId: string) => void\n}\n\nexport function ScribeShellMount({ labels, retention, stt, locale, onOpenSession }: ScribeShellMountProps) {\n const { user } = useAuth()\n // Full-bleed banner vs a fixed rail column: without subtracting its width the\n // banner lands on top of the recording bar — two sets of controls stacked.\n const railOffset = useRightRailStore((s) => (s.open ? s.width : 0))\n const contentTop = useContentTop()\n const bannerRef = React.useRef<HTMLDivElement>(null)\n useReserveSpace(bannerRef)\n const [pending, setPending] = React.useState<ScribeStartEventDetail | null>(null)\n\n React.useEffect(() => {\n wireScribeStore()\n }, [])\n\n React.useEffect(() => {\n const onStart = (ev: Event) => {\n const detail = (ev as CustomEvent<ScribeStartEventDetail>).detail ?? {}\n // With consent required the dialog comes BEFORE the first byte. The\n // decision belongs to the vertical, not to the screen.\n if (retention.requireConsent) setPending(detail)\n else void start(detail, retention.defaultConsentMode)\n }\n window.addEventListener(SCRIBE_START_EVENT, onStart)\n return () => window.removeEventListener(SCRIBE_START_EVENT, onStart)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [retention.requireConsent, retention.defaultConsentMode, user?.id, locale])\n\n const start = React.useCallback(\n async (detail: ScribeStartEventDetail, consentMode: ConsentMode) => {\n if (!user?.id) {\n toast.error('Faça login para gravar um atendimento')\n return\n }\n const result = await beginSession({\n userId: user.id,\n subjectId: detail.subjectId,\n subjectName: detail.subjectName,\n appointmentId: detail.appointmentId,\n templateKeys: detail.templateKeys,\n locale,\n consentMode,\n })\n if (!result.ok) {\n // The reason is always actionable (permission denied, no space, no\n // mic); a generic \"recording failed\" hides that a click would fix it.\n toast.error(`Não foi possível iniciar: ${result.reason}`)\n return\n }\n toast.success(`${labels.sessionSingular} iniciado`)\n },\n [user?.id, locale, labels.sessionSingular],\n )\n\n return (\n <>\n {/* Recording state lives in the assistant now. The topbar pill survives\n only where the FAB may not be (small screens): visible proof of an\n ongoing recording is a legal obligation, not decoration. */}\n <ScribeAssistantBridge labels={labels} stt={stt} onOpenSession={onOpenSession} />\n <ScribeRecordingPill labels={labels} onOpenSession={onOpenSession} mobileOnly />\n\n <ScribeConsentDialog\n open={pending !== null}\n labels={labels}\n subjectName={pending?.subjectName}\n defaultConsentMode={retention.defaultConsentMode}\n onCancel={() => setPending(null)}\n onConfirm={(mode) => {\n const detail = pending\n setPending(null)\n if (detail) void start(detail, mode)\n }}\n />\n\n {/* The banner belongs to the top of the PAGE, but the only always-alive\n mount is this widget, so it portals. The offset is MEASURED, not\n guessed: `top-14` fit one shell layout and put the banner behind the\n nav in the other — a defect that looks like a rendering bug and\n disappears when you switch layouts. */}\n {typeof document !== 'undefined' &&\n createPortal(\n <div\n ref={bannerRef}\n className=\"pointer-events-auto fixed left-0 z-40\"\n style={{ top: contentTop, right: railOffset }}\n >\n <ScribeRecoveryBanner labels={labels} onOpenSession={onOpenSession} />\n </div>,\n document.body,\n )}\n </>\n )\n}\n\n/**\n * Reserves the banner's height on `<main>`.\n *\n * A `fixed` notice takes no space, so it LANDS on the first thing in the page.\n * Reserving turns it from something that covers into something that pushes.\n * Living in the page flow instead is not an option: the page changes per route\n * and this widget is the only mount that survives navigation.\n */\nfunction useReserveSpace(ref: React.RefObject<HTMLDivElement>): void {\n React.useEffect(() => {\n if (typeof document === 'undefined') return\n const main = document.querySelector('main') as HTMLElement | null\n const node = ref.current\n if (!main || !node) return\n\n const previous = main.style.paddingTop\n const apply = () => {\n const height = node.getBoundingClientRect().height\n main.style.paddingTop = height > 0 ? `${Math.ceil(height)}px` : previous\n }\n apply()\n\n const observer = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(apply) : null\n observer?.observe(node)\n return () => {\n observer?.disconnect()\n main.style.paddingTop = previous\n }\n }, [ref])\n}\n\n/** Where the content area starts, in px — read from the shell's `<main>`,\n * which exists in all three layouts and already sits below the chrome. */\nfunction useContentTop(): number {\n const [top, setTop] = React.useState(56)\n\n React.useEffect(() => {\n if (typeof document === 'undefined') return\n const measure = () => {\n const main = document.querySelector('main')\n if (main) setTop(Math.round(main.getBoundingClientRect().top))\n }\n measure()\n window.addEventListener('resize', measure)\n // The header grows and shrinks without a window resize (a module sub-nav\n // appearing, for instance).\n const observer = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(measure) : null\n const main = document.querySelector('main')\n if (observer && main) observer.observe(main)\n return () => {\n window.removeEventListener('resize', measure)\n observer?.disconnect()\n }\n }, [])\n\n return top\n}\n\n/** Lets any screen start a session without importing the plugin. */\nexport function requestScribeStart(detail: ScribeStartEventDetail): void {\n window.dispatchEvent(new CustomEvent(SCRIBE_START_EVENT, { detail }))\n}\n","// ---------------------------------------------------------------------------\n// Construção do prompt. Puro, sem dependência — a edge function importa daqui\n// para que o prompt tenha UMA definição só. Duas cópias divergindo é como um\n// documento gerado no servidor deixa de bater com o que a tela prometeu.\n// ---------------------------------------------------------------------------\n\nimport type { NarrativeSchema, NarrativeSectionDef } from '../types'\n\n/**\n * Versão do prompt, gravada em `plg_scribe_generations.prompt_version`. Subir\n * este número a cada mudança de forma é o que torna possível responder \"por que\n * os documentos de março saíram diferentes dos de abril\".\n */\nexport const PROMPT_VERSION = 'scribe/narrative@1'\n\nexport interface TranscriptLine {\n startOffsetMs: number\n text: string\n gap: boolean\n speaker?: number\n}\n\n/** `mm:ss` — a marcação que o profissional usa para achar o trecho no áudio. */\nexport function formatOffset(ms: number): string {\n const total = Math.floor(ms / 1000)\n const mm = String(Math.floor(total / 60)).padStart(2, '0')\n const ss = String(total % 60).padStart(2, '0')\n return `${mm}:${ss}`\n}\n\n/**\n * Renderiza a transcrição para o modelo. Buracos entram como linha EXPLÍCITA:\n * o modelo precisa saber que houve descontinuidade, senão ele costura os dois\n * lados numa narrativa contínua e inventa a ponte.\n */\nexport function renderTranscript(lines: TranscriptLine[]): string {\n return lines\n .filter((l) => l.gap || l.text?.trim())\n .map((l) => {\n const at = formatOffset(l.startOffsetMs)\n if (l.gap) return `[${at}] (((TRECHO AUSENTE — a gravação foi interrompida aqui)))`\n const who = l.speaker !== undefined ? `Falante ${l.speaker + 1}: ` : ''\n return `[${at}] ${who}${l.text.trim()}`\n })\n .join('\\n')\n}\n\nfunction shapeInstruction(section: NarrativeSectionDef): string {\n switch (section.shape) {\n case 'bullets':\n return 'Formato: lista com hífen. Itens curtos, um fato por item.'\n case 'keyvalue':\n // Lista com rótulo em negrito, e não tabela, porque o renderer do SDK não\n // tem tabela e o print A4 sai quebrado.\n return 'Formato: lista com hífen no padrão `- **Rótulo:** valor`. Um par por linha.'\n default:\n return 'Formato: prosa corrida, sem listas.'\n }\n}\n\nexport function buildSystemPrompt(schema: NarrativeSchema): string {\n const parts: string[] = []\n\n parts.push(\n 'Você redige documentos a partir da transcrição de um atendimento gravado.',\n 'Escreva SOMENTE o que a transcrição sustenta. Você não é um assistente conversacional: sua saída é o documento, nada mais.',\n )\n\n if (schema.style) parts.push(`Estilo: ${schema.style}`)\n if (schema.outputLocale) parts.push(`Escreva o documento em ${schema.outputLocale}, mesmo que a transcrição esteja em outro idioma.`)\n\n parts.push(\n '',\n 'REGRAS INEGOCIÁVEIS:',\n '- Nunca invente fato, número, data, nome ou medida que não esteja na transcrição.',\n '- Quando a transcrição não cobre algo que a seção pede, escreva \"não informado\". Não deduza a partir do contexto.',\n '- Onde a transcrição indicar `(((TRECHO AUSENTE)))`, NÃO costure os dois lados como se fossem contínuos. Se aquilo afeta o que a seção diz, registre a lacuna.',\n '- Não dirija a palavra ao leitor, não comente o próprio trabalho, não peça confirmação.',\n )\n\n if (schema.neverInfer?.length) {\n parts.push(\n `- Estes itens NUNCA podem ser inferidos, apenas transcritos quando ditos explicitamente: ${schema.neverInfer.join(', ')}.`,\n )\n }\n\n parts.push(\n '',\n 'FORMATO DA SAÍDA (markdown restrito):',\n '- Cada seção começa com `## ` seguido exatamente do título fornecido.',\n '- Use no máximo `###` para subtítulo. Nunca `####` ou mais fundo.',\n '- PROIBIDO: tabela, link, imagem, bloco de código cercado por crases, HTML.',\n '- Permitido: parágrafo, lista com hífen, lista numerada, **negrito**, *itálico*.',\n '- Não escreva preâmbulo nem fecho. A primeira linha da resposta é o primeiro `## `.',\n )\n\n return parts.join('\\n')\n}\n\nexport function buildUserPrompt(args: {\n schema: NarrativeSchema\n transcript: string\n context?: Record<string, string | undefined>\n}): string {\n const { schema, transcript, context } = args\n const parts: string[] = []\n\n const ctx = Object.entries(context ?? {}).filter(([, v]) => v)\n if (ctx.length > 0) {\n // Contexto é DADO, resolvido do banco. Vai como referência para o modelo\n // situar o texto — nunca como algo a ser reproduzido no corpo, senão ele\n // repete o cabeçalho que a tela já renderiza.\n parts.push('DADOS DO ATENDIMENTO (referência; não repita como seção):')\n for (const [k, v] of ctx) parts.push(`- ${k}: ${v}`)\n parts.push('')\n }\n\n parts.push('TRANSCRIÇÃO:', '\"\"\"', transcript, '\"\"\"', '')\n parts.push('SEÇÕES A ESCREVER, nesta ordem:')\n\n schema.sections.forEach((section, i) => {\n parts.push('')\n parts.push(`${i + 1}. ## ${section.heading}`)\n parts.push(` O que entra: ${section.guidance}`)\n parts.push(` ${shapeInstruction(section)}`)\n if (section.maxWords) parts.push(` Limite: cerca de ${section.maxWords} palavras.`)\n if (section.required) parts.push(' Obrigatória: escreva-a mesmo que só para registrar \"não informado\".')\n else if (section.omitWhenEmpty) parts.push(' Se a transcrição não disser nada sobre isto, OMITA a seção inteira, título incluso.')\n })\n\n return parts.join('\\n')\n}\n\n/**\n * Quebra o markdown de volta em seções por `id`, casando os títulos `## `. Serve\n * para o commit gravar `data.sections` e para o assistente reescrever UMA seção\n * sem tocar no resto.\n *\n * Casa por título normalizado porque é o único âncora que o modelo devolve —\n * daí o `id` da seção nunca ser traduzido: ele é a chave, o título é a etiqueta.\n */\nexport function parseSections(markdown: string, schema: NarrativeSchema): Record<string, string> {\n const out: Record<string, string> = {}\n if (!markdown) return out\n\n const norm = (s: string) => s.trim().toLowerCase().replace(/\\s+/g, ' ').replace(/[:.]+$/, '')\n const byHeading = new Map(schema.sections.map((s) => [norm(s.heading), s.id]))\n\n const lines = markdown.split('\\n')\n let currentId: string | null = null\n let buffer: string[] = []\n\n const flush = () => {\n if (currentId) out[currentId] = buffer.join('\\n').trim()\n buffer = []\n }\n\n for (const line of lines) {\n const match = /^##\\s+(.+?)\\s*$/.exec(line)\n if (match && !line.startsWith('###')) {\n flush()\n currentId = byHeading.get(norm(match[1])) ?? null\n continue\n }\n if (currentId) buffer.push(line)\n }\n flush()\n return out\n}\n\n/** Título do documento: primeira seção com conteúdo, encurtada. Cai no nome do template. */\nexport function deriveTitle(markdown: string, fallback: string, maxLen = 80): string {\n const firstBody = markdown\n .split('\\n')\n .find((l) => l.trim() && !l.startsWith('#') && !l.startsWith('-') && !l.startsWith('>'))\n if (!firstBody) return fallback\n const clean = firstBody.replace(/[*_`]/g, '').trim()\n return clean.length > maxLen ? `${clean.slice(0, maxLen - 1)}…` : clean\n}\n","import * as React from 'react'\nimport { AlertTriangle, Loader2 } from 'lucide-react'\nimport { Badge } from '@fayz-ai/ui/primitives'\nimport { formatOffset } from '../lib/prompt'\nimport type { ScribeSegment } from '../types'\n\n// The transcript, timestamped.\n//\n// One requirement here is not cosmetic: A GAP MUST SHOW. A `gap` segment\n// renders as a visible line with the hole's duration. Without it the text reads\n// as continuous when it is not, and in a clinical record a hidden hole is worse\n// than a declared one — a declared hole the reader can weigh.\n\nexport interface TranscriptPanelProps {\n segments: ScribeSegment[]\n /** A segment is still uploading or transcribing. */\n pending: boolean\n emptyLabel?: string\n}\n\nexport function TranscriptPanel({ segments, pending, emptyLabel }: TranscriptPanelProps) {\n const visible = segments.filter((s) => s.gap || s.text?.trim())\n\n // Spinner and not skeleton while recording: a skeleton promises the shape of\n // something already on its way, and here the text depends on someone still\n // talking. Skeletons belong to the detail load, where the data exists.\n if (visible.length === 0) {\n return (\n <div className=\"flex flex-col items-center justify-center gap-2 py-12 text-center text-sm text-muted-foreground\">\n {pending ? (\n <>\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n <p>Transcrevendo…</p>\n <p className=\"text-xs\">O texto aparece conforme os trechos são processados.</p>\n </>\n ) : (\n <p>{emptyLabel ?? 'Nenhuma transcrição ainda.'}</p>\n )}\n </div>\n )\n }\n\n return (\n <div className=\"space-y-3\">\n {visible.map((seg) => {\n if (seg.gap) {\n return (\n <div\n key={seg.segIndex}\n className=\"flex items-center gap-2 rounded-md border border-dashed border-amber-500/40 bg-amber-500/5 px-3 py-2 text-xs text-amber-700 dark:text-amber-400\"\n >\n <AlertTriangle className=\"h-3.5 w-3.5 shrink-0\" />\n <span className=\"font-mono\">{formatOffset(seg.startOffsetMs)}</span>\n <span>\n Gravação interrompida\n {seg.durationMs ? ` por ${Math.round(seg.durationMs / 1000)}s` : ''}\n </span>\n </div>\n )\n }\n\n const speaker = seg.words?.[0]?.sp\n return (\n <div key={seg.segIndex} className=\"flex gap-3\">\n <span className=\"w-12 shrink-0 pt-0.5 font-mono text-xs text-muted-foreground\">\n {formatOffset(seg.startOffsetMs)}\n </span>\n <div className=\"min-w-0 flex-1\">\n {speaker !== undefined && (\n <Badge variant=\"secondary\" className=\"mb-1 text-[10px]\">\n Falante {speaker + 1}\n </Badge>\n )}\n <p className=\"text-sm leading-relaxed\">\n {seg.text}\n {seg.partial && (\n <span className=\"ml-1 text-xs text-muted-foreground\">(trecho parcial)</span>\n )}\n </p>\n </div>\n </div>\n )\n })}\n\n {pending && (\n <div className=\"flex items-center gap-2 pl-15 text-xs text-muted-foreground\">\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n Transcrevendo o restante…\n </div>\n )}\n </div>\n )\n}\n","import * as React from 'react'\nimport { Check, Loader2, RefreshCw, Trash2 } from 'lucide-react'\nimport { Badge, Button, MarkdownEditor, toast } from '@fayz-ai/ui/primitives'\nimport { parseSections } from '../lib/prompt'\nimport { commitGeneration, discardGeneration, saveDraft } from '../data/supabase'\nimport type { NarrativeSchema, ScribeGeneration, ScribeLabels, ScribeSession } from '../types'\n\n// ---------------------------------------------------------------------------\n// O rascunho: gerar → revisar → salvar na ficha.\n//\n// O texto do modelo (`markdown`) nunca é sobrescrito. O que o usuário edita vai\n// para `markdown_edited`, e a diferença entre os dois é o produto secundário\n// mais útil de toda a feature: é o selo de \"revisado pelo profissional\", é o\n// diff que diz se o prompt está bom, e é a proveniência que um registro\n// médico-legal precisa ter.\n//\n// O autosave é debounced em 2 s. Salvar a cada tecla castigaria o banco; salvar\n// só no commit perderia a edição de quem fechou a aba no meio da revisão — que\n// é justamente o modo de falha que esta feature inteira existe para não ter.\n// ---------------------------------------------------------------------------\n\nconst AUTOSAVE_DEBOUNCE_MS = 2_000\n\nexport interface DraftPanelProps {\n generation: ScribeGeneration\n session: ScribeSession\n schema: NarrativeSchema\n templateName: string\n /** Preset category — travels with the template created on first use. */\n templateCategory?: string\n labels: ScribeLabels\n onRegenerate: () => void\n onCommitted: (documentId: string) => void\n onDiscarded: () => void\n}\n\nexport function DraftPanel({\n generation,\n session,\n schema,\n templateName,\n templateCategory,\n labels,\n onRegenerate,\n onCommitted,\n onDiscarded,\n}: DraftPanelProps) {\n const original = generation.markdown ?? ''\n const [value, setValue] = React.useState(generation.markdownEdited ?? original)\n const [saving, setSaving] = React.useState(false)\n const [committing, setCommitting] = React.useState(false)\n const timer = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n\n // Uma geração nova (regenerar, trocar de aba) reseta o editor. Sem isto o\n // texto da geração anterior ficaria na tela sobre o conteúdo novo.\n React.useEffect(() => {\n setValue(generation.markdownEdited ?? generation.markdown ?? '')\n }, [generation.id, generation.markdown, generation.markdownEdited])\n\n const edited = value.trim() !== original.trim()\n\n const handleChange = React.useCallback(\n (next: string) => {\n setValue(next)\n if (timer.current) clearTimeout(timer.current)\n timer.current = setTimeout(() => {\n setSaving(true)\n saveDraft(generation.id, next)\n .catch(() => toast.error('Não foi possível salvar o rascunho'))\n .finally(() => setSaving(false))\n }, AUTOSAVE_DEBOUNCE_MS)\n },\n [generation.id],\n )\n\n // Flush no unmount: navegar para outra aba não pode engolir os últimos 2 s de\n // digitação.\n React.useEffect(() => {\n return () => {\n if (timer.current) {\n clearTimeout(timer.current)\n void saveDraft(generation.id, value).catch(() => undefined)\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [generation.id, value])\n\n const handleCommit = React.useCallback(async () => {\n setCommitting(true)\n try {\n if (timer.current) clearTimeout(timer.current)\n await saveDraft(generation.id, value)\n const documentId = await commitGeneration({\n generation,\n session,\n markdown: value,\n // The document's name is its TYPE. `deriveTitle` took the first line\n // of the text, so the record listed \"Paciente refere dor importante\n // em cotovelo…\" where it should list \"Anamnese\" — and two of them for\n // the same patient were indistinguishable.\n title: templateName,\n sections: parseSections(value, schema),\n edited,\n templateName,\n templateCategory,\n })\n toast.success('Documento salvo na ficha')\n onCommitted(documentId)\n } catch (err) {\n toast.error(`Não foi possível salvar: ${(err as Error)?.message ?? err}`)\n } finally {\n setCommitting(false)\n }\n }, [generation, session, value, schema, templateName, templateCategory, edited, onCommitted])\n\n if (generation.status === 'running' || generation.status === 'pending') {\n return (\n <div className=\"flex flex-col items-center justify-center gap-2 py-16 text-sm text-muted-foreground\">\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n <p>Gerando {templateName.toLowerCase()}…</p>\n </div>\n )\n }\n\n if (generation.status === 'failed') {\n return (\n <div className=\"flex flex-col items-center justify-center gap-3 py-16 text-sm\">\n <p className=\"text-destructive\">{generation.error ?? 'A geração falhou.'}</p>\n <Button size=\"sm\" variant=\"outline\" onClick={onRegenerate}>\n <RefreshCw className=\"mr-1.5 h-4 w-4\" />\n Tentar novamente\n </Button>\n </div>\n )\n }\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n {edited ? (\n <Badge variant=\"secondary\">Editado</Badge>\n ) : (\n <Badge variant=\"outline\">Gerado por IA</Badge>\n )}\n {saving && (\n <span className=\"flex items-center gap-1\">\n <Loader2 className=\"h-3 w-3 animate-spin\" />\n salvando…\n </span>\n )}\n {generation.model && <span className=\"hidden sm:inline\">· {generation.model}</span>}\n </div>\n\n <div className=\"flex gap-1.5\">\n <Button size=\"sm\" variant=\"ghost\" onClick={onRegenerate} disabled={committing}>\n <RefreshCw className=\"mr-1.5 h-3.5 w-3.5\" />\n Regerar\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={committing}\n onClick={() =>\n void discardGeneration(generation.id).then(onDiscarded).catch(() => toast.error('Falha ao descartar'))\n }\n >\n <Trash2 className=\"mr-1.5 h-3.5 w-3.5\" />\n {labels.discard}\n </Button>\n <Button size=\"sm\" onClick={() => void handleCommit()} disabled={committing || !value.trim()}>\n {committing ? (\n <Loader2 className=\"mr-1.5 h-3.5 w-3.5 animate-spin\" />\n ) : (\n <Check className=\"mr-1.5 h-3.5 w-3.5\" />\n )}\n Salvar na ficha\n </Button>\n </div>\n </div>\n\n <MarkdownEditor\n value={value}\n onChange={handleChange}\n minRows={20}\n defaultMode=\"preview\"\n editLabel=\"Editar\"\n previewLabel=\"Visualizar\"\n />\n\n {schema.disclaimer && (\n <p className=\"text-xs italic text-muted-foreground\">{schema.disclaimer}</p>\n )}\n </div>\n )\n}\n","import { getSupabaseClientOptional } from '@fayz-ai/core'\nimport { T } from '../data/tables'\nimport type { NarrativeSchema, ScribeSttConfig } from '../types'\nimport {\n PROMPT_VERSION,\n buildSystemPrompt,\n buildUserPrompt,\n renderTranscript,\n type TranscriptLine,\n} from './prompt'\nimport { resolveScribeTransport } from './transport'\n\n// Generation through the platform broker.\n//\n// Same dismantling as the STT drain: the edge function orchestrated AND held\n// the key, and only the key had to stay out of the client's reach. The prompt\n// is built by the same `lib/prompt.ts` on both sides, which is why it never\n// imported anything from Deno or the browser.\n//\n// What stays intact, because it is what makes a generation a DOCUMENT and not a\n// conversation:\n//\n// - the row is born `running` BEFORE the model call. A generation that hangs\n// without a trace is indistinguishable from one never asked for.\n// - `markdown` is written once and never again; human edits go to\n// `markdown_edited`. The difference between them is the proof somebody\n// reviewed it, and the metric that says whether the model is good.\n// - `prompt_version`, `model` and tokens are stored, so it is auditable.\n//\n// None of that fits in a chat turn, which is why generation stays outside the\n// assistant even now that the model comes from the same broker.\n\n/** Reproducing the same note twice matters more here than sounding creative. */\nconst TEMPERATURE = 0.2\nconst MAX_TOKENS = 4000\n\nfunction client(): any {\n const supabase = getSupabaseClientOptional() as any\n if (!supabase) throw new Error('Supabase not initialised')\n return supabase\n}\n\nexport interface GenerateResult {\n generationId: string\n markdown: string\n model: string\n}\n\n/**\n * Generates a document from the session transcript.\n *\n * Returns `null` when the configured transport is not the browser one — the\n * caller must use the edge function then.\n */\nexport async function generateViaTransport(args: {\n sessionId: string\n schema: NarrativeSchema\n templateName: string\n templateId?: string\n templateKey?: string\n tabOrder?: number\n stt: ScribeSttConfig\n model?: string\n}): Promise<GenerateResult | null> {\n const transport = resolveScribeTransport(args.stt)\n if (!transport) return null\n\n const supabase = client()\n\n const { data: userData } = await supabase.auth.getUser()\n const userId = userData?.user?.id\n if (!userId) throw new Error('não autenticado')\n\n const { data: session } = await supabase\n .from(T.sessions)\n .select('id, tenant_id, subject_id, started_at, audio_duration_ms, locale')\n .eq('id', args.sessionId)\n .maybeSingle()\n if (!session) throw new Error('sessão não encontrada')\n\n const lines = await loadTranscript(supabase, args.sessionId)\n if (!lines.some((l) => !l.gap && l.text.trim())) {\n throw new Error('não há transcrição para gerar o documento')\n }\n\n const { data: created, error: insErr } = await supabase\n .from(T.generations)\n .insert({\n tenant_id: session.tenant_id,\n session_id: args.sessionId,\n template_id: args.templateId ?? null,\n template_key: args.templateKey ?? null,\n tab_order: args.tabOrder ?? 0,\n status: 'running',\n prompt_version: PROMPT_VERSION,\n created_by: userId,\n })\n .select('id')\n .single()\n if (insErr || !created) throw new Error(`falha ao criar a geração: ${insErr?.message}`)\n\n const generationId: string = created.id\n\n try {\n let subjectName: string | undefined\n if (session.subject_id) {\n const { data: person } = await supabase\n .from('people')\n .select('name')\n .eq('id', session.subject_id)\n .maybeSingle()\n subjectName = person?.name\n }\n\n const result = await transport.complete({\n system: buildSystemPrompt(args.schema),\n user: buildUserPrompt({\n schema: args.schema,\n transcript: renderTranscript(lines),\n context: {\n Titular: subjectName,\n Data: new Date(session.started_at).toLocaleDateString(session.locale ?? 'pt-BR'),\n 'Duração (min)': String(Math.round((session.audio_duration_ms ?? 0) / 60000)),\n },\n }),\n model: args.model,\n temperature: TEMPERATURE,\n maxTokens: MAX_TOKENS,\n })\n\n const markdown = result.content.trim()\n if (!markdown) throw new Error('o modelo devolveu um documento vazio')\n\n await supabase\n .from(T.generations)\n .update({\n status: 'ready',\n markdown,\n // The document TYPE, not a summary of it: \"Anamnese\" identifies it;\n // the first sentence changes with every generation.\n title: args.templateName,\n model: result.model,\n input_tokens: result.tokensIn ?? null,\n output_tokens: result.tokensOut ?? null,\n updated_at: new Date().toISOString(),\n })\n .eq('id', generationId)\n\n await supabase\n .from(T.sessions)\n .update({ status: 'generating', updated_at: new Date().toISOString() })\n .eq('id', args.sessionId)\n\n return { generationId, markdown, model: result.model }\n } catch (err) {\n const message = String((err as Error)?.message ?? err)\n await supabase\n .from(T.generations)\n .update({ status: 'failed', error: message.slice(0, 500), updated_at: new Date().toISOString() })\n .eq('id', generationId)\n throw err\n }\n}\n\nasync function loadTranscript(supabase: any, sessionId: string): Promise<TranscriptLine[]> {\n const { data } = await supabase\n .from(T.segments)\n .select('seg_index, start_offset_ms, text, gap, words')\n .eq('session_id', sessionId)\n .order('seg_index', { ascending: true })\n if (!data) return []\n\n return data.map((s: any) => ({\n startOffsetMs: s.start_offset_ms ?? 0,\n text: s.text ?? '',\n gap: !!s.gap,\n speaker: Array.isArray(s.words) && s.words.length > 0 ? s.words[0]?.sp : undefined,\n }))\n}\n","import * as React from 'react'\nimport { FileText, Loader2, Sparkles } from 'lucide-react'\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n Skeleton,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n toast,\n} from '@fayz-ai/ui/primitives'\nimport { SubpageHeader } from '@fayz-ai/ui/layout'\nimport { useAgentSurface } from '@fayz-ai/admin'\nimport { TranscriptPanel } from './TranscriptPanel'\nimport { DraftPanel } from './DraftPanel'\nimport {\n fetchGenerations,\n fetchSegments,\n fetchSession,\n invokeGenerate,\n invokeTranscribe,\n} from '../data/supabase'\nimport { drainSessionViaTransport } from '../lib/drain'\nimport { generateViaTransport } from '../lib/generate'\nimport { formatElapsed } from '../store'\nimport type {\n NarrativeTemplatePreset,\n ScribeGeneration,\n ScribeLabels,\n ScribeSegment,\n ScribeSession,\n ScribeSttConfig,\n} from '../types'\n\n// ---------------------------------------------------------------------------\n// A tela da sessão.\n//\n// Ela faz POLLING enquanto há trabalho pendente, e isso é uma escolha, não uma\n// preguiça: o SDK não tem infraestrutura de job em background, então quem\n// empurra a fila de STT é o cliente que estiver com a tela aberta. O desenho\n// tolera isso porque `stt_state='pending'` PERSISTE — se ninguém estiver\n// olhando, o trabalho fica na fila e o próximo cliente a abrir a sessão drena.\n// Nada se perde por ninguém estar olhando; só demora mais.\n//\n// (O caminho definitivo é um trigger + pg_net disparando a function, como o\n// google-calendar-sync faz. Fica para quando `pg_net` estiver confirmado no\n// pool — está listado como item de verificação do plano.)\n// ---------------------------------------------------------------------------\n\nconst POLL_MS = 5_000\n\nexport interface ScribeSessionPageProps {\n sessionId: string\n labels: ScribeLabels\n presets: NarrativeTemplatePreset[]\n stt: ScribeSttConfig\n generateEndpoint: string\n onOpenDocument?: (documentId: string) => void\n onBack?: () => void\n /** Rendered INSIDE another screen rather than as its own page: keeps the\n * back navigation with the content instead of hoisting it to the shell bar,\n * which still belongs to the record. */\n embedded?: boolean\n}\n\nexport function ScribeSessionPage({\n sessionId,\n embedded,\n labels,\n presets,\n stt,\n generateEndpoint,\n onOpenDocument,\n onBack,\n}: ScribeSessionPageProps) {\n const [session, setSession] = React.useState<ScribeSession | null>(null)\n const [segments, setSegments] = React.useState<ScribeSegment[]>([])\n const [generations, setGenerations] = React.useState<ScribeGeneration[]>([])\n const [loading, setLoading] = React.useState(true)\n const [generating, setGenerating] = React.useState(false)\n const [tab, setTab] = React.useState('transcript')\n const draining = React.useRef(false)\n\n const refresh = React.useCallback(async () => {\n const [s, segs, gens] = await Promise.all([\n fetchSession(sessionId),\n fetchSegments(sessionId),\n fetchGenerations(sessionId),\n ])\n setSession(s)\n setSegments(segs)\n setGenerations(gens)\n return { session: s, segments: segs }\n }, [sessionId])\n\n React.useEffect(() => {\n setLoading(true)\n void refresh().finally(() => setLoading(false))\n }, [refresh])\n\n // Há trabalho pendente? Segmento que subiu e ainda não voltou do STT.\n const pendingStt = React.useMemo(\n () => segments.filter((s) => s.uploadState === 'uploaded' && ['pending', 'failed'].includes(s.sttState) && s.sttAttempts < 4),\n [segments],\n )\n const busy = pendingStt.length > 0 || segments.some((s) => s.sttState === 'running')\n\n React.useEffect(() => {\n if (!busy) return\n const id = setInterval(() => {\n void (async () => {\n // Um guard contra reentrância: a chamada de drain leva mais que o\n // intervalo do poll, e disparar a segunda por cima da primeira faria\n // dois clientes transcreverem o mesmo segmento.\n if (draining.current) return\n draining.current = true\n try {\n if (pendingStt.length > 0) {\n // `null` = a configuração escolheu a edge function; qualquer outra\n // coisa e o dreno já rodou aqui, contra o broker da plataforma.\n const drained = await drainSessionViaTransport(sessionId, stt)\n if (drained === null) {\n await invokeTranscribe({\n endpoint: stt.endpoint!,\n sessionId,\n provider: stt.provider,\n model: stt.model,\n diarize: stt.diarize,\n })\n }\n }\n await refresh()\n } catch {\n // Falha de drain não pode derrubar a tela; a próxima volta tenta.\n } finally {\n draining.current = false\n }\n })()\n }, POLL_MS)\n return () => clearInterval(id)\n }, [busy, pendingStt.length, sessionId, stt, refresh])\n\n const hasTranscript = segments.some((s) => !s.gap && s.text?.trim())\n\n const handleGenerate = React.useCallback(\n async (preset: NarrativeTemplatePreset) => {\n setGenerating(true)\n try {\n const viaTransport = await generateViaTransport({\n sessionId,\n schema: preset.schema,\n templateName: typeof preset.name === 'string' ? preset.name : 'Documento',\n templateKey: preset.key,\n tabOrder: generations.length,\n stt,\n })\n const generationId =\n viaTransport?.generationId ??\n (\n await invokeGenerate({\n endpoint: generateEndpoint,\n sessionId,\n templateKey: preset.key,\n schema: preset.schema,\n tabOrder: generations.length,\n })\n ).generationId\n await refresh()\n setTab(generationId)\n } catch (err) {\n toast.error(`Não foi possível gerar: ${(err as Error)?.message ?? err}`)\n } finally {\n setGenerating(false)\n }\n },\n [generateEndpoint, sessionId, generations.length, refresh, stt],\n )\n\n // -------------------------------------------------------------------------\n // Agent surface — o assistente nativo, sem infraestrutura nova.\n //\n // A geração NÃO passa por aqui (o turn loop tem teto de 4 rodadas, vive num\n // hook que morre na navegação e persistiria a transcrição no histórico de\n // conversa do broker). O que passa é a conversa DEPOIS: \"o que foi dito sobre\n // a dor?\", \"resuma para o paciente\". Isso é pequeno, limitado e cabe folgado.\n // -------------------------------------------------------------------------\n const activeGeneration = generations.find((g) => g.id === tab)\n useAgentSurface(\n React.useMemo(\n () => ({\n id: 'scribe.session',\n describe: () => ({\n title: `${labels.sessionSingular} — ${session?.startedAt ? new Date(session.startedAt).toLocaleDateString() : ''}`,\n state: {\n durationMinutes: Math.round((session?.audioDurationMs ?? 0) / 60000),\n transcriptExcerpt: segments\n .filter((s) => s.text)\n .map((s) => s.text)\n .join(' ')\n .slice(0, 4000),\n documentMarkdown: activeGeneration?.markdownEdited ?? activeGeneration?.markdown,\n documentStatus: activeGeneration?.status,\n },\n record: session?.subjectId\n ? { entity: 'person', id: session.subjectId, label: labels.subject }\n : undefined,\n }),\n instructions: [\n `O usuário está revisando um ${labels.sessionSingular.toLowerCase()} gravado e transcrito.`,\n 'Responda \"o que foi dito sobre X\" citando o trecho da transcrição, sempre com o minuto.',\n 'Nunca afirme algo que a transcrição não sustente — diga que não foi mencionado.',\n ].join(' '),\n }),\n [session, segments, activeGeneration, labels],\n ) as any,\n )\n\n if (loading) {\n return (\n // Same silhouette as the loaded screen, so nothing jumps on arrival.\n <div className=\"space-y-4\" aria-busy=\"true\">\n <div className=\"flex gap-2\">\n <Skeleton className=\"h-5 w-40 rounded-full\" />\n <Skeleton className=\"h-5 w-28 rounded-full\" />\n <Skeleton className=\"h-5 w-20 rounded-full\" />\n </div>\n <Skeleton className=\"h-8 w-32 rounded-lg\" />\n <div className=\"space-y-3 rounded-xl border p-4\">\n {[0, 1, 2].map((row) => (\n <div key={row} className=\"flex gap-3\">\n <Skeleton className=\"h-3 w-10 shrink-0\" />\n <div className=\"min-w-0 flex-1 space-y-1.5\">\n <Skeleton className=\"h-3 w-full\" />\n <Skeleton className={row === 1 ? 'h-3 w-2/5' : 'h-3 w-4/5'} />\n </div>\n </div>\n ))}\n </div>\n </div>\n )\n }\n\n if (!session) {\n return <p className=\"py-16 text-center text-sm text-muted-foreground\">Sessão não encontrada.</p>\n }\n\n const uploadPending = session.segmentCount - session.uploadedSegmentCount\n\n return (\n <div className=\"space-y-4\">\n <SubpageHeader\n title={labels.sessionSingular}\n parentLabel={onBack ? labels.sessionPlural : undefined}\n onBack={onBack}\n // Opened inside a tab, the page never changed: the back link stays\n // beside the content instead of rewriting the record's own path.\n inline={embedded}\n />\n\n <div className=\"flex flex-wrap items-center gap-2 text-sm text-muted-foreground\">\n <Badge variant=\"outline\">{new Date(session.startedAt).toLocaleString()}</Badge>\n <Badge variant=\"outline\">{formatElapsed(session.audioDurationMs)} de áudio</Badge>\n <Badge variant={session.status === 'ready' || session.status === 'completed' ? 'secondary' : 'outline'}>\n {statusLabel(session.status)}\n </Badge>\n {uploadPending > 0 && (\n <Badge variant=\"outline\" className=\"text-amber-600\">\n {uploadPending} trecho{uploadPending > 1 ? 's' : ''} enviando\n </Badge>\n )}\n </div>\n\n <Tabs value={tab} onValueChange={setTab}>\n <TabsList className=\"max-w-full justify-start overflow-x-auto scrollbar-none\">\n <TabsTrigger value=\"transcript\" className=\"gap-1.5\">\n <FileText className=\"h-3.5 w-3.5\" />\n {labels.transcript}\n </TabsTrigger>\n {generations.map((g) => (\n <TabsTrigger key={g.id} value={g.id} className=\"gap-1.5\">\n <Sparkles className=\"h-3.5 w-3.5\" />\n {g.title ?? presets.find((p) => p.key === g.templateKey)?.name ?? 'Documento'}\n </TabsTrigger>\n ))}\n </TabsList>\n\n <TabsContent value=\"transcript\" className=\"mt-4\">\n <Card>\n <CardContent className=\"pt-6\">\n <TranscriptPanel segments={segments} pending={busy} />\n </CardContent>\n </Card>\n\n {hasTranscript && (\n <div className=\"mt-4 flex flex-wrap gap-2\">\n {presets.map((preset) => (\n <Button\n key={preset.key}\n size=\"sm\"\n variant={generations.length === 0 ? 'default' : 'outline'}\n disabled={generating}\n onClick={() => void handleGenerate(preset)}\n >\n {generating ? (\n <Loader2 className=\"mr-1.5 h-4 w-4 animate-spin\" />\n ) : (\n <Sparkles className=\"mr-1.5 h-4 w-4\" />\n )}\n {labels.generate}: {preset.name}\n </Button>\n ))}\n </div>\n )}\n </TabsContent>\n\n {generations.map((g) => {\n const preset = presets.find((p) => p.key === g.templateKey)\n if (!preset) return null\n return (\n <TabsContent key={g.id} value={g.id} className=\"mt-4\">\n <DraftPanel\n generation={g}\n session={session}\n schema={preset.schema}\n templateName={preset.name}\n templateCategory={preset.category}\n labels={labels}\n onRegenerate={() => void handleGenerate(preset)}\n onCommitted={(documentId) => {\n void refresh()\n onOpenDocument?.(documentId)\n }}\n onDiscarded={() => {\n setTab('transcript')\n void refresh()\n }}\n />\n </TabsContent>\n )\n })}\n </Tabs>\n </div>\n )\n}\n\nfunction statusLabel(status: ScribeSession['status']): string {\n const map: Record<string, string> = {\n recording: 'Gravando',\n paused: 'Pausado',\n interrupted: 'Interrompido',\n uploading: 'Enviando',\n transcribing: 'Transcrevendo',\n ready: 'Pronto',\n generating: 'Rascunho',\n completed: 'Concluído',\n abandoned: 'Descartado',\n failed: 'Falhou',\n }\n return map[status] ?? status\n}\n","import * as React from 'react'\nimport { openAssistant, useAccessOptional } from '@fayz-ai/admin'\nimport { isCaptureSupported } from '../runtime'\nimport { requestScribeStart } from './ScribeShellMount'\nimport { ScribeStartButton } from './ScribeRecordingPill'\nimport type { ScribeLabels } from '../types'\n\n// The record verb where it belongs: on the name row, next to Edit, via the\n// `<entity>.detail.header.actions` zone.\n//\n// The only reachable zone used to be BELOW the tabs, so the app hid the button\n// inside a tab — and a tab is a place, not an action. Coming from the plugin it\n// also cannot exist without its engine: the app-registered tab showed up with\n// scribe disabled, and the click fired `scribe:start` at nobody.\n\nexport interface ScribeHeaderActionProps {\n labels: ScribeLabels\n /** The record CrudDetailPage is showing. */\n item?: { id?: string; personId?: string; name?: string; full_name?: string }\n embedded?: boolean\n}\n\nexport function ScribeHeaderAction({ labels, item, embedded }: ScribeHeaderActionProps) {\n // Extension tables point at the `people` row that owns the session.\n const subjectId = item?.personId ?? item?.id\n const subjectName = item?.name ?? item?.full_name\n // Recording costs real money per minute, so the plan decides whether it\n // exists here — otherwise the button invites people into a wall.\n const { entitled } = useAccessOptional()\n\n if (!subjectId) return null\n if (!entitled('scribe')) return null\n if (!isCaptureSupported()) return null\n\n return (\n <ScribeStartButton\n labels={labels}\n variant=\"outline\"\n size={embedded ? 'sm' : 'sm'}\n onStart={() => {\n // The assistant comes up with it: from here it carries the session\n // while the user navigates away, which is the normal case.\n openAssistant()\n requestScribeStart({ subjectId, subjectName })\n }}\n />\n )\n}\n","// AUTO-GENERATED from 001_scribe_base.sql — regenerate with scripts/embed-migrations.mjs\n// SQL files are the source of truth; this inline copy lets the manifest declare\n// migrations as data. Do not edit by hand — run the embed script instead.\n\nexport const MIGRATION_001_SCRIBE_BASE = `-- plugin-scribe 001: gravar um atendimento inteiro sem poder perdê-lo.\n--\n-- O problema que este schema resolve não é \"guardar áudio\". É que o profissional\n-- aperta gravar, navega o app por 40 minutos, e QUALQUER coisa pode acontecer no\n-- meio: reload, aba morta, wifi caindo, tampa fechando. Perder a consulta é uma\n-- falha irrecuperável — não dá para pedir para o paciente repetir.\n--\n-- Por isso o desenho não tem \"o arquivo da sessão\". Tem um MANIFESTO: a sessão\n-- é uma linha, e cada 30 segundos de áudio é OUTRA linha que sobe sozinha e é\n-- transcrita sozinha. O que chegou, chegou. O cliente pode morrer entre dois\n-- segmentos e o que já subiu continua íntegro e já transcrito.\n--\n-- ---------------------------------------------------------------------------\n-- Duas durações, e elas discordam de propósito\n-- ---------------------------------------------------------------------------\n-- \\`wall_duration_ms\\` é o relógio de parede que o browser reportou. Serve para o\n-- cronômetro na tela e para mais nada.\n--\n-- \\`audio_duration_ms\\` é SUM(segments.duration_ms) — soma do áudio que de fato\n-- existe, com a duração vinda da resposta do STT, não de um \\`setInterval\\`.\n--\n-- Elas divergem sempre que a aba congela: notebook dormindo por 10 minutos faz\n-- o relógio de parede contar a soneca inteira enquanto o MediaRecorder não\n-- emitiu um byte. Registro clínico, medidor de cota e fatura usam a SEGUNDA.\n-- Confundir as duas é cobrar do cliente pelo cochilo do notebook.\n--\n-- ---------------------------------------------------------------------------\n-- O contador nunca é escrito pelo browser\n-- ---------------------------------------------------------------------------\n-- \\`scribe_record_segment\\` (seção 5) faz o upsert do segmento e RECALCULA os\n-- contadores da sessão a partir da tabela de segmentos. Um cliente que sobe o\n-- mesmo segmento duas vezes (replay depois de queda de rede) converge para o\n-- mesmo número em vez de somar duas vezes.\n\n-- ---------------------------------------------------------------------------\n-- 1. sessions — o encontro\n-- ---------------------------------------------------------------------------\n-- \\`id\\` NÃO tem DEFAULT gen_random_uuid(). É gerado no cliente, antes do primeiro\n-- byte, e usado como chave no IndexedDB e no caminho do Storage. Sem isso a\n-- gravação teria que esperar um round-trip para saber onde escrever — e uma\n-- gravação que depende da rede para COMEÇAR já nasce frágil.\nCREATE TABLE IF NOT EXISTS public.plg_scribe_sessions (\n id uuid PRIMARY KEY,\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n\n -- O titular. SET NULL e não CASCADE: apagar a pessoa não pode apagar em\n -- silêncio o registro do atendimento que aconteceu.\n subject_id uuid REFERENCES public.people(id) ON DELETE SET NULL,\n context_kind text NOT NULL DEFAULT 'person'\n CHECK (context_kind IN ('person', 'appointment', 'standalone')),\n appointment_id uuid REFERENCES public.appointments(id) ON DELETE SET NULL,\n owner_user_id uuid NOT NULL,\n\n status text NOT NULL DEFAULT 'recording'\n CHECK (status IN ('recording','paused','interrupted','uploading','transcribing',\n 'ready','generating','completed','abandoned','failed')),\n locale text NOT NULL DEFAULT 'pt-BR',\n mime_type text,\n\n started_at timestamptz NOT NULL DEFAULT now(),\n ended_at timestamptz,\n wall_duration_ms integer NOT NULL DEFAULT 0,\n audio_duration_ms integer NOT NULL DEFAULT 0,\n\n segment_count integer NOT NULL DEFAULT 0,\n uploaded_segment_count integer NOT NULL DEFAULT 0,\n transcribed_segment_count integer NOT NULL DEFAULT 0,\n transcript_chars integer NOT NULL DEFAULT 0,\n\n -- Base legal, carimbada antes do primeiro byte. \"Quando\" e \"como\" é o que se\n -- prova depois; guardar isso na mesma linha da gravação é o que torna a prova\n -- barata.\n consent_at timestamptz,\n consent_mode text CHECK (consent_mode IN ('verbal','written','implied_contract')),\n\n -- Retenção: o áudio some, a transcrição e o documento assinado ficam.\n retention_until date,\n audio_deleted_at timestamptz,\n\n error text,\n metadata jsonb NOT NULL DEFAULT '{}'::jsonb,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\nALTER TABLE public.plg_scribe_sessions ENABLE ROW LEVEL SECURITY;\n\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_sessions_tenant\n ON public.plg_scribe_sessions(tenant_id);\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_sessions_subject\n ON public.plg_scribe_sessions(subject_id) WHERE subject_id IS NOT NULL;\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_sessions_appointment\n ON public.plg_scribe_sessions(appointment_id) WHERE appointment_id IS NOT NULL;\n\n-- A consulta da recuperação: \"este usuário tem alguma sessão aberta?\". Roda em\n-- todo boot do app, então é índice parcial e não varredura.\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_sessions_open\n ON public.plg_scribe_sessions(tenant_id, owner_user_id, status)\n WHERE status IN ('recording','paused','interrupted','uploading','transcribing','ready','generating');\n\n-- A consulta do job de retenção.\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_sessions_retention\n ON public.plg_scribe_sessions(retention_until)\n WHERE audio_deleted_at IS NULL AND retention_until IS NOT NULL;\n\n-- ---------------------------------------------------------------------------\n-- 2. segments — o manifesto dos pedaços\n-- ---------------------------------------------------------------------------\n-- A PK é (session_id, seg_index) e não um uuid: o índice do segmento já é\n-- único dentro da sessão, e usar isso como chave faz o re-upload do mesmo\n-- pedaço ser um ON CONFLICT em vez de uma linha duplicada. Idempotência de\n-- graça, que é exatamente o que um pump com retry infinito precisa.\nCREATE TABLE IF NOT EXISTS public.plg_scribe_segments (\n session_id uuid NOT NULL REFERENCES public.plg_scribe_sessions(id) ON DELETE CASCADE,\n seg_index integer NOT NULL,\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n\n storage_path text,\n bytes integer,\n start_offset_ms integer NOT NULL,\n -- Vem da resposta do STT. O relógio do JS não é testemunha confiável de\n -- quanto áudio existe: ele conta tempo, não amostras.\n duration_ms integer,\n\n -- Montado a partir de pieces de um segmento cortado no meio (crash). Vale\n -- transcrever mesmo assim: 22 segundos de consulta é melhor que zero.\n partial boolean NOT NULL DEFAULT false,\n\n -- Linha SINTÉTICA: não tem áudio, marca um buraco (sono, mic perdido,\n -- permissão revogada). Existe para a transcrição ADMITIR o buraco em vez de\n -- emendar dois momentos como se fossem contínuos. Isso é requisito\n -- médico-legal: um registro que esconde a costura é pior que um com furo.\n gap boolean NOT NULL DEFAULT false,\n\n upload_state text NOT NULL DEFAULT 'pending'\n CHECK (upload_state IN ('pending','uploaded','missing')),\n uploaded_at timestamptz,\n\n stt_state text NOT NULL DEFAULT 'pending'\n CHECK (stt_state IN ('pending','running','done','failed','skipped')),\n stt_provider text,\n stt_attempts smallint NOT NULL DEFAULT 0,\n stt_error text,\n\n text text,\n -- [{w,s,e,c,sp}] com offsets RELATIVOS a este segmento. Absoluto se calcula\n -- somando start_offset_ms; guardar relativo é o que deixa reprocessar um\n -- segmento sozinho sem reescrever os vizinhos.\n words jsonb,\n confidence real,\n\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now(),\n PRIMARY KEY (session_id, seg_index)\n);\nALTER TABLE public.plg_scribe_segments ENABLE ROW LEVEL SECURITY;\n\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_segments_tenant\n ON public.plg_scribe_segments(tenant_id);\n\n-- A fila do STT. Parcial porque só interessa o que já subiu e ainda não foi\n-- transcrito — que é uma fração minúscula da tabela em regime permanente.\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_segments_stt_queue\n ON public.plg_scribe_segments(tenant_id, stt_state, created_at)\n WHERE upload_state = 'uploaded' AND stt_state IN ('pending','failed');\n\n-- ---------------------------------------------------------------------------\n-- 3. generations — uma tentativa de documento\n-- ---------------------------------------------------------------------------\n-- DUAS colunas de markdown, e isso não é redundância.\n--\n-- \\`markdown\\` saída do modelo, escrita uma vez, nunca mutada\n-- \\`markdown_edited\\` a cópia de trabalho do humano\n--\n-- A diferença entre elas é a única prova de que alguém revisou. Ela sustenta\n-- três coisas: o selo \"revisado pelo profissional\", o diff que diz se o prompt\n-- está bom (documento muito editado = prompt ruim), e a cadeia de proveniência\n-- que um registro médico-legal precisa ter. Guardar só o texto final apaga tudo\n-- isso para economizar uma coluna de texto.\nCREATE TABLE IF NOT EXISTS public.plg_scribe_generations (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n session_id uuid NOT NULL REFERENCES public.plg_scribe_sessions(id) ON DELETE CASCADE,\n\n -- Um dos dois. \\`template_id\\` quando o tenant forkou e tem linha própria;\n -- \\`template_key\\` quando é preset de código (que de propósito não tem linha,\n -- para não consumir a cota de templates do plano).\n template_id uuid,\n template_key text,\n\n tab_order integer NOT NULL DEFAULT 0,\n title text,\n status text NOT NULL DEFAULT 'pending'\n CHECK (status IN ('pending','running','ready','failed','committed','discarded')),\n\n markdown text,\n markdown_edited text,\n\n -- Extração stage-1 do modo two-stage. A coluna entra agora, vazia, porque a\n -- interface já nasce com esse formato — quando as abas de multi-documento\n -- chegarem, é preencher, não migrar.\n facts jsonb,\n\n model text,\n prompt_version text,\n input_tokens integer,\n output_tokens integer,\n error text,\n\n document_id uuid,\n created_by uuid,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\nALTER TABLE public.plg_scribe_generations ENABLE ROW LEVEL SECURITY;\n\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_generations_session\n ON public.plg_scribe_generations(session_id, tab_order);\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_generations_tenant\n ON public.plg_scribe_generations(tenant_id);\n\n-- FKs para plugin-forms e para o arquétipo de documentos são condicionais: o\n-- scribe declara \\`dependencies: ['custom_forms']\\`, mas a ordem de aplicação\n-- entre plugins não é garantida e um pool pode aplicar este arquivo antes.\n-- Guardado, o re-apply converge; sem guarda, quebraria o passo inteiro.\nDO $$\nBEGIN\n IF to_regclass('public.plg_forms_templates') IS NOT NULL\n AND NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'plg_scribe_generations_template_fk') THEN\n ALTER TABLE public.plg_scribe_generations\n ADD CONSTRAINT plg_scribe_generations_template_fk\n FOREIGN KEY (template_id) REFERENCES public.plg_forms_templates(id) ON DELETE SET NULL;\n END IF;\n\n IF to_regclass('public.documents') IS NOT NULL\n AND NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'plg_scribe_generations_document_fk') THEN\n ALTER TABLE public.plg_scribe_generations\n ADD CONSTRAINT plg_scribe_generations_document_fk\n FOREIGN KEY (document_id) REFERENCES public.documents(id) ON DELETE SET NULL;\n END IF;\nEND $$;\n\n-- ---------------------------------------------------------------------------\n-- 4. RLS — a forma canônica deste repo, três tabelas x quatro verbos\n-- ---------------------------------------------------------------------------\nDROP POLICY IF EXISTS plg_scribe_sessions_select ON public.plg_scribe_sessions;\nCREATE POLICY plg_scribe_sessions_select ON public.plg_scribe_sessions FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_sessions_insert ON public.plg_scribe_sessions;\nCREATE POLICY plg_scribe_sessions_insert ON public.plg_scribe_sessions FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_sessions_update ON public.plg_scribe_sessions;\nCREATE POLICY plg_scribe_sessions_update ON public.plg_scribe_sessions FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_sessions_delete ON public.plg_scribe_sessions;\nCREATE POLICY plg_scribe_sessions_delete ON public.plg_scribe_sessions FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_scribe_sessions TO authenticated;\n\nDROP POLICY IF EXISTS plg_scribe_segments_select ON public.plg_scribe_segments;\nCREATE POLICY plg_scribe_segments_select ON public.plg_scribe_segments FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_segments_insert ON public.plg_scribe_segments;\nCREATE POLICY plg_scribe_segments_insert ON public.plg_scribe_segments FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_segments_update ON public.plg_scribe_segments;\nCREATE POLICY plg_scribe_segments_update ON public.plg_scribe_segments FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_segments_delete ON public.plg_scribe_segments;\nCREATE POLICY plg_scribe_segments_delete ON public.plg_scribe_segments FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_scribe_segments TO authenticated;\n\nDROP POLICY IF EXISTS plg_scribe_generations_select ON public.plg_scribe_generations;\nCREATE POLICY plg_scribe_generations_select ON public.plg_scribe_generations FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_generations_insert ON public.plg_scribe_generations;\nCREATE POLICY plg_scribe_generations_insert ON public.plg_scribe_generations FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_generations_update ON public.plg_scribe_generations;\nCREATE POLICY plg_scribe_generations_update ON public.plg_scribe_generations FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_generations_delete ON public.plg_scribe_generations;\nCREATE POLICY plg_scribe_generations_delete ON public.plg_scribe_generations FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_scribe_generations TO authenticated;\n\n-- ---------------------------------------------------------------------------\n-- 5. scribe_record_segment — o browser registra, o banco conta\n-- ---------------------------------------------------------------------------\n-- SECURITY INVOKER: a RLS acima já amarra o tenant. Não há nada aqui que\n-- precise de privilégio elevado, e uma função SECURITY DEFINER que não precisa\n-- ser é só superfície de ataque a mais.\n--\n-- O ponto da função é a segunda metade: recalcular os contadores da sessão a\n-- partir de um SELECT sobre os segmentos, nunca de um \\`+1\\` vindo do cliente.\n-- Um pump com retry infinito vai reenviar o mesmo segmento, e um contador\n-- incremental transformaria isso em números que crescem sem o áudio crescer.\nCREATE OR REPLACE FUNCTION public.scribe_record_segment(p_payload jsonb)\nRETURNS jsonb\nLANGUAGE plpgsql\nSECURITY INVOKER\nSET search_path TO 'public'\nAS $function$\nDECLARE\n v_session_id uuid;\n v_seg_index integer;\n v_tenant_id uuid;\n v_result jsonb;\nBEGIN\n v_session_id := (p_payload->>'session_id')::uuid;\n v_seg_index := (p_payload->>'seg_index')::integer;\n\n IF v_session_id IS NULL OR v_seg_index IS NULL THEN\n RAISE EXCEPTION 'session_id and seg_index are required';\n END IF;\n\n -- O tenant vem da SESSÃO, nunca do payload. Se o cliente pudesse informar o\n -- tenant, ele escolheria em que pasta escrever.\n SELECT s.tenant_id INTO v_tenant_id\n FROM plg_scribe_sessions s WHERE s.id = v_session_id;\n\n IF v_tenant_id IS NULL THEN\n -- Sessão inexistente OU invisível pela RLS: a mesma resposta nos dois\n -- casos, de propósito — não vaza a existência de sessão de outro tenant.\n RAISE EXCEPTION 'unknown session';\n END IF;\n\n INSERT INTO plg_scribe_segments (\n session_id, seg_index, tenant_id, storage_path, bytes,\n start_offset_ms, duration_ms, partial, gap, upload_state, uploaded_at, stt_state\n ) VALUES (\n v_session_id, v_seg_index, v_tenant_id,\n p_payload->>'storage_path',\n (p_payload->>'bytes')::integer,\n COALESCE((p_payload->>'start_offset_ms')::integer, 0),\n (p_payload->>'duration_ms')::integer,\n COALESCE((p_payload->>'partial')::boolean, false),\n COALESCE((p_payload->>'gap')::boolean, false),\n CASE WHEN COALESCE((p_payload->>'gap')::boolean, false) THEN 'missing' ELSE 'uploaded' END,\n now(),\n -- Buraco não vai para o STT: não há áudio para transcrever, e mandá-lo\n -- queimaria uma chamada para receber string vazia.\n CASE WHEN COALESCE((p_payload->>'gap')::boolean, false) THEN 'skipped' ELSE 'pending' END\n )\n ON CONFLICT (session_id, seg_index) DO UPDATE SET\n storage_path = COALESCE(EXCLUDED.storage_path, plg_scribe_segments.storage_path),\n bytes = COALESCE(EXCLUDED.bytes, plg_scribe_segments.bytes),\n duration_ms = COALESCE(EXCLUDED.duration_ms, plg_scribe_segments.duration_ms),\n partial = EXCLUDED.partial,\n upload_state = EXCLUDED.upload_state,\n uploaded_at = COALESCE(plg_scribe_segments.uploaded_at, EXCLUDED.uploaded_at),\n updated_at = now();\n\n -- Contadores derivados. \\`audio_duration_ms\\` soma só o que tem duração medida,\n -- então um segmento que ainda não voltou do STT não infla o total.\n UPDATE plg_scribe_sessions s SET\n segment_count = agg.total,\n uploaded_segment_count = agg.uploaded,\n transcribed_segment_count = agg.transcribed,\n audio_duration_ms = agg.audio_ms,\n transcript_chars = agg.chars,\n updated_at = now()\n FROM (\n SELECT\n count(*)::int AS total,\n count(*) FILTER (WHERE upload_state = 'uploaded')::int AS uploaded,\n count(*) FILTER (WHERE stt_state = 'done')::int AS transcribed,\n COALESCE(sum(duration_ms) FILTER (WHERE NOT gap), 0)::int AS audio_ms,\n COALESCE(sum(length(text)), 0)::int AS chars\n FROM plg_scribe_segments WHERE session_id = v_session_id\n ) agg\n WHERE s.id = v_session_id;\n\n SELECT to_jsonb(x) INTO v_result FROM (\n SELECT s.segment_count, s.uploaded_segment_count,\n s.transcribed_segment_count, s.audio_duration_ms, s.status\n FROM plg_scribe_sessions s WHERE s.id = v_session_id\n ) x;\n\n RETURN v_result;\nEND;\n$function$;\n\nCOMMENT ON FUNCTION public.scribe_record_segment(jsonb) IS\n 'Idempotent upsert of one audio segment + recomputation of the parent session counters from the segments table. Called by the browser upload pump after each successful Storage write.';\n\nGRANT EXECUTE ON FUNCTION public.scribe_record_segment(jsonb) TO authenticated;\n\n-- ---------------------------------------------------------------------------\n-- 6. o bucket\n-- ---------------------------------------------------------------------------\n-- PRIVADO, sem discussão: é a voz de uma pessoa falando da própria saúde. URL\n-- pública seria um endereço eterno para isso. O backoffice lê por URL assinada\n-- de curta duração; a edge function de transcrição lê com service key.\n--\n-- 5 MB por SEGMENTO é folgado por design: 30 s de Opus mono a 24 kbps dá ~90 KB.\n-- O limite existe para barrar um cliente com bug mandando um arquivo inteiro,\n-- não para apertar o caso normal.\nINSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)\nVALUES (\n 'scribe-audio',\n 'scribe-audio',\n false,\n 5242880,\n ARRAY['audio/webm','audio/ogg','audio/mp4','audio/mpeg','audio/m4a','audio/x-m4a']\n)\nON CONFLICT (id) DO UPDATE SET\n public = EXCLUDED.public,\n file_size_limit = EXCLUDED.file_size_limit,\n allowed_mime_types = EXCLUDED.allowed_mime_types;\n\n-- ---------------------------------------------------------------------------\n-- 7. o caminho é válido?\n-- ---------------------------------------------------------------------------\n-- Gramática (o SDK constrói, o banco confere):\n--\n-- {tenant_id}/{yyyy}/{mm}/{session_id}/{seg_index:05d}.{ext}\n-- 1 2 3 4 5\n--\n-- 1 tenant_id chave do isolamento — toda policy pendura aqui\n-- 2,3 yyyy/mm varredura de retenção por prefixo (LGPD), pasta listável\n-- 4 session_id uuid do cliente: agrupa a sessão e é imprevisível\n-- 5 seg_index zero-padded, então list() já volta em ordem cronológica\n--\n-- SECURITY DEFINER porque o EXISTS olha \\`tenants\\`, que tem RLS.\nCREATE OR REPLACE FUNCTION public.scribe_audio_path_ok(p_name text)\nRETURNS boolean\nLANGUAGE sql\nSTABLE\nSECURITY DEFINER\nSET search_path TO 'public'\nAS $function$\n SELECT\n p_name IS NOT NULL\n AND length(p_name) <= 200\n AND p_name ~ ('^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'\n || '/[0-9]{4}/[0-9]{2}'\n || '/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'\n || '/[0-9]{5}\\\\.(webm|ogg|m4a|mp3)$')\n -- comparação em texto (nunca \\`::uuid\\`): entrada malformada devolve falso em\n -- vez de derrubar a policy com erro de cast.\n AND EXISTS (\n SELECT 1 FROM public.tenants t WHERE t.id::text = split_part(p_name, '/', 1)\n );\n$function$;\n\nCOMMENT ON FUNCTION public.scribe_audio_path_ok(text) IS\n 'True when a storage object name follows the scribe-audio grammar {tenant}/{yyyy}/{mm}/{session}/{segIndex}.{ext} and segment 1 is a real tenant. Used by the INSERT policy on that bucket.';\n\nGRANT EXECUTE ON FUNCTION public.scribe_audio_path_ok(text) TO authenticated;\n\n-- ---------------------------------------------------------------------------\n-- 8. políticas do bucket\n-- ---------------------------------------------------------------------------\nDROP POLICY IF EXISTS plg_scribe_audio_insert ON storage.objects;\nDROP POLICY IF EXISTS plg_scribe_audio_read ON storage.objects;\nDROP POLICY IF EXISTS plg_scribe_audio_delete ON storage.objects;\n\n-- Escrever: só logado (diferente do bucket do CRM, que aceita visitante anônimo\n-- — aqui quem grava é sempre um profissional autenticado), dentro da gramática\n-- e da própria pasta.\n--\n-- NÃO existe policy de UPDATE, e isso é a trava mais importante das três: sem\n-- ela o \\`upsert\\` falha, e um caminho já gravado NUNCA pode ser trocado por\n-- outro conteúdo. É o que torna o áudio uma testemunha e não um rascunho.\nCREATE POLICY plg_scribe_audio_insert ON storage.objects\n FOR INSERT TO authenticated\n WITH CHECK (\n bucket_id = 'scribe-audio'\n AND public.scribe_audio_path_ok(name)\n AND (storage.foldername(name))[1] IN (SELECT public.user_tenant_ids()::text)\n );\n\nCREATE POLICY plg_scribe_audio_read ON storage.objects\n FOR SELECT TO authenticated\n USING (\n bucket_id = 'scribe-audio'\n AND (storage.foldername(name))[1] IN (SELECT public.user_tenant_ids()::text)\n );\n\n-- Apagar: mesma regra da leitura. É o que torna \"apagar o áudio desta sessão\"\n-- executável por quem é dono dele — o direito de eliminação do art. 18 VI não\n-- vale nada se ninguém no tenant consegue exercê-lo.\nCREATE POLICY plg_scribe_audio_delete ON storage.objects\n FOR DELETE TO authenticated\n USING (\n bucket_id = 'scribe-audio'\n AND (storage.foldername(name))[1] IN (SELECT public.user_tenant_ids()::text)\n );\n`\n\nexport const MIGRATIONS: Array<{ id: string; sql: string }> = [\n { id: \"001_scribe_base\", sql: MIGRATION_001_SCRIBE_BASE },\n]\n","import { getSupabaseClientOptional } from '@fayz-ai/core'\nimport { registerTimelineSource, type TimelineItem } from '@fayz-ai/admin'\n\nconst STATUS_LABELS: Record<string, string> = {\n recording: 'Gravando',\n paused: 'Pausado',\n interrupted: 'Interrompido',\n uploading: 'Enviando',\n transcribing: 'Transcrevendo',\n ready: 'Pronto',\n generating: 'Rascunho',\n completed: 'Concluído',\n abandoned: 'Descartado',\n failed: 'Falhou',\n}\n\nfunction minutes(ms: unknown): string | undefined {\n const value = Number(ms)\n if (!Number.isFinite(value) || value <= 0) return undefined\n const total = Math.round(value / 60000)\n return total < 1 ? 'menos de 1 min' : `${total} min de áudio`\n}\n\n/**\n * Gravações de atendimento na linha do tempo da pessoa.\n *\n * `started_at` e não `created_at`: o que a pessoa procura é o dia do\n * atendimento, e uma gravação recuperada de uma sessão interrompida pode ter\n * sido criada bem depois de ter acontecido.\n */\nexport function registerScribeTimelineSource(): void {\n registerTimelineSource({\n id: 'recordings',\n label: 'Gravações',\n icon: 'Mic',\n // `magic` é o tom que o SDK já reserva para o que a IA produz.\n tone: 'magic',\n async fetch(args): Promise<TimelineItem[]> {\n const db = getSupabaseClientOptional() as any\n if (!db) return []\n\n let query = db\n .from('plg_scribe_sessions')\n .select('id, status, started_at, audio_duration_ms, transcript_chars')\n .eq('tenant_id', args.tenantId)\n .eq('subject_id', args.personId)\n .order('started_at', { ascending: false })\n .limit(args.limit)\n\n if (args.before) query = query.lt('started_at', args.before)\n\n const { data, error } = await query\n if (error) return []\n\n return (data ?? []).map((row: any) => ({\n id: `scribe:${row.id}`,\n sourceId: 'recordings',\n occurredAt: row.started_at,\n title: 'Atendimento gravado',\n description: minutes(row.audio_duration_ms),\n badge: STATUS_LABELS[row.status] ?? row.status ?? undefined,\n icon: 'Mic',\n targetTab: 'scribe',\n }))\n },\n })\n}\n","import * as React from 'react'\nimport { Mic } from 'lucide-react'\nimport {\n Button,\n Checkbox,\n Modal,\n ModalBody,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalTitle,\n} from '@fayz-ai/ui/primitives'\nimport { getActiveDeviceId, setPreferredDeviceId, switchInputDevice } from '../runtime'\n\n// Pick the microphone — with a level meter on EVERY row.\n//\n// A consulting room has four audio inputs and the browser picks one alone. A\n// list of names does not help: \"MacBook Pro Microphone\" and \"BlackHole 16ch\"\n// are equally plausible. Seeing which one is picking up sound does.\n//\n// The cost is one analysis stream per device while the dialog is open —\n// acceptable because it is ephemeral and closed on unmount.\n\nconst BARS = 8\n\ninterface DeviceRow {\n deviceId: string\n label: string\n}\n\n/** One meter per device, each on its own analysis stream. */\nfunction DeviceLevel({ deviceId, onSignal }: { deviceId: string; onSignal: (has: boolean) => void }) {\n const barsRef = React.useRef<Array<HTMLSpanElement | null>>([])\n\n React.useEffect(() => {\n let cancelled = false\n let stream: MediaStream | null = null\n let context: AudioContext | null = null\n let frame = 0\n let sawSignal = false\n\n const AudioCtor: typeof AudioContext | undefined =\n typeof window === 'undefined'\n ? undefined\n : window.AudioContext ??\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext\n if (!AudioCtor) return\n\n void navigator.mediaDevices\n .getUserMedia({ audio: { deviceId: { exact: deviceId } } })\n .then((granted) => {\n if (cancelled) {\n granted.getTracks().forEach((t) => t.stop())\n return\n }\n stream = granted\n context = new AudioCtor()\n const analyser = context.createAnalyser()\n analyser.fftSize = 128\n analyser.smoothingTimeConstant = 0.7\n context.createMediaStreamSource(granted).connect(analyser)\n const spectrum = new Uint8Array(analyser.frequencyBinCount)\n\n const tick = () => {\n if (cancelled) return\n analyser.getByteFrequencyData(spectrum)\n let sum = 0\n for (const v of spectrum) sum += v\n const level = sum / spectrum.length / 255\n if (!sawSignal && level > 0.02) {\n sawSignal = true\n onSignal(true)\n }\n for (let i = 0; i < BARS; i++) {\n const bar = barsRef.current[i]\n if (!bar) continue\n const lit = level * BARS > i\n bar.style.opacity = lit ? '1' : '0.25'\n }\n frame = requestAnimationFrame(tick)\n }\n frame = requestAnimationFrame(tick)\n })\n .catch(() => {\n /* device busy or denied — the row renders without a meter */\n })\n\n return () => {\n cancelled = true\n if (frame) cancelAnimationFrame(frame)\n stream?.getTracks().forEach((t) => t.stop())\n void context?.close().catch(() => {})\n }\n }, [deviceId, onSignal])\n\n return (\n <div className=\"flex items-center gap-[2px]\" aria-hidden>\n {Array.from({ length: BARS }, (_, i) => (\n <span\n key={i}\n ref={(el) => {\n barsRef.current[i] = el\n }}\n className=\"h-3.5 w-[3px] rounded-sm bg-current opacity-25 transition-opacity duration-75\"\n />\n ))}\n </div>\n )\n}\n\nexport interface ScribeDevicePickerProps {\n open: boolean\n onOpenChange: (open: boolean) => void\n}\n\nexport function ScribeDevicePicker({ open, onOpenChange }: ScribeDevicePickerProps) {\n const [devices, setDevices] = React.useState<DeviceRow[]>([])\n const [selected, setSelected] = React.useState<string | null>(null)\n const [remember, setRemember] = React.useState(true)\n const [signals, setSignals] = React.useState<Record<string, boolean>>({})\n const [busy, setBusy] = React.useState(false)\n\n React.useEffect(() => {\n if (!open) return\n let cancelled = false\n const load = async () => {\n try {\n // Labels only exist once permission is granted; without it the list\n // comes back nameless and helps nobody.\n const probe = await navigator.mediaDevices.getUserMedia({ audio: true })\n probe.getTracks().forEach((t) => t.stop())\n const all = await navigator.mediaDevices.enumerateDevices()\n if (cancelled) return\n const inputs = all\n .filter((d) => d.kind === 'audioinput' && d.deviceId)\n .map((d, i) => ({ deviceId: d.deviceId, label: d.label || `Microfone ${i + 1}` }))\n setDevices(inputs)\n setSelected(getActiveDeviceId() ?? inputs[0]?.deviceId ?? null)\n } catch {\n setDevices([])\n }\n }\n void load()\n return () => {\n cancelled = true\n }\n }, [open])\n\n const markSignal = React.useCallback((deviceId: string) => {\n setSignals((prev) => (prev[deviceId] ? prev : { ...prev, [deviceId]: true }))\n }, [])\n\n const confirm = async () => {\n if (!selected) return\n setBusy(true)\n try {\n await switchInputDevice(selected)\n setPreferredDeviceId(remember ? selected : null)\n onOpenChange(false)\n } finally {\n setBusy(false)\n }\n }\n\n return (\n <Modal open={open} onOpenChange={onOpenChange}>\n <ModalContent className=\"max-w-md\">\n <ModalHeader>\n <ModalTitle className=\"flex items-center gap-2\">\n <Mic className=\"h-4 w-4\" />\n Selecionar microfone\n </ModalTitle>\n </ModalHeader>\n <ModalBody className=\"space-y-3\">\n <p className=\"text-xs text-muted-foreground\">\n {devices.length} dispositivo{devices.length === 1 ? '' : 's'} encontrado\n {devices.length === 1 ? '' : 's'}\n </p>\n\n <div className=\"max-h-64 space-y-1.5 overflow-y-auto\">\n {devices.map((device) => (\n <button\n key={device.deviceId}\n type=\"button\"\n onClick={() => setSelected(device.deviceId)}\n className={`flex w-full items-center gap-3 rounded-lg border p-2.5 text-left transition-colors ${\n selected === device.deviceId\n ? 'border-primary bg-primary/5'\n : 'border-border hover:bg-muted/50'\n }`}\n >\n <Mic\n className={`h-4 w-4 shrink-0 ${signals[device.deviceId] ? 'text-primary' : 'text-muted-foreground'}`}\n />\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate text-[13px] font-medium text-foreground\">{device.label}</span>\n <span className=\"block text-[11px] text-muted-foreground\">\n {signals[device.deviceId] ? 'Captando áudio' : 'Sem áudio detectado'}\n </span>\n </span>\n {open && (\n <span className=\"shrink-0 text-primary\">\n <DeviceLevel deviceId={device.deviceId} onSignal={() => markSignal(device.deviceId)} />\n </span>\n )}\n </button>\n ))}\n {devices.length === 0 && (\n <p className=\"py-6 text-center text-xs text-muted-foreground\">\n Nenhum microfone disponível. Verifique a permissão do navegador.\n </p>\n )}\n </div>\n\n <label className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <Checkbox checked={remember} onChange={setRemember} />\n Lembrar minha escolha\n </label>\n </ModalBody>\n\n <ModalFooter>\n <Button variant=\"outline\" size=\"sm\" onClick={() => onOpenChange(false)}>\n Cancelar\n </Button>\n <Button size=\"sm\" disabled={!selected || busy} onClick={() => void confirm()}>\n Confirmar\n </Button>\n </ModalFooter>\n </ModalContent>\n </Modal>\n )\n}\n","import * as React from 'react'\nimport { Mic, Pause, Play, Settings2, Square } from 'lucide-react'\nimport { Button } from '@fayz-ai/ui/primitives'\nimport { useScribeStore, formatElapsed } from '../store'\nimport { endSession, getSnapshot, pauseSession, resumeSession } from '../runtime'\nimport { useLiveTranscript } from '../lib/live-transcript'\nimport { TranscriptPanel } from './TranscriptPanel'\nimport { ScribeDevicePicker } from './ScribeDevicePicker'\nimport type { ScribeLabels } from '../types'\n\n// The encounter as it happens: controls plus the transcript growing.\n//\n// ON \"REAL TIME\": the pipeline transcribes 30s CLIPS after upload, not word by\n// word, so text arrives in blocks. Faking a stream — letters appearing out of a\n// block that already arrived whole — would be an animation lying about the\n// system's latency. Real word-by-word needs a streaming STT transport.\n\nexport interface ScribeLivePanelProps {\n labels: ScribeLabels\n onOpenSession?: (sessionId: string) => void\n}\n\nexport function ScribeLivePanel({ labels, onOpenSession }: ScribeLivePanelProps) {\n const state = useScribeStore((s) => s.state)\n const sessionId = useScribeStore((s) => s.sessionId)\n const subjectName = useScribeStore((s) => s.subjectName)\n // From the shared watcher: the agent needs the transcript with this screen\n // closed, so the fetching cannot live here.\n const segments = useLiveTranscript((s) => s.segments)\n const [busy, setBusy] = React.useState(false)\n const [pickerOpen, setPickerOpen] = React.useState(false)\n const [, force] = React.useReducer((n: number) => n + 1, 0)\n\n const live = state === 'recording' || state === 'paused' || state === 'interrupted'\n\n // Sample faster than the unit shown, and read elapsed fresh.\n React.useEffect(() => {\n if (state !== 'recording') return\n const timer = setInterval(() => {\n if (document.visibilityState === 'visible') force()\n }, 250)\n return () => clearInterval(timer)\n }, [state])\n\n const elapsed = live ? getSnapshot().elapsedMs : 0\n const transcribed = segments.filter((s) => s.text?.trim()).length\n const pending = live || segments.some((s) => !s.gap && !s.text)\n\n const toggle = async () => {\n setBusy(true)\n try {\n if (state === 'recording') await pauseSession()\n else await resumeSession()\n } finally {\n setBusy(false)\n }\n }\n\n const finish = async () => {\n setBusy(true)\n try {\n const id = await endSession()\n if (id) onOpenSession?.(id)\n } finally {\n setBusy(false)\n }\n }\n\n if (!sessionId) {\n return (\n <div className=\"flex flex-1 flex-col items-center justify-center gap-2 p-6 text-center text-sm text-muted-foreground\">\n <Mic className=\"h-5 w-5\" />\n <p className=\"font-medium text-foreground\">{labels.sessionPlural}</p>\n <p className=\"text-xs\">\n Inicie um {labels.sessionSingular.toLowerCase()} pela ficha do {labels.subject.toLowerCase()} ou pelo\n botão no chat. A transcrição aparece aqui enquanto você atende.\n </p>\n </div>\n )\n }\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <div className=\"flex items-center gap-2 border-b border-border/40 px-3 py-2\">\n <span className=\"relative inline-flex h-2 w-2 shrink-0\">\n {state === 'recording' && (\n <span className=\"absolute inline-flex h-full w-full animate-ping rounded-full bg-destructive opacity-60\" />\n )}\n <span className=\"relative inline-flex h-2 w-2 rounded-full bg-destructive\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-[12.5px] font-medium text-foreground\">\n {subjectName ?? labels.sessionSingular}\n </p>\n <p className=\"text-[11px] tabular-nums text-muted-foreground\">\n {formatElapsed(elapsed)}\n {transcribed > 0 && ` · ${transcribed} trecho${transcribed > 1 ? 's' : ''}`}\n </p>\n </div>\n {live && (\n <>\n <Button variant=\"ghost\" size=\"sm\" className=\"h-7 px-2\" disabled={busy} onClick={() => void toggle()}>\n {state === 'recording' ? <Pause className=\"h-3.5 w-3.5\" /> : <Play className=\"h-3.5 w-3.5\" />}\n <span className=\"ml-1 text-[11px]\">{state === 'recording' ? labels.pause : labels.resume}</span>\n </Button>\n <Button variant=\"ghost\" size=\"sm\" className=\"h-7 px-2\" disabled={busy} onClick={() => void finish()}>\n <Square className=\"h-3.5 w-3.5\" />\n <span className=\"ml-1 text-[11px]\">{labels.finish}</span>\n </Button>\n {/* Swapping mid-session: finding out later is finding out when the\n audio is already gone. */}\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-7 w-7 p-0\"\n title=\"Selecionar microfone\"\n aria-label=\"Selecionar microfone\"\n onClick={() => setPickerOpen(true)}\n >\n <Settings2 className=\"h-3.5 w-3.5\" />\n </Button>\n </>\n )}\n </div>\n\n <div className=\"min-h-0 flex-1 overflow-y-auto p-3\">\n <TranscriptPanel\n segments={segments}\n pending={pending}\n emptyLabel={`Nenhuma transcrição ainda neste ${labels.sessionSingular.toLowerCase()}.`}\n />\n </div>\n\n <ScribeDevicePicker open={pickerOpen} onOpenChange={setPickerOpen} />\n </div>\n )\n}\n","// ---------------------------------------------------------------------------\n// Scribe vocabulary — deliberately vertical-neutral.\n//\n// A \"session\" is a recorded encounter: a salon appointment, a clinic visit, a\n// meeting at a law firm. The \"subject\" is the person it is about (a `people`\n// row), the \"operator\" runs it. If a type here needs a clinical word, the\n// abstraction failed — the vertical supplies those through `labels`.\n// ---------------------------------------------------------------------------\n\n/**\n * SERVER-side session state. Advances only on durable facts: a segment that\n * reached Storage, a transcript that came back. Client state, which can die at\n * any moment, lives separately in `CaptureState`.\n */\nexport type SessionStatus =\n | 'recording'\n | 'paused'\n | 'interrupted'\n | 'uploading'\n | 'transcribing'\n | 'ready'\n | 'generating'\n | 'completed'\n | 'abandoned'\n | 'failed'\n\n/** States where the session is still someone's — used by the partial index and recovery. */\nexport const OPEN_SESSION_STATUSES: SessionStatus[] = [\n 'recording', 'paused', 'interrupted', 'uploading', 'transcribing', 'ready', 'generating',\n]\n\n/**\n * CLIENT-side capture state. `interrupted` is the honest state between \"the mic\n * is gone\" and \"the user decided what to do\" — collapsing it into `paused` would\n * lose the distinction between intent and accident, which the transcript needs\n * in order to record the hole.\n */\nexport type CaptureState =\n | 'idle'\n | 'consent'\n | 'requesting'\n | 'recording'\n | 'paused'\n | 'interrupted'\n | 'stopping'\n | 'error'\n\n/** What the encounter is attached to. `standalone` records first, attaches later. */\nexport type SessionContextKind = 'person' | 'appointment' | 'standalone'\n\n/**\n * Legal basis recorded at capture time. Consent is what separates the recording\n * from a problem, and \"when\" and \"how\" is what gets proven later.\n * `implied_contract` covers recording as part of a contracted service.\n */\nexport type ConsentMode = 'verbal' | 'written' | 'implied_contract'\n\nexport type UploadState = 'pending' | 'uploading' | 'uploaded' | 'failed' | 'missing'\nexport type SttState = 'pending' | 'running' | 'done' | 'failed' | 'skipped'\nexport type GenerationStatus = 'pending' | 'running' | 'ready' | 'failed' | 'committed' | 'discarded'\n\n// ---------------------------------------------------------------------------\n// Server rows\n// ---------------------------------------------------------------------------\n\nexport interface ScribeSession {\n id: string\n tenantId: string\n subjectId?: string\n subjectName?: string\n contextKind: SessionContextKind\n appointmentId?: string\n ownerUserId: string\n status: SessionStatus\n locale: string\n mimeType?: string\n startedAt: string\n endedAt?: string\n /** Wall clock reported by the client. Moves the pill; not a record. */\n wallDurationMs: number\n /** SUM(segments.duration_ms) — the only duration that counts for records, limits and billing. */\n audioDurationMs: number\n segmentCount: number\n uploadedSegmentCount: number\n transcribedSegmentCount: number\n transcriptChars: number\n consentAt?: string\n consentMode?: ConsentMode\n retentionUntil?: string\n audioDeletedAt?: string\n error?: string\n metadata: Record<string, unknown>\n}\n\n/** A word, offset RELATIVE to the start of its segment. */\nexport interface TranscriptWord {\n /** word */\n w: string\n /** start ms, relative to the segment */\n s: number\n /** end ms, relative to the segment */\n e: number\n /** confidence 0..1 */\n c?: number\n /** speaker index, when diarized */\n sp?: number\n}\n\nexport interface ScribeSegment {\n sessionId: string\n segIndex: number\n tenantId: string\n storagePath?: string\n bytes?: number\n /** Offset of this segment from the start of the session. */\n startOffsetMs: number\n /** From the STT response, not a JS clock. */\n durationMs?: number\n /** Assembled from pieces of a segment interrupted mid-way. */\n partial: boolean\n /**\n * SYNTHETIC row marking a hole (sleep, mic lost, permission revoked). Carries\n * no audio. It exists so the transcript admits the hole instead of silently\n * splicing two moments together — a medico-legal requirement, not polish.\n */\n gap: boolean\n uploadState: UploadState\n uploadedAt?: string\n sttState: SttState\n sttProvider?: string\n sttAttempts: number\n sttError?: string\n text?: string\n words?: TranscriptWord[]\n confidence?: number\n}\n\nexport interface ScribeGeneration {\n id: string\n tenantId: string\n sessionId: string\n /** Tenant template row. NULL when the generation came from a code preset. */\n templateId?: string\n /** Preset key (`beauty.anamnesis.v1`) when there is no row. */\n templateKey?: string\n tabOrder: number\n title?: string\n status: GenerationStatus\n /** Model output. Written once, NEVER mutated. */\n markdown?: string\n /** The human's working copy. The difference between the two is what proves review. */\n markdownEdited?: string\n /** Stage-1 extraction (two-stage). Reserved; the column exists from v0 on purpose. */\n facts?: Record<string, unknown>\n model?: string\n promptVersion?: string\n inputTokens?: number\n outputTokens?: number\n error?: string\n documentId?: string\n createdBy?: string\n createdAt: string\n}\n\n// ---------------------------------------------------------------------------\n// Narrative templates\n//\n// A narrative template describes PROSE SECTIONS, not form fields — the exact\n// point where a vertical expresses itself. The same capture, transcription and\n// generation engines serve an anamnesis and a legal draft; only the section list\n// changes. Stored in `plg_forms_templates.schema`, discriminated by `kind`.\n// ---------------------------------------------------------------------------\n\nexport type SectionShape = 'prose' | 'bullets' | 'keyvalue'\n\nexport interface NarrativeSectionDef {\n /** Stable machine key. NEVER translated — it outlives heading renames. */\n id: string\n /** Becomes `## <heading>` in the markdown. Translatable. */\n heading: string\n /**\n * The one per-section prompt the model sees. Write it as an instruction to a\n * junior: what belongs here, what does not, in which voice.\n */\n guidance: string\n /**\n * Controls the SHAPE of the markdown. `keyvalue` exists so structured data\n * comes out as `- **Label:** value` rather than a pipe table — the SDK\n * renderer has no table, and a broken A4 prints garbage.\n */\n shape?: SectionShape\n required?: boolean\n /** Drops the whole section, heading included, when the transcript said nothing. */\n omitWhenEmpty?: boolean\n maxWords?: number\n /** Fact keys this section consumes in two-stage mode. Reserved. */\n facts?: string[]\n}\n\n/** Where each header field is pulled from. Resolved from DATA, never asked of the model. */\nexport type NarrativeHeaderSource =\n | 'subject.name'\n | 'subject.documentNumber'\n | 'subject.birthDate'\n | 'session.startedAt'\n | 'session.durationMinutes'\n | 'operator.name'\n | (string & {})\n\nexport interface NarrativeSchema {\n kind: 'narrative'\n sections: NarrativeSectionDef[]\n /** Tone, person, tense, jargon level. Injected ahead of the sections. */\n style?: string\n /** OUTPUT language, independent of the transcript's. */\n outputLocale?: string\n /** Block above the first section, resolved from data. */\n header?: { fields: Array<{ label: string; source: NarrativeHeaderSource }> }\n /** Footer, e.g. 'AI-generated from a recording, reviewed by the professional.' */\n disclaimer?: string\n /**\n * Things the model must report as \"not stated\" instead of inferring, e.g.\n * ['diagnosis', 'dosage', 'ICD']. The line between summarising and inventing.\n */\n neverInfer?: string[]\n}\n\nexport function isNarrativeSchema(schema: unknown): schema is NarrativeSchema {\n return !!schema && typeof schema === 'object' && (schema as NarrativeSchema).kind === 'narrative'\n}\n\n/**\n * A system template, defined in CODE rather than in a SQL seed. Fixing a prompt\n * becomes a release instead of a migration editing a row the user may have\n * changed, and the default does not eat the tenant's template quota. Customising\n * forks it, and the fork is the row.\n */\nexport interface NarrativeTemplatePreset {\n /** Versioned in the key itself: `beauty.anamnesis.v1`. */\n key: string\n name: string\n description?: string\n /** plugin-forms category: anamnesis | evolution | report | contract | general. */\n category: string\n specialty?: string\n schema: NarrativeSchema\n}\n\n// ---------------------------------------------------------------------------\n// Plugin configuration — a vertical's whole surface\n// ---------------------------------------------------------------------------\n\nexport interface ScribeLabels {\n /** \"Appointment\" / \"Visit\" / \"Meeting\" */\n sessionSingular: string\n sessionPlural: string\n /** \"Start appointment\" */\n start: string\n pause: string\n resume: string\n finish: string\n discard: string\n /** \"Generate document\" / \"Generate draft\" */\n generate: string\n /** \"Client\" / \"Patient\" */\n subject: string\n transcript: string\n settingsTitle: string\n}\n\n/**\n * Where the model credential lives. See `lib/transport.ts` for why there are\n * exactly these two values and no third.\n */\nexport type ScribeTransportKind = 'fayz' | 'edge'\n\nexport interface ScribeSttConfig {\n /**\n * Defaults to `'fayz'`: the platform API, the same broker the app assistant\n * already uses — no secret and no deploy on the app side. `'edge'` keeps the\n * own-edge-function path, for anyone running off-platform or needing a\n * provider the broker does not offer.\n */\n transport?: ScribeTransportKind\n /**\n * Provider behind a one-method seam — swapping must not be a refactor. The\n * platform broker speaks OpenAI; Deepgram requires `transport: 'edge'`.\n */\n provider: 'deepgram' | 'openai'\n /**\n * Explicit, with no hidden default, because the choice shows: `whisper-1`\n * returns duration and per-word offsets, while the `gpt-4o-*-transcribe`\n * models are usually better at pt-BR but return text only — and then the\n * authoritative duration has to come from somewhere else.\n */\n model?: string\n locale: string\n /** Only Deepgram diarizes today; ignored on the `fayz` transport. */\n diarize?: boolean\n /**\n * Transcription edge function name. REQUIRED when `transport: 'edge'`: with no\n * transport configured the plugin refuses to boot, because the only remaining\n * fallback would be the browser's native recognizer, which ships the session\n * audio to an undeclared third party. See the invariant in `createScribePlugin`.\n */\n endpoint?: string\n}\n\nexport interface ScribeRetentionConfig {\n /** Days until the audio may be deleted. Transcript and document survive. */\n audioDays: number\n /** When true, no byte is recorded before the consent gate. */\n requireConsent: boolean\n defaultConsentMode: ConsentMode\n}\n\nexport interface ScribeLimitsConfig {\n minutesMonth?: string\n documentsMonth?: string\n}\n\nexport interface ScribePluginOptions {\n /** `kind` of the `people` row allowed as subject ('customer', 'client', …). */\n subjectKind?: string\n contextEntities?: string[]\n /**\n * Detail pages that get the \"start session\" button in their header.\n * Defaults to the person-like entity names the pools use today.\n */\n subjectEntities?: string[]\n /**\n * Activate for every tenant of this app unless a binding says otherwise.\n *\n * Ships OFF: recording audio is not something a generic app should acquire by\n * accident. But leaving it off in a vertical built around the encounter makes\n * it unreachable — the only screen that could enable a plugin is its own\n * settings tab, which does not render until it is enabled.\n */\n enabledByDefault?: boolean\n labels?: Partial<ScribeLabels>\n /** The vertical's default templates — the ONLY new code a vertical writes. */\n templatePresets?: NarrativeTemplatePreset[]\n stt: ScribeSttConfig\n retention?: Partial<ScribeRetentionConfig>\n limits?: ScribeLimitsConfig\n /** Generation edge function name. */\n generateEndpoint?: string\n scope?: 'universal' | 'vertical'\n verticalId?: string\n}\n","import React from 'react'\nimport type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'\nimport { registerTranslations } from '@fayz-ai/core'\nimport { scribeLocales } from './locales'\nimport { initScribeRuntime } from './runtime'\nimport { wireScribeStore } from './store'\nimport { ScribeShellMount, requestScribeStart } from './components/ScribeShellMount'\nimport { ScribeSessionPage } from './components/ScribeSessionPage'\nimport { ScribeHeaderAction } from './components/ScribeHeaderAction'\nimport { MIGRATION_001_SCRIBE_BASE } from './migrations'\nimport { assertSttConfig } from './lib/config'\nimport { registerScribeTimelineSource } from './lib/timeline-source'\nimport type {\n ScribeLabels,\n ScribePluginOptions,\n ScribeRetentionConfig,\n} from './types'\n\n// The plugin's vocabulary names what IT owns: a recording. \"Atendimento\" is the\n// encounter — what the agenda books, what the client pays for — and belongs to\n// the app. While scribe called itself that, its tab competed with the record's\n// own encounter tab and nobody could tell them apart.\nconst DEFAULT_LABELS: ScribeLabels = {\n sessionSingular: 'Gravação',\n sessionPlural: 'Gravações',\n start: 'Gravar atendimento',\n pause: 'Pausar',\n resume: 'Retomar',\n finish: 'Finalizar',\n discard: 'Descartar',\n generate: 'Gerar documento',\n subject: 'Cliente',\n transcript: 'Transcrição',\n settingsTitle: 'Gravação de atendimentos',\n}\n\nconst DEFAULT_RETENTION: ScribeRetentionConfig = {\n audioDays: 90,\n requireConsent: true,\n defaultConsentMode: 'verbal',\n}\n\nexport function createScribePlugin(options: ScribePluginOptions): PluginManifest {\n // -------------------------------------------------------------------------\n // O invariante que justifica lançar na construção.\n //\n // Sem um transporte de STT próprio, a única alternativa a mão seria o\n // reconhecedor nativo do browser — que envia o áudio do microfone para\n // servidores do Google. Para um atendimento isso significa um operador não\n // declarado processando dado sensível, sem DPA, sem base documentada de\n // transferência internacional, e sem como honrar um pedido de eliminação:\n // não dá para pedir a deleção do que você nunca soube que foi recebido.\n //\n // Falhar aqui, alto e no boot, é infinitamente melhor que degradar em\n // silêncio para o caminho que ninguém escolheu.\n //\n // `transport: 'fayz'` (o default) satisfaz o invariante sozinho: o broker da\n // plataforma É o endpoint próprio. `transport: 'edge'` continua exigindo o\n // nome da função.\n // -------------------------------------------------------------------------\n assertSttConfig(options?.stt)\n\n const labels: ScribeLabels = { ...DEFAULT_LABELS, ...options.labels }\n const retention: ScribeRetentionConfig = { ...DEFAULT_RETENTION, ...options.retention }\n const presets = options.templatePresets ?? []\n const generateEndpoint = options.generateEndpoint ?? 'scribe-generate'\n const locale = options.stt.locale ?? 'pt-BR'\n\n registerTranslations(scribeLocales)\n\n // Gravação de atendimento passa a aparecer na linha do tempo do cliente.\n registerScribeTimelineSource()\n\n // O runtime sobe AQUI, na construção do manifesto — não num efeito de\n // componente. É o que faz a recuperação de uma sessão de ontem começar antes\n // de qualquer tela existir.\n initScribeRuntime({ retention })\n wireScribeStore()\n\n const openSession = (sessionId: string) => {\n window.location.hash = `/scribe/${sessionId}`\n }\n\n const ShellMount: React.FC = () =>\n React.createElement(ScribeShellMount, { labels, retention, stt: options.stt, locale, onOpenSession: openSession })\n ShellMount.displayName = 'ScribeShellMount'\n\n const SessionRoute: React.FC = () => {\n const hash = typeof window !== 'undefined' ? window.location.hash : ''\n const sessionId = hash.split('/scribe/')[1]?.split('/')[0] ?? ''\n if (!sessionId) return null\n return React.createElement(ScribeSessionPage, {\n sessionId,\n labels,\n presets,\n stt: options.stt,\n generateEndpoint,\n onBack: () => {\n window.location.hash = '/clients'\n },\n onOpenDocument: () => {\n window.location.hash = '/clients'\n },\n })\n }\n SessionRoute.displayName = 'ScribeSessionRoute'\n\n const HeaderAction: React.FC<Record<string, unknown>> = (props) =>\n React.createElement(ScribeHeaderAction, { ...(props as object), labels })\n HeaderAction.displayName = 'ScribeHeaderAction'\n\n // Which record types can be a session's subject. The plugin cannot know what\n // a vertical calls its people, so the vertical says.\n const subjectEntities = options.subjectEntities ?? ['clients', 'patients', 'people', 'persons', 'customers']\n\n return {\n id: 'scribe',\n name: labels.sessionPlural,\n icon: 'Mic',\n version: '0.1.0',\n scope: (options.scope ?? 'universal') as PluginScope,\n verticalId: options.verticalId as VerticalId | undefined,\n defaultEnabled: options.enabledByDefault ?? false,\n // O scribe EMITE documento mas não é dono do conceito. plugin-forms é a\n // engine de documento; este é uma engine de aquisição. A dependência aponta\n // nessa direção e não na inversa — senão todo app que só quer uma anamnese\n // impressa passaria a depender de bucket, duas edge functions e chave de\n // modelo.\n dependencies: ['custom_forms'],\n\n navigation: [],\n\n routes: [\n {\n path: '/scribe/*',\n component: SessionRoute as unknown as React.ComponentType<unknown>,\n },\n ],\n\n widgets: [\n {\n id: 'scribe-shell-mount',\n // A única zona global verificadamente renderizada (AdminShell.tsx:979-980)\n // e que fica FORA do switch de rota. Navegar não a desmonta.\n zone: 'shell.topbar.end',\n component: ShellMount as unknown as React.ComponentType<unknown>,\n order: -10,\n },\n // Next to Edit on the person record. From the plugin on purpose: it only\n // renders where the engine listening for `scribe:start` is also mounted,\n // so there is no version of it that does nothing.\n ...subjectEntities.map((entity) => ({\n id: `scribe-start-${entity}`,\n zone: `${entity}.detail.header.actions`,\n component: HeaderAction as unknown as React.ComponentType<unknown>,\n order: -10,\n })),\n ],\n\n declaredFeatures: [\n { id: 'scribe', label: labels.settingsTitle, actions: ['read', 'create', 'edit', 'delete'] },\n ],\n\n declaredLimits: [\n // Cota entra no dia um, não depois: a 1000 atendimentos/mês o STT bruto\n // já é a maior linha de custo variável do produto.\n {\n key: options.limits?.minutesMonth ?? 'scribe_minutes_month',\n label: 'Minutos de gravação por mês',\n table: 'plg_scribe_sessions',\n period: 'month',\n },\n {\n key: options.limits?.documentsMonth ?? 'scribe_documents_month',\n label: 'Documentos gerados por mês',\n table: 'plg_scribe_generations',\n period: 'month',\n },\n ] as PluginManifest['declaredLimits'],\n\n aiTools: [\n {\n id: 'scribe.list-sessions',\n name: 'listScribeSessions',\n description: `Lista ${labels.sessionPlural.toLowerCase()} gravados, com data, duração e status.`,\n icon: 'Mic',\n mode: 'read' as const,\n category: labels.sessionPlural,\n permission: { feature: 'scribe', action: 'read' as const },\n parameters: {\n type: 'object' as const,\n properties: {\n subjectId: { type: 'string' as const, description: 'Filtra pelo id do titular' },\n status: { type: 'string' as const, description: 'Filtra pelo status da sessão' },\n },\n },\n suggestions: [\n { label: `Quais ${labels.sessionPlural.toLowerCase()} ainda não viraram documento?` },\n ],\n },\n ],\n\n migrations: [\n {\n id: 'scribe-001-base',\n version: '0.1.0',\n sql: MIGRATION_001_SCRIBE_BASE,\n description:\n 'Cria plg_scribe_sessions/_segments/_generations, o bucket privado scribe-audio com guard de caminho, e o RPC scribe_record_segment',\n },\n ],\n\n locales: scribeLocales,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Re-exports\n// ---------------------------------------------------------------------------\n\nexport { requestScribeStart, SCRIBE_START_EVENT } from './components/ScribeShellMount'\nexport { ScribeStartButton } from './components/ScribeRecordingPill'\nexport { ScribeLivePanel } from './components/ScribeLivePanel'\nexport { ScribeDevicePicker } from './components/ScribeDevicePicker'\nexport { ScribeSessionPage } from './components/ScribeSessionPage'\nexport { useScribeStore, formatElapsed } from './store'\nexport { PROMPT_VERSION, formatOffset, parseSections, renderTranscript } from './lib/prompt'\nexport {\n beginSession,\n endSession,\n pauseSession,\n resumeSession,\n discardSession,\n isCaptureSupported,\n} from './runtime'\nexport {\n fetchSession,\n fetchSegments,\n fetchGenerations,\n fetchSessionsForSubject,\n fetchOpenSessions,\n deleteSessionAudio,\n} from './data/supabase'\nexport type {\n ScribePluginOptions,\n ScribeLabels,\n ScribeSession,\n ScribeSegment,\n ScribeGeneration,\n NarrativeSchema,\n NarrativeSectionDef,\n NarrativeTemplatePreset,\n SessionStatus,\n CaptureState,\n ConsentMode,\n} from './types'\nexport { isNarrativeSchema } from './types'\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/locales/en.ts","../src/locales/pt-BR.ts","../src/locales/index.ts","../src/runtime/idb.ts","../src/runtime/capture.ts","../src/data/tables.ts","../src/runtime/pump.ts","../src/runtime/index.ts","../src/store.ts","../src/components/ScribeRecordingPill.tsx","../src/data/supabase.ts","../src/lib/config.ts","../src/lib/transport.ts","../src/lib/transcript-clean.ts","../src/lib/drain.ts","../src/lib/live-transcript.ts","../src/lib/start.ts","../src/components/ScribeAssistantBridge.tsx","../src/components/ScribeShellMount.tsx","../src/lib/prompt.ts","../src/components/TranscriptPanel.tsx","../src/components/DraftPanel.tsx","../src/lib/generate.ts","../src/components/ScribeSessionPage.tsx","../src/components/ScribeHeaderAction.tsx","../src/migrations/index.ts","../src/lib/timeline-source.ts","../src/components/ScribeDevicePicker.tsx","../src/components/ScribeLivePanel.tsx","../src/types.ts","../src/index.ts"],"names":["running","timer","getSupabaseClientOptional","orphans","React","elapsedMs","CONCURRENCY","client","create","React2","retention","live","React3","jsxs","jsx","Fragment","React4","toast","Loader2","Button","Badge","draining","useAgentSurface","Mic","React6","tick","load","React7","Pause","Play","Square"],"mappings":"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AAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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","file":"index.js","sourcesContent":["export const en: Record<string, string> = {\n 'scribe.nav.sessions': 'Sessions',\n 'scribe.settings.title': 'Session recording',\n 'scribe.action.start': 'Start session',\n 'scribe.action.pause': 'Pause',\n 'scribe.action.resume': 'Resume',\n 'scribe.action.finish': 'Finish',\n 'scribe.action.discard': 'Discard',\n 'scribe.action.generate': 'Generate document',\n 'scribe.transcript': 'Transcript',\n 'scribe.recovery.title': 'Recording interrupted',\n 'scribe.consent.title': 'Start recording',\n}\n","export const ptBR: Record<string, string> = {\n 'scribe.nav.sessions': 'Atendimentos',\n 'scribe.settings.title': 'Gravação de atendimentos',\n 'scribe.action.start': 'Iniciar atendimento',\n 'scribe.action.pause': 'Pausar',\n 'scribe.action.resume': 'Retomar',\n 'scribe.action.finish': 'Finalizar',\n 'scribe.action.discard': 'Descartar',\n 'scribe.action.generate': 'Gerar documento',\n 'scribe.transcript': 'Transcrição',\n 'scribe.recovery.title': 'Gravação interrompida',\n 'scribe.consent.title': 'Iniciar gravação',\n}\n","import { en } from './en'\nimport { ptBR } from './pt-BR'\n\nexport const scribeLocales: Record<string, Record<string, string>> = {\n en,\n 'pt-BR': ptBR,\n}\n","// ---------------------------------------------------------------------------\n// IndexedDB — onde o áudio fica seguro antes de existir no servidor.\n//\n// Esta é a primeira camada de persistência local do SDK, e ela existe por um\n// motivo só: entre o microfone capturar um som e esse som chegar ao Storage\n// existe uma janela onde ele só mora na RAM de uma aba. Fechar a aba nessa\n// janela apaga a consulta. IndexedDB reduz essa janela para 5 segundos e a\n// torna sobrevivente a reload, crash e queda de rede.\n//\n// localStorage não serve (síncrono, ~5 MB, só string). OPFS seria elegante mas\n// não tem suporte parelho. IndexedDB guarda Blob por referência de arquivo nos\n// três engines, então 7 MB de consulta não viram 7 MB de string serializada.\n//\n// Zero dependência de propósito: `idb-keyval` resolveria a ergonomia, mas a\n// regra deste repo é não somar dependência para o que cabe em 200 linhas.\n// ---------------------------------------------------------------------------\n\nimport type { CaptureState } from '../types'\n\nconst DB_NAME = 'fayz.scribe'\nconst DB_VERSION = 1\n\nexport const STORE_SESSIONS = 'sessions'\nexport const STORE_PIECES = 'pieces'\nexport const STORE_SEGMENTS = 'segments'\nexport const STORE_TRANSCRIPT = 'transcript'\n\n/** Índice usado pelo pump para achar o que ainda precisa subir. */\nexport const IDX_SEGMENT_UPLOAD = 'by_upload'\n\n// ---------------------------------------------------------------------------\n// Formas\n// ---------------------------------------------------------------------------\n\nexport interface IdbSession {\n /** uuid gerado no cliente. É o mesmo id da linha no servidor. */\n id: string\n tenantId: string\n subjectId?: string\n subjectName?: string\n appointmentId?: string\n userId: string\n mimeType: string\n locale: string\n startedAtEpochMs: number\n /** null enquanto pausado. Ver `elapsedMs` — o tempo é calculado, nunca contado. */\n resumedAtEpochMs: number | null\n /** Relógio de parede acumulado através de pausas. */\n accumulatedMs: number\n /** Heartbeat de 2 s. Sessão com tick velho no boot = órfã de um crash. */\n lastTickEpochMs: number\n state: CaptureState\n /** Próximo segmento a abrir. */\n segIndex: number\n templateKeys: string[]\n}\n\nexport interface IdbPiece {\n sessionId: string\n segIndex: number\n pieceIndex: number\n blob: Blob\n bytes: number\n createdAt: number\n}\n\nexport interface IdbSegment {\n sessionId: string\n segIndex: number\n /** Vira null depois do upload — é o que devolve os bytes ao disco do usuário. */\n blob: Blob | null\n bytes: number\n startOffsetMs: number\n /** Duração NOMINAL (contada pelo cliente). A real vem do STT, no servidor. */\n nominalDurationMs: number\n partial: boolean\n gap: boolean\n /** No signal above the noise floor. Not the same as `gap`: the audio exists\n * and is kept, it just is not worth a transcription request. */\n silent?: boolean\n uploadState: 'pending' | 'uploading' | 'uploaded' | 'failed'\n attempts: number\n nextAttemptAt: number\n storagePath: string | null\n}\n\nexport interface IdbTranscriptPart {\n sessionId: string\n segIndex: number\n text: string\n startOffsetMs: number\n endOffsetMs: number\n provider: string\n confidence: number\n}\n\n// ---------------------------------------------------------------------------\n// Abertura\n// ---------------------------------------------------------------------------\n\nlet dbPromise: Promise<IDBDatabase> | null = null\n\nexport function isIdbAvailable(): boolean {\n return typeof indexedDB !== 'undefined'\n}\n\nexport function openScribeDb(): Promise<IDBDatabase> {\n if (dbPromise) return dbPromise\n dbPromise = new Promise((resolve, reject) => {\n if (!isIdbAvailable()) {\n reject(new Error('IndexedDB indisponível neste navegador'))\n return\n }\n const req = indexedDB.open(DB_NAME, DB_VERSION)\n\n req.onupgradeneeded = () => {\n const db = req.result\n if (!db.objectStoreNames.contains(STORE_SESSIONS)) {\n db.createObjectStore(STORE_SESSIONS, { keyPath: 'id' })\n }\n if (!db.objectStoreNames.contains(STORE_PIECES)) {\n // Chave composta: os pieces de um segmento saem juntos e em ordem num\n // único range, que é exatamente como a montagem os consome.\n db.createObjectStore(STORE_PIECES, { keyPath: ['sessionId', 'segIndex', 'pieceIndex'] })\n }\n if (!db.objectStoreNames.contains(STORE_SEGMENTS)) {\n const store = db.createObjectStore(STORE_SEGMENTS, { keyPath: ['sessionId', 'segIndex'] })\n // O pump varre por [estado, quando-tentar-de-novo]. Sem este índice ele\n // leria a tabela inteira a cada volta do loop.\n store.createIndex(IDX_SEGMENT_UPLOAD, ['uploadState', 'nextAttemptAt'])\n }\n if (!db.objectStoreNames.contains(STORE_TRANSCRIPT)) {\n db.createObjectStore(STORE_TRANSCRIPT, { keyPath: ['sessionId', 'segIndex'] })\n }\n }\n\n req.onsuccess = () => resolve(req.result)\n req.onerror = () => reject(req.error ?? new Error('falha ao abrir o IndexedDB'))\n req.onblocked = () => reject(new Error('IndexedDB bloqueado por outra aba'))\n })\n return dbPromise\n}\n\n// ---------------------------------------------------------------------------\n// Helpers de transação\n// ---------------------------------------------------------------------------\n\nfunction promisify<T>(req: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n req.onsuccess = () => resolve(req.result)\n req.onerror = () => reject(req.error ?? new Error('falha na operação do IndexedDB'))\n })\n}\n\nasync function withStore<T>(\n name: string,\n mode: IDBTransactionMode,\n fn: (store: IDBObjectStore) => IDBRequest<T> | Promise<T>,\n): Promise<T> {\n const db = await openScribeDb()\n const tx = db.transaction(name, mode)\n const store = tx.objectStore(name)\n const out = fn(store)\n const value = out instanceof Promise ? await out : await promisify(out)\n // Esperar o `complete` e não só o request: em modo readwrite o request pode\n // ter sucesso e a transação abortar depois (quota, por exemplo). Resolver\n // antes disso reportaria \"gravado\" para algo que não foi.\n await new Promise<void>((resolve, reject) => {\n tx.oncomplete = () => resolve()\n tx.onerror = () => reject(tx.error ?? new Error('transação abortada'))\n tx.onabort = () => reject(tx.error ?? new Error('transação abortada'))\n })\n return value\n}\n\n// ---------------------------------------------------------------------------\n// Sessões\n// ---------------------------------------------------------------------------\n\nexport function putSession(session: IdbSession): Promise<IDBValidKey> {\n return withStore(STORE_SESSIONS, 'readwrite', (s) => s.put(session))\n}\n\nexport function getSession(id: string): Promise<IdbSession | undefined> {\n return withStore(STORE_SESSIONS, 'readonly', (s) => s.get(id) as IDBRequest<IdbSession | undefined>)\n}\n\nexport function getAllSessions(): Promise<IdbSession[]> {\n return withStore(STORE_SESSIONS, 'readonly', (s) => s.getAll() as IDBRequest<IdbSession[]>)\n}\n\nexport function deleteSession(id: string): Promise<undefined> {\n return withStore(STORE_SESSIONS, 'readwrite', (s) => s.delete(id) as IDBRequest<undefined>)\n}\n\n/**\n * Sessões que o boot precisa olhar: estavam vivas e o heartbeat morreu. Só\n * `lastTickEpochMs` distingue \"a aba fechou agora\" de \"isto está órfão desde\n * ontem\" — o estado sozinho não distingue, porque a aba que morreu não teve\n * chance de escrever o estado final.\n */\nexport async function findOrphanSessions(staleAfterMs = 15_000): Promise<IdbSession[]> {\n const now = Date.now()\n const all = await getAllSessions()\n return all.filter(\n (s) =>\n (s.state === 'recording' || s.state === 'paused' || s.state === 'interrupted') &&\n now - s.lastTickEpochMs > staleAfterMs,\n )\n}\n\n// ---------------------------------------------------------------------------\n// Pieces — a unidade de durabilidade (5 s)\n// ---------------------------------------------------------------------------\n\nexport function putPiece(piece: IdbPiece): Promise<IDBValidKey> {\n return withStore(STORE_PIECES, 'readwrite', (s) => s.put(piece))\n}\n\n/** Todos os pieces de um segmento, em ordem de pieceIndex. */\nexport function getPieces(sessionId: string, segIndex: number): Promise<IdbPiece[]> {\n const range = IDBKeyRange.bound(\n [sessionId, segIndex, -Infinity],\n [sessionId, segIndex, Infinity],\n )\n return withStore(STORE_PIECES, 'readonly', (s) => s.getAll(range) as IDBRequest<IdbPiece[]>)\n}\n\n/** Pieces soltos de uma sessão inteira — o que a recuperação encontra depois de um crash. */\nexport function getAllPiecesForSession(sessionId: string): Promise<IdbPiece[]> {\n const range = IDBKeyRange.bound(\n [sessionId, -Infinity, -Infinity],\n [sessionId, Infinity, Infinity],\n )\n return withStore(STORE_PIECES, 'readonly', (s) => s.getAll(range) as IDBRequest<IdbPiece[]>)\n}\n\nexport function deletePieces(sessionId: string, segIndex: number): Promise<void> {\n const range = IDBKeyRange.bound(\n [sessionId, segIndex, -Infinity],\n [sessionId, segIndex, Infinity],\n )\n return withStore(STORE_PIECES, 'readwrite', (s) => promisify(s.delete(range) as IDBRequest<undefined>).then(() => undefined))\n}\n\n// ---------------------------------------------------------------------------\n// Segments — a unidade de upload + STT (30 s)\n// ---------------------------------------------------------------------------\n\nexport function putSegment(segment: IdbSegment): Promise<IDBValidKey> {\n return withStore(STORE_SEGMENTS, 'readwrite', (s) => s.put(segment))\n}\n\nexport function getSegment(sessionId: string, segIndex: number): Promise<IdbSegment | undefined> {\n return withStore(STORE_SEGMENTS, 'readonly', (s) =>\n s.get([sessionId, segIndex]) as IDBRequest<IdbSegment | undefined>,\n )\n}\n\nexport function getSegmentsForSession(sessionId: string): Promise<IdbSegment[]> {\n const range = IDBKeyRange.bound([sessionId, -Infinity], [sessionId, Infinity])\n return withStore(STORE_SEGMENTS, 'readonly', (s) => s.getAll(range) as IDBRequest<IdbSegment[]>)\n}\n\n/**\n * O que o pump deve tentar agora: pendente ou falho, com `nextAttemptAt` já\n * vencido. `uploading` fica de fora — se uma volta anterior travou nesse estado,\n * o `reclaimStuckUploads` do boot é quem devolve, não este método.\n */\nexport async function claimUploadable(limit: number): Promise<IdbSegment[]> {\n const now = Date.now()\n const out: IdbSegment[] = []\n for (const state of ['pending', 'failed'] as const) {\n if (out.length >= limit) break\n const range = IDBKeyRange.bound([state, -Infinity], [state, now])\n const rows = await withStore(STORE_SEGMENTS, 'readonly', (s) =>\n s.index(IDX_SEGMENT_UPLOAD).getAll(range, limit - out.length) as IDBRequest<IdbSegment[]>,\n )\n out.push(...rows)\n }\n // Ordem (sessão, índice) para o servidor ver os segmentos chegarem em ordem\n // cronológica no caso comum, o que deixa a transcrição parcial legível\n // enquanto ainda está subindo.\n return out.sort((a, b) =>\n a.sessionId === b.sessionId ? a.segIndex - b.segIndex : a.sessionId.localeCompare(b.sessionId),\n )\n}\n\n/**\n * Segmentos parados em `uploading` de uma aba que morreu no meio do upload.\n * Sem isso eles ficariam invisíveis para sempre: `claimUploadable` só olha\n * `pending` e `failed`.\n */\nexport async function reclaimStuckUploads(): Promise<number> {\n const range = IDBKeyRange.bound(['uploading', -Infinity], ['uploading', Infinity])\n const stuck = await withStore(STORE_SEGMENTS, 'readonly', (s) =>\n s.index(IDX_SEGMENT_UPLOAD).getAll(range) as IDBRequest<IdbSegment[]>,\n )\n for (const seg of stuck) {\n await putSegment({ ...seg, uploadState: 'pending', nextAttemptAt: 0 })\n }\n return stuck.length\n}\n\nexport function deleteSegmentsForSession(sessionId: string): Promise<void> {\n const range = IDBKeyRange.bound([sessionId, -Infinity], [sessionId, Infinity])\n return withStore(STORE_SEGMENTS, 'readwrite', (s) =>\n promisify(s.delete(range) as IDBRequest<undefined>).then(() => undefined),\n )\n}\n\n// ---------------------------------------------------------------------------\n// Transcrição (cache local, para a tela renderizar offline)\n// ---------------------------------------------------------------------------\n\nexport function putTranscriptPart(part: IdbTranscriptPart): Promise<IDBValidKey> {\n return withStore(STORE_TRANSCRIPT, 'readwrite', (s) => s.put(part))\n}\n\nexport function getTranscriptForSession(sessionId: string): Promise<IdbTranscriptPart[]> {\n const range = IDBKeyRange.bound([sessionId, -Infinity], [sessionId, Infinity])\n return withStore(STORE_TRANSCRIPT, 'readonly', (s) =>\n s.getAll(range) as IDBRequest<IdbTranscriptPart[]>,\n )\n}\n\n// ---------------------------------------------------------------------------\n// Quota\n// ---------------------------------------------------------------------------\n\n/** Consulta de 40 min a 24 kbps mono ≈ 7,2 MB. 50 MB de folga cobre um dia ruim. */\nexport const MIN_FREE_BYTES = 50 * 1024 * 1024\n\nexport interface StorageHealth {\n ok: boolean\n persisted: boolean\n freeBytes: number | null\n reason?: string\n}\n\n/**\n * Pré-voo antes do primeiro byte. Duas coisas distintas:\n *\n * `persist()` pede ao browser para NÃO despejar este origin quando o disco\n * apertar. Sem isso o navegador pode limpar o IndexedDB no meio da consulta e a\n * gravação some sem erro nenhum — o pior modo de falha possível.\n *\n * `estimate()` é a checagem de espaço. Recusar a gravar com o disco cheio é\n * infinitamente melhor que gravar 12 minutos e descobrir no minuto 13.\n */\nexport async function checkStorageHealth(): Promise<StorageHealth> {\n if (!isIdbAvailable()) {\n return { ok: false, persisted: false, freeBytes: null, reason: 'IndexedDB indisponível' }\n }\n let persisted = false\n try {\n if (navigator.storage?.persist) persisted = await navigator.storage.persist()\n } catch {\n // Firefox pode pedir permissão ao usuário e rejeitar. Não é fatal: sem\n // persistência a gravação ainda funciona, só fica sujeita a eviction.\n }\n try {\n if (!navigator.storage?.estimate) return { ok: true, persisted, freeBytes: null }\n const { quota = 0, usage = 0 } = await navigator.storage.estimate()\n const free = quota - usage\n if (free < MIN_FREE_BYTES) {\n return {\n ok: false,\n persisted,\n freeBytes: free,\n reason: `Espaço insuficiente no navegador (${Math.round(free / 1024 / 1024)} MB livres, mínimo ${MIN_FREE_BYTES / 1024 / 1024} MB)`,\n }\n }\n return { ok: true, persisted, freeBytes: free }\n } catch {\n return { ok: true, persisted, freeBytes: null }\n }\n}\n\n/** Só para testes — força a próxima chamada a reabrir o banco. */\nexport function __resetDbForTests(): void {\n dbPromise = null\n}\n","// ---------------------------------------------------------------------------\n// A captura. Um singleton de módulo, deliberadamente fora do React.\n//\n// A premissa inteira da feature é \"grave enquanto navego\". Num SPA isso já é\n// quase de graça — mas só se o dono da gravação NÃO for um componente. Um hook\n// morre no unmount; a gravação não pode. Então o recorder, o relógio, os\n// listeners e a máquina de estados vivem aqui, em escopo de módulo, e o React\n// apenas assina uma store para desenhar o pill.\n//\n// ---------------------------------------------------------------------------\n// Por que o recorder é ROTACIONADO a cada 30 s\n// ---------------------------------------------------------------------------\n// Em WebM/Opus com `start(timeslice)`, só o PRIMEIRO blob carrega o header\n// EBML; os seguintes são clusters nus. Consequência prática: não dá para pegar\n// o chunk 37 e mandar para o STT — isoladamente ele não é um arquivo.\n//\n// Rotacionar (parar o recorder e criar outro sobre o MESMO MediaStream) faz\n// cada segmento começar com header próprio. Aí `new Blob([p0..pk])` é um WebM\n// válido MESMO TRUNCADO no meio — e é isso que dá recuperação de segmento\n// parcial de graça depois de um crash.\n//\n// O preço é o gap da troca: ~15 ms por rotação, ~80 rotações em 40 minutos,\n// ~1,2 s de 2400 s (0,05%). A alternativa sem gap exigiria montar bytes de\n// container à mão e torcer para Chrome, Safari e Firefox decodificarem igual.\n// ---------------------------------------------------------------------------\n\nimport type { CaptureState, ConsentMode } from '../types'\nimport {\n checkStorageHealth,\n deletePieces,\n getPieces,\n putPiece,\n putSegment,\n putSession,\n getSession,\n type IdbSession,\n} from './idb'\n\n/** Unidade de durabilidade: no máximo isto de áudio vive só em RAM. */\nexport const PIECE_MS = 5_000\n/** Unidade de upload + STT. 6 pieces por segmento. */\nexport const SEGMENT_MS = 30_000\n/** Heartbeat. É o que distingue \"aba fechou agora\" de \"isto está órfão\". */\nconst HEARTBEAT_MS = 2_000\n\n/** Ordem de preferência. Safari só tem mp4; os outros preferem Opus. */\nconst MIME_CANDIDATES = [\n 'audio/webm;codecs=opus',\n 'audio/ogg;codecs=opus',\n 'audio/mp4',\n 'audio/webm',\n]\n\nexport function pickMimeType(): string | null {\n if (typeof MediaRecorder === 'undefined') return null\n for (const mime of MIME_CANDIDATES) {\n try {\n if (MediaRecorder.isTypeSupported(mime)) return mime\n } catch {\n // isTypeSupported pode lançar em engines antigas; seguir para a próxima.\n }\n }\n return null\n}\n\nexport function isCaptureSupported(): boolean {\n return (\n typeof navigator !== 'undefined' &&\n !!navigator.mediaDevices?.getUserMedia &&\n typeof MediaRecorder !== 'undefined' &&\n pickMimeType() !== null\n )\n}\n\n// ---------------------------------------------------------------------------\n// Estado vivo (não persistido — o persistido está no IndexedDB)\n// ---------------------------------------------------------------------------\n\ninterface LiveCapture {\n session: IdbSession\n stream: MediaStream\n recorder: MediaRecorder | null\n /** Pieces do segmento em aberto, na ordem em que chegaram. */\n buffer: Blob[]\n pieceIndex: number\n /** Offset do começo do segmento atual desde o início da sessão. */\n segStartOffsetMs: number\n wakeLock: WakeLockSentinel | null\n heartbeat: ReturnType<typeof setInterval> | null\n /** Fecha o segmento em aberto quando ele completa 30 s. */\n rotateTimer: ReturnType<typeof setTimeout> | null\n /** True enquanto uma rotação está em curso, para o `onstop` saber o motivo. */\n rotating: boolean\n /** Quando a captura foi interrompida (mic perdido), para dimensionar o gap. */\n interruptedAtMs: number | null\n /** Medidor de nível do segmento em aberto — ver `attachLevelMeter`. */\n meter: { context: AudioContext; stop: () => void; peak: () => number; reset: () => void } | null\n}\n\nlet live: LiveCapture | null = null\n\ntype Listener = (snapshot: CaptureSnapshot) => void\nconst listeners = new Set<Listener>()\n\nexport interface CaptureSnapshot {\n state: CaptureState\n sessionId: string | null\n subjectId?: string\n subjectName?: string\n /** Relógio de parede. Move o cronômetro; NÃO é o registro. */\n elapsedMs: number\n segIndex: number\n error?: string\n}\n\nlet lastError: string | undefined\n\n// Microphone choice.\n//\n// In a consulting room the system default is rarely the right input, and\n// recording the whole encounter on the wrong one is discovered later — when the\n// audio no longer exists. The choice lives in localStorage and NOT in the\n// session's IndexedDB: it is an operator preference on that machine, not a fact\n// about that recording.\n\nconst DEVICE_KEY = 'fayz.scribe.inputDevice'\n\n/** The remembered input, if the operator picked one. */\nexport function getPreferredDeviceId(): string | null {\n try {\n return localStorage.getItem(DEVICE_KEY)\n } catch {\n return null\n }\n}\n\nexport function setPreferredDeviceId(deviceId: string | null): void {\n try {\n if (deviceId) localStorage.setItem(DEVICE_KEY, deviceId)\n else localStorage.removeItem(DEVICE_KEY)\n } catch {\n // Private mode without storage: the choice lasts this session only.\n }\n}\n\n/** In use now — the remembered one, or what the running capture opened. */\nlet activeDeviceId: string | null = null\n\nexport function getActiveDeviceId(): string | null {\n return activeDeviceId ?? getPreferredDeviceId()\n}\n\nfunction audioConstraints(): MediaTrackConstraints {\n const deviceId = getActiveDeviceId()\n return {\n channelCount: 1,\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n sampleRate: 16_000,\n // `exact` would be worse: a remembered device may be unplugged, and then\n // recording would not start at all instead of falling back.\n ...(deviceId ? { deviceId: { ideal: deviceId } } : {}),\n }\n}\n\n/** Swaps the input of a RUNNING capture. The open clip closes first — swapping\n * without closing would put two sources inside one file. */\nexport async function switchInputDevice(deviceId: string): Promise<boolean> {\n activeDeviceId = deviceId\n if (!live) return true\n const cap = live\n try {\n const next = await navigator.mediaDevices.getUserMedia({ audio: audioConstraints() })\n const wasRecording = cap.session.state === 'recording'\n if (wasRecording) await pauseCapture()\n cap.stream.getTracks().forEach((track) => track.stop())\n cap.stream = next\n cap.meter?.stop()\n cap.meter = attachLevelMeter(next)\n if (wasRecording) await resumeCapture()\n emit()\n return true\n } catch {\n return false\n }\n}\n\n/**\n * O MediaStream que está sendo gravado, para quem só quer OLHAR para ele —\n * hoje o medidor de nível do composer do assistente.\n *\n * Emprestar o stream em vez de deixar a shell abrir o seu: um segundo\n * `getUserMedia` sobre o mesmo microfone durante uma gravação é uma segunda\n * superfície de permissão, mais bateria, e em alguns aparelhos um ganho\n * diferente — tudo para desenhar barras de um áudio que já está aqui.\n *\n * Quem recebe NUNCA para estas tracks: elas são a gravação.\n */\nexport function getCaptureStream(): MediaStream | null {\n return live?.stream ?? null\n}\n\nexport function getSnapshot(): CaptureSnapshot {\n if (!live) return { state: 'idle', sessionId: null, elapsedMs: 0, segIndex: 0, error: lastError }\n return {\n state: live.session.state,\n sessionId: live.session.id,\n subjectId: live.session.subjectId,\n subjectName: live.session.subjectName,\n elapsedMs: elapsedMs(live.session),\n segIndex: live.session.segIndex,\n error: lastError,\n }\n}\n\nfunction emit(): void {\n const snap = getSnapshot()\n for (const fn of listeners) {\n try {\n fn(snap)\n } catch {\n // Um assinante quebrado não pode derrubar a gravação.\n }\n }\n}\n\nexport function subscribeCapture(fn: Listener): () => void {\n listeners.add(fn)\n fn(getSnapshot())\n return () => listeners.delete(fn)\n}\n\n// ---------------------------------------------------------------------------\n// Tempo: calcular, nunca contar\n// ---------------------------------------------------------------------------\n\n/**\n * Timers throttlam. Uma aba em segundo plano clampa `setInterval` em ≥1/s, e\n * uma aba congelada não roda nada. Qualquer cronômetro que ACUMULE ticks vai\n * mentir — e mentir para menos, que é o modo de falha silencioso.\n *\n * `Date.now()` sobrevive a throttle e a sono do sistema operacional. O preço é\n * ficar sujeito a salto de relógio de parede (NTP, fuso), o que é raro e\n * detectável, enquanto o throttle é garantido e invisível.\n *\n * Nada disso é o registro: a duração que vale é SUM(segments.duration_ms), do\n * áudio que de fato existe. Este número move o pill e para por aí.\n */\nexport function elapsedMs(session: IdbSession): number {\n const running = session.state === 'recording' && session.resumedAtEpochMs !== null\n return session.accumulatedMs + (running ? Date.now() - session.resumedAtEpochMs! : 0)\n}\n\n// ---------------------------------------------------------------------------\n// Segmentos\n// ---------------------------------------------------------------------------\n\n/**\n * Fecha o segmento em aberto: junta os pieces num Blob e o enfileira para\n * upload. `partial` marca o que foi montado de um segmento cortado no meio —\n * vale transcrever mesmo assim, 22 segundos é melhor que zero.\n */\n// Noise floor.\n//\n// Whisper HALLUCINATES over silence, returning phrases it learned to produce\n// when there is nothing to transcribe. In a consultation with pauses those are\n// invented sentences entering a clinical record.\n//\n// Measuring the level during capture solves both at once: a silent clip never\n// becomes a request (no cost, no latency) and cannot hallucinate. The audio is\n// still stored — what is dropped is the REQUEST, not the record.\n\n/** Below this, 30s of audio holds no speech — only room tone. */\nconst SILENCE_PEAK = 0.02\n\nfunction attachLevelMeter(stream: MediaStream): LiveCapture['meter'] {\n const AudioCtor: typeof AudioContext | undefined =\n typeof window === 'undefined'\n ? undefined\n : window.AudioContext ??\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext\n if (!AudioCtor) return null\n\n try {\n const context = new AudioCtor()\n const analyser = context.createAnalyser()\n analyser.fftSize = 512\n context.createMediaStreamSource(stream).connect(analyser)\n const buffer = new Uint8Array(analyser.frequencyBinCount)\n let peak = 0\n\n // 10Hz is enough: a syllable lasts ~150ms, so nothing audible slips\n // between two samples.\n const timer = setInterval(() => {\n analyser.getByteFrequencyData(buffer)\n let sum = 0\n for (const v of buffer) sum += v\n const level = sum / buffer.length / 255\n if (level > peak) peak = level\n }, 100)\n\n return {\n context,\n stop: () => {\n clearInterval(timer)\n void context.close().catch(() => {})\n },\n peak: () => peak,\n reset: () => {\n peak = 0\n },\n }\n } catch {\n return null\n }\n}\n\nasync function closeSegment(cap: LiveCapture, partial: boolean): Promise<void> {\n const { session, buffer, segStartOffsetMs } = cap\n if (buffer.length === 0) return\n\n const segIndex = session.segIndex\n const blob = new Blob(buffer, { type: session.mimeType })\n const nominalDurationMs = buffer.length * PIECE_MS\n // No meter (browser without AudioContext) means transcribe anyway: when in\n // doubt, transcribing is the safe behaviour.\n const silent = cap.meter ? cap.meter.peak() < SILENCE_PEAK : false\n cap.meter?.reset()\n\n await putSegment({\n sessionId: session.id,\n segIndex,\n blob,\n bytes: blob.size,\n startOffsetMs: segStartOffsetMs,\n nominalDurationMs,\n partial,\n gap: false,\n silent,\n uploadState: 'pending',\n attempts: 0,\n nextAttemptAt: 0,\n storagePath: null,\n })\n\n // Pieces já cumpriram o papel: existem só para o intervalo entre \"capturado\"\n // e \"montado num segmento\". Apagar aqui é o que impede o IndexedDB de guardar\n // o mesmo áudio duas vezes durante uma consulta inteira.\n await deletePieces(session.id, segIndex)\n\n cap.buffer = []\n cap.pieceIndex = 0\n cap.segStartOffsetMs = segStartOffsetMs + nominalDurationMs\n session.segIndex = segIndex + 1\n await putSession(session)\n}\n\n/**\n * Grava uma linha SINTÉTICA marcando um buraco. Não tem áudio nem upload: existe\n * para a transcrição admitir a costura.\n *\n * Emendar dois momentos separados por 8 minutos como se fossem contínuos produz\n * um registro que MENTE — e num registro clínico ou jurídico isso é pior que um\n * registro com furo declarado.\n */\nasync function writeGapSegment(cap: LiveCapture, durationMs: number): Promise<void> {\n const { session } = cap\n const segIndex = session.segIndex\n await putSegment({\n sessionId: session.id,\n segIndex,\n blob: null,\n bytes: 0,\n startOffsetMs: cap.segStartOffsetMs,\n nominalDurationMs: durationMs,\n partial: false,\n gap: true,\n uploadState: 'pending',\n attempts: 0,\n nextAttemptAt: 0,\n storagePath: null,\n })\n cap.segStartOffsetMs += durationMs\n session.segIndex = segIndex + 1\n await putSession(session)\n}\n\n// ---------------------------------------------------------------------------\n// Recorder\n// ---------------------------------------------------------------------------\n\nfunction startRecorder(cap: LiveCapture): void {\n const recorder = new MediaRecorder(cap.stream, {\n mimeType: cap.session.mimeType,\n // 24 kbps mono é transparente para fala e mantém 40 min em ~7 MB — o número\n // que torna toda a história de durabilidade barata.\n audioBitsPerSecond: 24_000,\n })\n\n recorder.ondataavailable = (ev: BlobEvent) => {\n if (!ev.data || ev.data.size === 0) return\n cap.buffer.push(ev.data)\n const pieceIndex = cap.pieceIndex++\n // Fire-and-forget de propósito: o `ondataavailable` não pode aguardar I/O,\n // ou o próximo piece encavala. A ordem é garantida pelo pieceIndex.\n void putPiece({\n sessionId: cap.session.id,\n segIndex: cap.session.segIndex,\n pieceIndex,\n blob: ev.data,\n bytes: ev.data.size,\n createdAt: Date.now(),\n }).catch(() => {\n // Escrita local falhou (quota?). O piece ainda está em `buffer`, então o\n // segmento fecha inteiro — só perdemos a rede de segurança do crash.\n })\n }\n\n recorder.onstop = () => {\n const wasRotating = cap.rotating\n cap.rotating = false\n void (async () => {\n await closeSegment(cap, !wasRotating)\n // Rotação: o segmento fechou porque completou 30 s, então segue gravando.\n if (wasRotating && cap.session.state === 'recording') {\n startRecorder(cap)\n armRotation(cap)\n }\n emit()\n })()\n }\n\n recorder.onerror = () => {\n void handleInterruption('erro do gravador')\n }\n\n cap.recorder = recorder\n recorder.start(PIECE_MS)\n}\n\nfunction armRotation(cap: LiveCapture): void {\n if (cap.rotateTimer) clearTimeout(cap.rotateTimer)\n cap.rotateTimer = setTimeout(() => {\n if (!cap.recorder || cap.recorder.state !== 'recording') return\n cap.rotating = true\n // `stop()` dispara um último `ondataavailable` com o resto do buffer e\n // depois `onstop`, que fecha o segmento e reabre. O MediaStream continua\n // vivo o tempo todo — é isso que mantém o gap na casa dos milissegundos.\n cap.recorder.stop()\n }, SEGMENT_MS)\n}\n\n// ---------------------------------------------------------------------------\n// Wake lock, beforeunload, ciclo de vida da página\n// ---------------------------------------------------------------------------\n\nasync function acquireWakeLock(cap: LiveCapture): Promise<void> {\n try {\n if (!('wakeLock' in navigator)) return\n cap.wakeLock = await navigator.wakeLock.request('screen')\n } catch {\n // Negado ou indisponível. Não é fatal — só significa que a tela pode apagar.\n }\n}\n\nasync function releaseWakeLock(cap: LiveCapture): Promise<void> {\n try {\n await cap.wakeLock?.release()\n } catch {\n // Já liberado pelo browser ao esconder a página.\n }\n cap.wakeLock = null\n}\n\nfunction onBeforeUnload(ev: BeforeUnloadEvent): void {\n if (!live || live.session.state !== 'recording') return\n // Grosseiro, mas é o único freio que o browser oferece contra o Cmd+W\n // acidental no meio de uma consulta.\n ev.preventDefault()\n ev.returnValue = ''\n}\n\nfunction onVisibilityChange(): void {\n if (!live) return\n // O wake lock é solto automaticamente quando a página esconde; reaver ao\n // voltar, senão a tela apaga no meio da consulta seguinte.\n if (document.visibilityState === 'visible' && live.session.state === 'recording' && !live.wakeLock) {\n void acquireWakeLock(live)\n }\n emit()\n}\n\nfunction onFreeze(): void {\n if (!live) return\n live.interruptedAtMs = Date.now()\n live.session.lastTickEpochMs = Date.now()\n void putSession(live.session)\n}\n\nfunction onResume(): void {\n if (!live) return\n // A track pode ter morrido enquanto congelado. Verificar em vez de supor.\n const track = live.stream.getAudioTracks()[0]\n if (!track || track.readyState === 'ended') {\n void handleInterruption('microfone perdido enquanto a aba estava suspensa')\n }\n}\n\nfunction attachLifecycle(): void {\n window.addEventListener('beforeunload', onBeforeUnload)\n document.addEventListener('visibilitychange', onVisibilityChange)\n document.addEventListener('freeze', onFreeze)\n document.addEventListener('resume', onResume)\n}\n\nfunction detachLifecycle(): void {\n window.removeEventListener('beforeunload', onBeforeUnload)\n document.removeEventListener('visibilitychange', onVisibilityChange)\n document.removeEventListener('freeze', onFreeze)\n document.removeEventListener('resume', onResume)\n}\n\nfunction watchTrack(cap: LiveCapture): void {\n const track = cap.stream.getAudioTracks()[0]\n if (!track) return\n track.onended = () => void handleInterruption('microfone desconectado')\n track.onmute = () => void handleInterruption('microfone silenciado pelo sistema')\n}\n\n// ---------------------------------------------------------------------------\n// Transições\n// ---------------------------------------------------------------------------\n\nasync function handleInterruption(reason: string): Promise<void> {\n if (!live || live.session.state !== 'recording') return\n const cap = live\n cap.interruptedAtMs = Date.now()\n cap.session.state = 'interrupted'\n cap.session.accumulatedMs = elapsedMs(cap.session)\n cap.session.resumedAtEpochMs = null\n lastError = reason\n\n if (cap.rotateTimer) clearTimeout(cap.rotateTimer)\n try {\n if (cap.recorder?.state === 'recording') cap.recorder.stop()\n else await closeSegment(cap, true)\n } catch {\n await closeSegment(cap, true)\n }\n await releaseWakeLock(cap)\n await putSession(cap.session)\n emit()\n}\n\nexport interface StartOptions {\n tenantId: string\n userId: string\n subjectId?: string\n subjectName?: string\n appointmentId?: string\n locale?: string\n templateKeys?: string[]\n consentMode?: ConsentMode\n /** Chamado assim que a linha do servidor deve existir. */\n onSessionCreated?: (session: IdbSession, consentMode?: ConsentMode) => Promise<void> | void\n}\n\n/**\n * Começa a gravar. A ordem aqui importa: checagem de suporte → saúde do\n * armazenamento → permissão do microfone → linha local → PRIMEIRO BYTE. A linha\n * no servidor é notificada depois, porque uma gravação que depende da rede para\n * COMEÇAR já nasce frágil — e o id é gerado aqui, então não há round-trip.\n */\nexport async function startCapture(opts: StartOptions): Promise<{ ok: true; sessionId: string } | { ok: false; reason: string }> {\n if (live) return { ok: false, reason: 'já existe uma gravação em andamento' }\n if (!isCaptureSupported()) return { ok: false, reason: 'este navegador não suporta gravação de áudio' }\n\n const health = await checkStorageHealth()\n if (!health.ok) return { ok: false, reason: health.reason ?? 'armazenamento local indisponível' }\n\n const mimeType = pickMimeType()!\n let stream: MediaStream\n try {\n stream = await navigator.mediaDevices.getUserMedia({\n audio: audioConstraints(),\n })\n } catch (err) {\n const name = (err as DOMException)?.name\n return {\n ok: false,\n reason:\n name === 'NotAllowedError'\n ? 'permissão de microfone negada'\n : name === 'NotFoundError'\n ? 'nenhum microfone encontrado'\n : 'não foi possível acessar o microfone',\n }\n }\n\n const now = Date.now()\n const session: IdbSession = {\n id: crypto.randomUUID(),\n tenantId: opts.tenantId,\n subjectId: opts.subjectId,\n subjectName: opts.subjectName,\n appointmentId: opts.appointmentId,\n userId: opts.userId,\n mimeType,\n locale: opts.locale ?? 'pt-BR',\n startedAtEpochMs: now,\n resumedAtEpochMs: now,\n accumulatedMs: 0,\n lastTickEpochMs: now,\n state: 'recording',\n segIndex: 0,\n templateKeys: opts.templateKeys ?? [],\n }\n await putSession(session)\n\n const cap: LiveCapture = {\n session,\n stream,\n recorder: null,\n buffer: [],\n pieceIndex: 0,\n segStartOffsetMs: 0,\n wakeLock: null,\n heartbeat: null,\n rotateTimer: null,\n rotating: false,\n interruptedAtMs: null,\n meter: attachLevelMeter(stream),\n }\n live = cap\n lastError = undefined\n\n startRecorder(cap)\n armRotation(cap)\n watchTrack(cap)\n attachLifecycle()\n void acquireWakeLock(cap)\n\n cap.heartbeat = setInterval(() => {\n if (!live) return\n live.session.lastTickEpochMs = Date.now()\n // The accumulated time too: it was only written on pause/stop/end, so a\n // tab closed mid-encounter left the session at 0 while half an hour of\n // audio sat on disk — and the recovery banner offered \"00:00\" for it.\n live.session.accumulatedMs = elapsedMs(live.session)\n live.session.resumedAtEpochMs = Date.now()\n void putSession(live.session)\n emit()\n }, HEARTBEAT_MS)\n\n // A linha do servidor entra depois do primeiro byte, de propósito.\n try {\n await opts.onSessionCreated?.(session, opts.consentMode)\n } catch {\n // Rede indisponível no start não pode abortar a gravação: o pump reconcilia\n // depois, e áudio no IndexedDB sem linha no servidor ainda é recuperável.\n }\n\n emit()\n return { ok: true, sessionId: session.id }\n}\n\n/**\n * Pausa. A TRACK CONTINUA VIVA de propósito: soltar o stream e chamar\n * `getUserMedia` de novo no resume arrisca um segundo prompt de permissão no\n * meio da consulta, e um prompt no meio de uma consulta é uma gravação perdida.\n */\nexport async function pauseCapture(): Promise<void> {\n if (!live || live.session.state !== 'recording') return\n const cap = live\n cap.session.accumulatedMs = elapsedMs(cap.session)\n cap.session.resumedAtEpochMs = null\n cap.session.state = 'paused'\n\n if (cap.rotateTimer) clearTimeout(cap.rotateTimer)\n if (cap.recorder?.state === 'recording') {\n cap.rotating = false\n cap.recorder.stop() // fecha o segmento em aberto como parcial\n }\n await releaseWakeLock(cap)\n await putSession(cap.session)\n emit()\n}\n\nexport async function resumeCapture(): Promise<void> {\n if (!live || (live.session.state !== 'paused' && live.session.state !== 'interrupted')) return\n const cap = live\n\n // Retomar depois de uma INTERRUPÇÃO grava o buraco. Retomar de uma PAUSA não:\n // pausa é intenção declarada e todo mundo sabe que houve um intervalo.\n if (cap.session.state === 'interrupted' && cap.interruptedAtMs) {\n await writeGapSegment(cap, Date.now() - cap.interruptedAtMs)\n }\n cap.interruptedAtMs = null\n\n const track = cap.stream.getAudioTracks()[0]\n if (!track || track.readyState === 'ended') {\n try {\n cap.stream = await navigator.mediaDevices.getUserMedia({ audio: audioConstraints() })\n cap.meter?.stop()\n cap.meter = attachLevelMeter(cap.stream)\n watchTrack(cap)\n } catch {\n lastError = 'não foi possível reabrir o microfone'\n emit()\n return\n }\n }\n\n cap.session.state = 'recording'\n cap.session.resumedAtEpochMs = Date.now()\n lastError = undefined\n await putSession(cap.session)\n\n startRecorder(cap)\n armRotation(cap)\n void acquireWakeLock(cap)\n emit()\n}\n\n/**\n * Encerra. Devolve o id para a tela navegar para a sessão. O upload NÃO espera\n * por isto — o pump já subiu quase tudo durante a gravação; o que sobra aqui é\n * o último segmento.\n */\nexport async function stopCapture(): Promise<string | null> {\n if (!live) return null\n const cap = live\n const sessionId = cap.session.id\n\n cap.session.accumulatedMs = elapsedMs(cap.session)\n cap.session.resumedAtEpochMs = null\n cap.session.state = 'stopping'\n await putSession(cap.session)\n emit()\n\n if (cap.rotateTimer) clearTimeout(cap.rotateTimer)\n if (cap.heartbeat) clearInterval(cap.heartbeat)\n\n if (cap.recorder?.state === 'recording') {\n cap.rotating = false\n await new Promise<void>((resolve) => {\n const rec = cap.recorder!\n const prev = rec.onstop\n rec.onstop = (ev) => {\n // Deixa o handler original fechar o segmento, depois resolve.\n if (typeof prev === 'function') prev.call(rec, ev)\n setTimeout(resolve, 0)\n }\n rec.stop()\n })\n } else {\n await closeSegment(cap, true)\n }\n\n for (const track of cap.stream.getTracks()) track.stop()\n await releaseWakeLock(cap)\n detachLifecycle()\n\n const finished = await getSession(sessionId)\n if (finished) {\n finished.state = 'idle'\n await putSession(finished)\n }\n\n live?.meter?.stop()\n live = null\n emit()\n return sessionId\n}\n\n/**\n * Reata a UI a uma sessão órfã sem religar o microfone. Um browser não permite\n * reabrir o mic sem gesto do usuário depois de um reload, então \"retomar\" é\n * sempre um clique — o que esta função faz é deixar o pill e o banner\n * enxergarem a sessão de novo.\n */\nexport async function adoptOrphan(session: IdbSession): Promise<void> {\n if (live) return\n const pieces = await getPieces(session.id, session.segIndex)\n\n live = {\n session: { ...session, state: 'interrupted' },\n stream: new MediaStream(),\n recorder: null,\n buffer: pieces.map((p) => p.blob),\n pieceIndex: pieces.length,\n segStartOffsetMs: 0,\n wakeLock: null,\n heartbeat: null,\n rotateTimer: null,\n rotating: false,\n interruptedAtMs: session.lastTickEpochMs,\n // An adopted orphan has no open mic — nothing to measure.\n meter: null,\n }\n // O rabo que sobrou vira segmento parcial agora, não quando o usuário decidir:\n // recuperar BYTES nunca espera decisão humana.\n const adopted = live\n if (pieces.length > 0) await closeSegment(adopted, true)\n adopted.session.state = 'interrupted'\n await putSession(adopted.session)\n lastError = 'gravação interrompida'\n emit()\n}\n\n/** Descarta a sessão viva sem apagar nada — o servidor decide o que fazer com o áudio. */\nexport async function releaseCapture(): Promise<void> {\n if (!live) return\n if (live.rotateTimer) clearTimeout(live.rotateTimer)\n if (live.heartbeat) clearInterval(live.heartbeat)\n for (const track of live.stream.getTracks()) track.stop()\n await releaseWakeLock(live)\n detachLifecycle()\n live?.meter?.stop()\n live = null\n lastError = undefined\n emit()\n}\n\nexport function getLiveSessionId(): string | null {\n return live?.session.id ?? null\n}\n","// Nomes físicos das tabelas do scribe (prefixo plg_scribe_*). Importe T e use\n// T.<chave> no provider, para um rename cair em exatamente um lugar.\nexport const T = {\n sessions: 'plg_scribe_sessions',\n segments: 'plg_scribe_segments',\n generations: 'plg_scribe_generations',\n} as const\n\n/** Bucket privado do áudio. A gramática do caminho está em `storagePath()`. */\nexport const AUDIO_BUCKET = 'scribe-audio'\n\n/**\n * {tenant_id}/{yyyy}/{mm}/{session_id}/{seg_index:05d}.{ext}\n *\n * Segmento 1 é a chave do isolamento (toda policy pendura nele); 2-3 dão\n * varredura por prefixo para retenção; 4 agrupa a sessão e é inadivinhável;\n * 5 ordena lexicograficamente = cronologicamente, o que faz `list()` já voltar\n * na ordem certa sem sort.\n */\nexport function storagePath(args: {\n tenantId: string\n sessionId: string\n segIndex: number\n startedAt: Date\n ext: string\n}): string {\n const yyyy = String(args.startedAt.getUTCFullYear())\n const mm = String(args.startedAt.getUTCMonth() + 1).padStart(2, '0')\n const seg = String(args.segIndex).padStart(5, '0')\n return `${args.tenantId}/${yyyy}/${mm}/${args.sessionId}/${seg}.${args.ext}`\n}\n\n/** Prefixo de uma sessão inteira — é o que \"apagar o áudio\" varre. */\nexport function sessionPrefix(args: { tenantId: string; sessionId: string; startedAt: Date }): string {\n const yyyy = String(args.startedAt.getUTCFullYear())\n const mm = String(args.startedAt.getUTCMonth() + 1).padStart(2, '0')\n return `${args.tenantId}/${yyyy}/${mm}/${args.sessionId}`\n}\n\n/** Extensão a partir do mime negociado — o regex da policy só aceita estas quatro. */\nexport function extForMime(mimeType: string): string {\n if (mimeType.includes('ogg')) return 'ogg'\n if (mimeType.includes('mp4')) return 'm4a'\n if (mimeType.includes('mpeg')) return 'mp3'\n return 'webm'\n}\n","// ---------------------------------------------------------------------------\n// O pump — o que leva os bytes do disco do usuário para o servidor.\n//\n// Regras que ele nunca quebra, porque cada uma corresponde a um jeito real de\n// perder uma consulta:\n//\n// 1. NUNCA descarta um segmento. Não existe caminho de código que remova um\n// segmento não enviado. Backoff cresce até o teto e fica lá tentando.\n// 2. NUNCA para de tentar. Não há contador máximo de tentativas — a rede que\n// caiu às 14h volta às 15h e o segmento sobe.\n// 3. Roda em TODO boot, não só durante gravação. É assim que o rabo de uma\n// sessão que morreu ontem chega hoje.\n// 4. 409 do Storage é SUCESSO. `upsert:false` faz o re-envio do mesmo caminho\n// falhar com \"Duplicate\" — e \"já está lá\" é precisamente o que queríamos.\n// Tratar isso como erro faria o replay girar para sempre.\n//\n// Singleton de módulo, como a captura, e pelo mesmo motivo: nada aqui pode\n// depender de um componente estar montado.\n// ---------------------------------------------------------------------------\n\nimport { getSupabaseClientOptional } from '@fayz-ai/core'\nimport {\n claimUploadable,\n getSegment,\n putSegment,\n putSession,\n getSession,\n reclaimStuckUploads,\n type IdbSegment,\n} from './idb'\nimport { AUDIO_BUCKET, T, extForMime, storagePath } from '../data/tables'\n\n/** Quantos segmentos sobem em paralelo. 2 é suficiente: um segmento tem ~90 KB. */\nconst CONCURRENCY = 2\n/** Intervalo ocioso. Um segmento novo aparece a cada 30 s; olhar a cada 3 s é folgado. */\nconst IDLE_POLL_MS = 3_000\nconst BACKOFF_BASE_MS = 500\nconst BACKOFF_CEILING_MS = 30_000\n\nconst CHANNEL_NAME = 'fayz.scribe'\n\nlet running = false\nlet timer: ReturnType<typeof setTimeout> | null = null\nlet channel: BroadcastChannel | null = null\n/** Id desta aba. Menor id ganha a eleição de líder. */\nconst tabId = typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : String(Math.random())\nlet isLeader = true\nlet peerIds = new Set<string>()\n\n// ---------------------------------------------------------------------------\n// Eleição de líder\n// ---------------------------------------------------------------------------\n\n/**\n * Duas abas subindo o mesmo segmento é SEGURO (`upsert:false` + 409-como-sucesso\n * garantem isso), só desperdiça banda. O canal existe mesmo assim porque a aba\n * B precisa saber que há gravação em curso na aba A — senão ela oferece um\n * segundo botão \"Iniciar\" e o usuário grava a mesma consulta duas vezes.\n */\nfunction setupChannel(): void {\n if (typeof BroadcastChannel === 'undefined' || channel) return\n channel = new BroadcastChannel(CHANNEL_NAME)\n channel.onmessage = (ev: MessageEvent) => {\n const msg = ev.data as { type?: string; tabId?: string }\n if (msg?.type === 'hello' && msg.tabId) {\n peerIds.add(msg.tabId)\n channel?.postMessage({ type: 'ack', tabId })\n recomputeLeader()\n } else if (msg?.type === 'ack' && msg.tabId) {\n peerIds.add(msg.tabId)\n recomputeLeader()\n } else if (msg?.type === 'bye' && msg.tabId) {\n peerIds.delete(msg.tabId)\n recomputeLeader()\n }\n }\n channel.postMessage({ type: 'hello', tabId })\n window.addEventListener('pagehide', () => channel?.postMessage({ type: 'bye', tabId }))\n}\n\nfunction recomputeLeader(): void {\n const all = [tabId, ...peerIds].sort()\n isLeader = all[0] === tabId\n}\n\n// ---------------------------------------------------------------------------\n// Upload de um segmento\n// ---------------------------------------------------------------------------\n\nfunction isDuplicateError(err: unknown): boolean {\n const e = err as { statusCode?: string | number; status?: number; message?: string; error?: string }\n const status = Number(e?.statusCode ?? e?.status)\n if (status === 409) return true\n const text = `${e?.message ?? ''} ${e?.error ?? ''}`.toLowerCase()\n return text.includes('duplicate') || text.includes('already exists') || text.includes('resource already')\n}\n\nfunction nextBackoff(attempts: number): number {\n const base = Math.min(BACKOFF_CEILING_MS, BACKOFF_BASE_MS * 2 ** attempts)\n // Jitter para N abas/segmentos não baterem no servidor no mesmo milissegundo\n // depois que a rede volta.\n return Date.now() + base + Math.floor(Math.random() * 500)\n}\n\nasync function uploadOne(segment: IdbSegment): Promise<void> {\n const supabase = getSupabaseClientOptional() as any\n if (!supabase) return\n\n const session = await getSession(segment.sessionId)\n if (!session) {\n // Sessão sumiu do IndexedDB mas o segmento ficou: não há como montar o\n // caminho (falta tenant e data). Marcar falho e deixar o boot limpar.\n await putSegment({ ...segment, uploadState: 'failed', nextAttemptAt: nextBackoff(segment.attempts + 1), attempts: segment.attempts + 1 })\n return\n }\n\n await putSegment({ ...segment, uploadState: 'uploading' })\n\n // Buraco não tem áudio: só registra a linha e segue. É por isso que ele passa\n // pelo pump em vez de ir direto ao servidor — assim o manifesto recebe os\n // segmentos na ordem, com ou sem bytes.\n if (segment.gap || !segment.blob) {\n await recordSegment(supabase, session.id, segment, null)\n await putSegment({ ...segment, blob: null, uploadState: 'uploaded', storagePath: null })\n return\n }\n\n const path = storagePath({\n tenantId: session.tenantId,\n sessionId: session.id,\n segIndex: segment.segIndex,\n startedAt: new Date(session.startedAtEpochMs),\n ext: extForMime(session.mimeType),\n })\n\n try {\n const { error } = await supabase.storage.from(AUDIO_BUCKET).upload(path, segment.blob, {\n contentType: session.mimeType,\n // Sem upsert de propósito: combina com a ausência de policy de UPDATE no\n // bucket. Um caminho gravado nunca é trocado por outro conteúdo.\n upsert: false,\n })\n if (error && !isDuplicateError(error)) throw error\n\n await recordSegment(supabase, session.id, segment, path)\n\n // A speechless clip never becomes a transcription request: `skipped` is\n // terminal, so the drain walks past it. Saves the call AND prevents the\n // hallucination that would enter the record as the patient's words.\n if (segment.silent) {\n await supabase\n .from(T.segments)\n .update({ stt_state: 'skipped' })\n .eq('session_id', session.id)\n .eq('seg_index', segment.segIndex)\n }\n\n // Só agora o blob local pode sumir. Fazer isso antes do RPC deixaria uma\n // janela em que o áudio não está nem aqui nem lá.\n await putSegment({ ...segment, blob: null, uploadState: 'uploaded', storagePath: path })\n } catch (err) {\n const attempts = segment.attempts + 1\n await putSegment({\n ...segment,\n uploadState: 'failed',\n attempts,\n nextAttemptAt: nextBackoff(attempts),\n })\n }\n}\n\nasync function recordSegment(\n supabase: any,\n sessionId: string,\n segment: IdbSegment,\n path: string | null,\n): Promise<void> {\n const { error } = await supabase.rpc('scribe_record_segment', {\n p_payload: {\n session_id: sessionId,\n seg_index: segment.segIndex,\n storage_path: path,\n bytes: segment.bytes,\n start_offset_ms: segment.startOffsetMs,\n // Duração NOMINAL. O servidor sobrescreve com a medida do STT quando ela\n // chega — o cliente conta tempo, o STT conta amostras.\n duration_ms: segment.nominalDurationMs,\n partial: segment.partial,\n gap: segment.gap,\n },\n })\n if (error) throw error\n}\n\n// ---------------------------------------------------------------------------\n// Loop\n// ---------------------------------------------------------------------------\n\nasync function tick(): Promise<void> {\n if (!running) return\n\n try {\n // Offline: não gasta tentativa (que só inflaria o backoff). O listener de\n // 'online' religa o loop na hora em que a rede volta.\n if (typeof navigator !== 'undefined' && navigator.onLine === false) return\n if (!isLeader) return\n if (!getSupabaseClientOptional()) return\n\n const batch = await claimUploadable(CONCURRENCY)\n if (batch.length === 0) return\n\n await Promise.all(\n batch.map(async (seg) => {\n // Reler antes de subir: outra volta do loop (ou outra aba) pode ter\n // resolvido este segmento desde o `claim`.\n const fresh = await getSegment(seg.sessionId, seg.segIndex)\n if (!fresh || fresh.uploadState === 'uploaded') return\n await uploadOne(fresh)\n }),\n )\n } catch {\n // Um erro no loop nunca pode matar o loop.\n } finally {\n if (running) timer = setTimeout(() => void tick(), IDLE_POLL_MS)\n }\n}\n\nfunction onOnline(): void {\n if (!running) return\n if (timer) clearTimeout(timer)\n void tick()\n}\n\n/** Acorda o pump agora, sem esperar o poll. Chamado ao fechar um segmento. */\nexport function kickPump(): void {\n if (!running) return\n if (timer) clearTimeout(timer)\n timer = setTimeout(() => void tick(), 0)\n}\n\nexport function startPump(): void {\n if (running) return\n running = true\n setupChannel()\n if (typeof window !== 'undefined') window.addEventListener('online', onOnline)\n // Devolve à fila o que uma aba morta deixou preso em `uploading` — sem isto\n // esses segmentos ficariam invisíveis para sempre.\n void reclaimStuckUploads().finally(() => void tick())\n}\n\nexport function stopPump(): void {\n running = false\n if (timer) clearTimeout(timer)\n timer = null\n if (typeof window !== 'undefined') window.removeEventListener('online', onOnline)\n channel?.postMessage({ type: 'bye', tabId })\n channel?.close()\n channel = null\n peerIds = new Set()\n}\n\nexport function isPumpLeader(): boolean {\n return isLeader\n}\n\n/**\n * Marca a sessão como encerrada no servidor. Separado do `stopCapture` porque a\n * captura não pode depender da rede para terminar.\n */\nexport async function finalizeSession(sessionId: string, wallDurationMs: number): Promise<void> {\n const supabase = getSupabaseClientOptional() as any\n if (!supabase) return\n const local = await getSession(sessionId)\n await supabase\n .from('plg_scribe_sessions')\n .update({\n status: 'uploading',\n ended_at: new Date().toISOString(),\n wall_duration_ms: wallDurationMs,\n updated_at: new Date().toISOString(),\n })\n .eq('id', sessionId)\n if (local) {\n local.state = 'idle'\n await putSession(local)\n }\n}\n","// ---------------------------------------------------------------------------\n// initScribeRuntime — o único ponto de entrada do lado durável.\n//\n// Chamado uma vez pela factory do plugin. Depois disso a gravação existe\n// independente de qualquer árvore React: o React apenas assina.\n//\n// A ordem do boot não é arbitrária. Primeiro o pump (recuperar BYTES nunca\n// espera decisão de usuário), depois a varredura de órfãs, e só então a UI\n// pergunta o que fazer. Um usuário que ignore o banner por uma hora ainda tem\n// todo o áudio no servidor quando voltar.\n// ---------------------------------------------------------------------------\n\nimport { getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core'\nimport {\n findOrphanSessions,\n getSession,\n isIdbAvailable,\n putSession,\n type IdbSession,\n} from './idb'\nimport {\n adoptOrphan,\n elapsedMs,\n getSnapshot,\n getLiveSessionId,\n pauseCapture,\n releaseCapture,\n resumeCapture,\n startCapture,\n stopCapture,\n subscribeCapture,\n type StartOptions,\n} from './capture'\nimport { finalizeSession, kickPump, startPump } from './pump'\nimport type { ConsentMode, ScribeRetentionConfig } from '../types'\n\nexport * from './idb'\nexport * from './capture'\nexport { startPump, stopPump, kickPump, isPumpLeader, finalizeSession } from './pump'\n\nlet initialized = false\nlet retention: ScribeRetentionConfig = {\n audioDays: 90,\n requireConsent: true,\n defaultConsentMode: 'verbal',\n}\n\n/** Sessões órfãs achadas no boot — a UI lê isto para desenhar o banner. */\n/** Below this an interrupted session has nothing worth recovering. */\nconst TRIVIAL_ORPHAN_MS = 5_000\n\n/**\n * How long an orphan session actually ran.\n *\n * `accumulatedMs` is written by the heartbeat, but sessions recorded BEFORE\n * that fix sit at 0 with audio on disk; the wall clock is the reliable floor\n * there. The larger of the two is the only reading that neither discards a real\n * recording nor inflates an empty one.\n */\nexport function orphanDurationMs(session: IdbSession): number {\n const wall = Math.max(0, session.lastTickEpochMs - session.startedAtEpochMs)\n return Math.max(session.accumulatedMs, wall)\n}\n\nlet orphans: IdbSession[] = []\nconst orphanListeners = new Set<(list: IdbSession[]) => void>()\n\nexport function subscribeOrphans(fn: (list: IdbSession[]) => void): () => void {\n orphanListeners.add(fn)\n fn(orphans)\n return () => orphanListeners.delete(fn)\n}\n\nfunction emitOrphans(): void {\n for (const fn of orphanListeners) {\n try {\n fn(orphans)\n } catch {\n /* um assinante quebrado não derruba a recuperação */\n }\n }\n}\n\nexport interface ScribeRuntimeOptions {\n retention?: Partial<ScribeRetentionConfig>\n}\n\nexport function initScribeRuntime(options?: ScribeRuntimeOptions): void {\n if (initialized) return\n if (typeof window === 'undefined' || !isIdbAvailable()) return\n initialized = true\n retention = { ...retention, ...options?.retention }\n\n // O pump primeiro, e sem await: ele começa a drenar enquanto o resto do boot\n // acontece. Um segmento de ontem já está subindo antes de a tela existir.\n startPump()\n\n void (async () => {\n try {\n const found = await findOrphanSessions()\n // A session with no real audio is not recovery, it is litter: offering\n // three choices for a 00:00 recording spends attention on nothing.\n const worthless = found.filter((s) => orphanDurationMs(s) < TRIVIAL_ORPHAN_MS)\n for (const session of worthless) {\n try {\n await discardSession(session.id)\n } catch {\n // Could not clean now — the next boot tries again.\n }\n }\n orphans = found.filter((s) => orphanDurationMs(s) >= TRIVIAL_ORPHAN_MS)\n if (orphans.length > 0) {\n // Reata a mais recente para o pill enxergá-la; as outras ficam listadas\n // no banner. Adotar já converte pieces soltos em segmento parcial.\n const latest = orphans.reduce((a, b) => (a.lastTickEpochMs > b.lastTickEpochMs ? a : b))\n await adoptOrphan(latest)\n kickPump()\n }\n emitOrphans()\n } catch {\n // Boot da recuperação nunca pode derrubar o app.\n }\n })()\n}\n\n// ---------------------------------------------------------------------------\n// Ações de alto nível (o que a UI chama)\n// ---------------------------------------------------------------------------\n\nexport interface BeginSessionArgs {\n subjectId?: string\n subjectName?: string\n appointmentId?: string\n userId: string\n locale?: string\n templateKeys?: string[]\n consentMode?: ConsentMode\n}\n\n/**\n * Começa uma sessão: grava primeiro, avisa o servidor depois. Se o INSERT\n * falhar (offline), a gravação segue — o áudio está no IndexedDB e o pump\n * reconcilia. O caso contrário, exigir rede para começar, transformaria uma\n * queda de wifi em consulta perdida.\n */\nexport async function beginSession(\n args: BeginSessionArgs,\n): Promise<{ ok: true; sessionId: string } | { ok: false; reason: string }> {\n const tenantId = getActiveTenantId()\n if (!tenantId) return { ok: false, reason: 'nenhum tenant ativo' }\n\n const opts: StartOptions = {\n tenantId,\n userId: args.userId,\n subjectId: args.subjectId,\n subjectName: args.subjectName,\n appointmentId: args.appointmentId,\n locale: args.locale,\n templateKeys: args.templateKeys,\n consentMode: args.consentMode ?? retention.defaultConsentMode,\n onSessionCreated: async (session, consentMode) => {\n const supabase = getSupabaseClientOptional() as any\n if (!supabase) return\n const retentionUntil = new Date(Date.now() + retention.audioDays * 86_400_000)\n await supabase.from('plg_scribe_sessions').insert({\n id: session.id,\n tenant_id: session.tenantId,\n subject_id: session.subjectId ?? null,\n appointment_id: session.appointmentId ?? null,\n context_kind: session.appointmentId ? 'appointment' : session.subjectId ? 'person' : 'standalone',\n owner_user_id: session.userId,\n status: 'recording',\n locale: session.locale,\n mime_type: session.mimeType,\n started_at: new Date(session.startedAtEpochMs).toISOString(),\n // O consentimento é carimbado no INSERT, não depois: a linha nunca\n // existe sem a base legal do que ela guarda.\n consent_at: retention.requireConsent ? new Date().toISOString() : null,\n consent_mode: retention.requireConsent ? (consentMode ?? retention.defaultConsentMode) : null,\n retention_until: retentionUntil.toISOString().slice(0, 10),\n })\n },\n }\n\n const result = await startCapture(opts)\n if (result.ok) kickPump()\n return result\n}\n\nexport async function pauseSession(): Promise<void> {\n await pauseCapture()\n const id = getLiveSessionId()\n if (!id) return\n kickPump()\n await patchStatus(id, 'paused')\n}\n\nexport async function resumeSession(): Promise<void> {\n await resumeCapture()\n const id = getLiveSessionId()\n if (id) await patchStatus(id, 'recording')\n}\n\n/** Encerra e devolve o id, para a tela navegar até a sessão. */\nexport async function endSession(): Promise<string | null> {\n const snapshot = getSnapshot()\n const wall = snapshot.elapsedMs\n const id = await stopCapture()\n if (!id) return null\n kickPump()\n await finalizeSession(id, wall)\n orphans = orphans.filter((o) => o.id !== id)\n emitOrphans()\n return id\n}\n\n/**\n * Descarte SUAVE. `abandoned`, não delete: o áudio fica até `retention_until`.\n * Hard-delete de sessão de saúde por clique errado é um dano que não tem\n * desfazer, e \"descartar\" quase sempre significa \"não quero o documento\", não\n * \"apague a consulta\".\n */\nexport async function discardSession(sessionId?: string): Promise<void> {\n const id = sessionId ?? getLiveSessionId()\n if (!id) return\n await releaseCapture()\n await patchStatus(id, 'abandoned')\n const local = await getSession(id)\n if (local) {\n local.state = 'idle'\n await putSession(local)\n }\n orphans = orphans.filter((o) => o.id !== id)\n emitOrphans()\n}\n\n/** Dispensa o banner sem mexer no estado da sessão — ela continua no servidor. */\nexport function dismissOrphan(sessionId: string): void {\n orphans = orphans.filter((o) => o.id !== sessionId)\n emitOrphans()\n}\n\nasync function patchStatus(sessionId: string, status: string): Promise<void> {\n const supabase = getSupabaseClientOptional() as any\n if (!supabase) return\n try {\n await supabase\n .from('plg_scribe_sessions')\n .update({ status, updated_at: new Date().toISOString() })\n .eq('id', sessionId)\n } catch {\n // Status é conveniência de UI; o manifesto de segmentos é a verdade. Falhar\n // aqui não pode interromper a gravação.\n }\n}\n\nexport { subscribeCapture, getSnapshot, elapsedMs, getLiveSessionId }\n","// ---------------------------------------------------------------------------\n// A ponte entre o runtime (que vive fora do React) e os componentes.\n//\n// O runtime é a fonte da verdade; esta store é uma projeção dele. Ela nunca\n// decide nada — quem decide é `runtime/capture.ts`. Se um componente pudesse\n// escrever estado de gravação aqui, teríamos duas verdades e a que sobrevive à\n// navegação seria a errada.\n// ---------------------------------------------------------------------------\n\nimport { create } from 'zustand'\nimport type { CaptureState } from './types'\nimport type { IdbSession } from './runtime/idb'\nimport { getSnapshot, subscribeCapture, subscribeOrphans } from './runtime'\n\nexport interface ScribeUiState {\n state: CaptureState\n sessionId: string | null\n subjectId?: string\n subjectName?: string\n elapsedMs: number\n segIndex: number\n error?: string\n /** Sessões interrompidas encontradas no boot. Alimentam o banner. */\n orphans: IdbSession[]\n /** Sessão cuja tela está aberta — não necessariamente a que está gravando. */\n viewingSessionId: string | null\n setViewing: (id: string | null) => void\n}\n\nexport const useScribeStore = create<ScribeUiState>((set) => ({\n ...getSnapshot(),\n orphans: [],\n viewingSessionId: null,\n setViewing: (id) => set({ viewingSessionId: id }),\n}))\n\nlet wired = false\n\n/**\n * Liga a store aos assinantes do runtime. Idempotente: a factory do plugin\n * chama uma vez, e chamar de novo (HMR) não duplica assinatura.\n */\nexport function wireScribeStore(): void {\n if (wired) return\n wired = true\n subscribeCapture((snap) => {\n useScribeStore.setState({\n state: snap.state,\n sessionId: snap.sessionId,\n subjectId: snap.subjectId,\n subjectName: snap.subjectName,\n elapsedMs: snap.elapsedMs,\n segIndex: snap.segIndex,\n error: snap.error,\n })\n })\n subscribeOrphans((orphans) => useScribeStore.setState({ orphans }))\n}\n\n/** `mm:ss`, ou `h:mm:ss` quando passa da hora. */\nexport function formatElapsed(ms: number): string {\n const total = Math.max(0, Math.floor(ms / 1000))\n const h = Math.floor(total / 3600)\n const m = Math.floor((total % 3600) / 60)\n const s = total % 60\n const mm = String(m).padStart(2, '0')\n const ss = String(s).padStart(2, '0')\n return h > 0 ? `${h}:${mm}:${ss}` : `${mm}:${ss}`\n}\n","import * as React from 'react'\nimport { createPortal } from 'react-dom'\nimport { Mic, Pause, Play, Square } from 'lucide-react'\nimport { Button } from '@fayz-ai/ui/primitives'\nimport { useScribeStore, formatElapsed } from '../store'\nimport { endSession, getSnapshot, pauseSession, resumeSession } from '../runtime'\nimport type { ScribeLabels } from '../types'\n\n// ---------------------------------------------------------------------------\n// O pill. Sempre montado, nunca remontado.\n//\n// Ele mora em `shell.topbar.end`, que é a única zona global do shell que de fato\n// é renderizada (`AdminShell.tsx:979-980`) e que fica FORA do switch de rota —\n// só o conteúdo da página troca dentro do PageTransition. Por isso navegar não\n// desmonta o pill nem interrompe nada.\n//\n// (`shell.floating` e `PluginManifest.floatingUI` estão declarados nos tipos do\n// core mas nada os renderiza. Construir neles daria silêncio.)\n//\n// Pausar e parar vivem AQUI e em nenhum outro lugar. A tela onde a gravação\n// começou desapareceu no instante em que o usuário navegou — que é o ponto\n// inteiro da feature. Um botão de parar que só existe na tela de origem seria\n// um botão que some quando é mais necessário.\n//\n// Ele também é o artefato de LGPD: a prova de que o titular podia ver, o tempo\n// todo, que a gravação estava em curso.\n// ---------------------------------------------------------------------------\n\nexport interface ScribeRecordingPillProps {\n labels: ScribeLabels\n onOpenSession?: (sessionId: string, subjectId?: string) => void\n /**\n * Render only on small screens. On desktop the live state belongs to the\n * assistant (FAB pill + panel bar) — two timers for one recording is the app\n * telling you the same thing twice. The mobile portal stays because the FAB\n * is opt-in there, and the visible proof of an ongoing recording is a legal\n * obligation, not decoration.\n */\n mobileOnly?: boolean\n}\n\n/** Abaixo disto o topbar do shell é `hidden md:flex` — ver comentário no portal. */\nconst MOBILE_BREAKPOINT = 768\n\nfunction useIsMobile(): boolean {\n const [isMobile, setIsMobile] = React.useState(\n () => typeof window !== 'undefined' && window.innerWidth < MOBILE_BREAKPOINT,\n )\n React.useEffect(() => {\n const mq = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)\n const onChange = (e: MediaQueryListEvent) => setIsMobile(e.matches)\n setIsMobile(mq.matches)\n mq.addEventListener('change', onChange)\n return () => mq.removeEventListener('change', onChange)\n }, [])\n return isMobile\n}\n\n/**\n * Repinta só quando a aba está visível. `requestAnimationFrame` em vez de\n * `setInterval` porque aba escondida throttla o interval para 1/min — e o\n * cronômetro que volta errado ao focar é pior que um que não anda.\n *\n * O tempo em si nunca é acumulado aqui: `elapsedMs` é recalculado de\n * `Date.now()` no runtime. Este hook só decide QUANDO redesenhar.\n */\nfunction useTicker(active: boolean): number {\n const [, force] = React.useReducer((n: number) => n + 1, 0)\n React.useEffect(() => {\n if (!active) return\n let raf = 0\n let last = 0\n const loop = (t: number) => {\n if (t - last >= 250) {\n last = t\n if (document.visibilityState === 'visible') force()\n }\n raf = requestAnimationFrame(loop)\n }\n raf = requestAnimationFrame(loop)\n const onVis = () => force()\n document.addEventListener('visibilitychange', onVis)\n return () => {\n cancelAnimationFrame(raf)\n document.removeEventListener('visibilitychange', onVis)\n }\n }, [active])\n return 0\n}\n\nexport function ScribeRecordingPill({ labels, onOpenSession, mobileOnly }: ScribeRecordingPillProps) {\n const state = useScribeStore((s) => s.state)\n const sessionId = useScribeStore((s) => s.sessionId)\n const subjectId = useScribeStore((s) => s.subjectId)\n const subjectName = useScribeStore((s) => s.subjectName)\n const isMobile = useIsMobile()\n const [busy, setBusy] = React.useState(false)\n\n const active = state === 'recording' || state === 'paused' || state === 'interrupted'\n useTicker(state === 'recording')\n\n // `getSnapshot()` e não o campo da store: aquele só muda quando o runtime\n // emite (transição de estado, giro de segmento), então o cronômetro andava\n // aos pulos. Este recalcula do relógio de parede na hora da leitura.\n //\n // Fora do selector de propósito: o valor muda a cada frame e um selector\n // sobre ele faria a store notificar todo assinante 4x por segundo.\n const elapsedMs = active ? getSnapshot().elapsedMs : 0\n\n const handleToggle = React.useCallback(async () => {\n setBusy(true)\n try {\n if (state === 'recording') await pauseSession()\n else await resumeSession()\n } finally {\n setBusy(false)\n }\n }, [state])\n\n const handleStop = React.useCallback(async () => {\n setBusy(true)\n try {\n const id = await endSession()\n if (id) onOpenSession?.(id, subjectId)\n } finally {\n setBusy(false)\n }\n }, [onOpenSession, subjectId])\n\n if (!active || !sessionId) return null\n if (mobileOnly && !isMobile) return null\n\n const tone =\n state === 'recording'\n ? 'bg-destructive/10 text-destructive border-destructive/30'\n : state === 'interrupted'\n ? 'bg-amber-500/10 text-amber-700 border-amber-500/30 dark:text-amber-400'\n : 'bg-muted text-muted-foreground border-border'\n\n const content = (\n <div\n className={`flex items-center gap-2 rounded-full border px-2.5 py-1 ${tone}`}\n role=\"status\"\n aria-live=\"polite\"\n aria-label={`${labels.sessionSingular}: ${formatElapsed(elapsedMs)}`}\n >\n <button\n type=\"button\"\n onClick={() => onOpenSession?.(sessionId, subjectId)}\n className=\"flex items-center gap-1.5 text-sm font-medium tabular-nums\"\n title={subjectName ?? labels.sessionSingular}\n >\n <span className=\"relative flex h-2 w-2 shrink-0\">\n {state === 'recording' && (\n <span className=\"absolute inline-flex h-full w-full animate-ping rounded-full bg-current opacity-60\" />\n )}\n <span className=\"relative inline-flex h-2 w-2 rounded-full bg-current\" />\n </span>\n {formatElapsed(elapsedMs)}\n {subjectName && <span className=\"hidden max-w-[12ch] truncate sm:inline\">· {subjectName}</span>}\n </button>\n\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-6 w-6 p-0\"\n disabled={busy}\n onClick={handleToggle}\n title={state === 'recording' ? labels.pause : labels.resume}\n aria-label={state === 'recording' ? labels.pause : labels.resume}\n >\n {state === 'recording' ? <Pause className=\"h-3.5 w-3.5\" /> : <Play className=\"h-3.5 w-3.5\" />}\n </Button>\n\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-6 w-6 p-0\"\n disabled={busy}\n onClick={handleStop}\n title={labels.finish}\n aria-label={labels.finish}\n >\n <Square className=\"h-3.5 w-3.5\" />\n </Button>\n </div>\n )\n\n // No mobile o topbar do shell é `hidden md:flex` (AppShell.tsx:317, porque\n // `mobileHeader` default é 'minimal'), então o pill sumiria justamente onde\n // ele deixa de ser conveniência e vira o ÚNICO botão de pausa.\n //\n // Um portal fixo resolve dentro do plugin, sem mudar o SDK.\n if (isMobile && typeof document !== 'undefined') {\n return createPortal(\n <div className=\"fixed inset-x-0 top-0 z-50 flex justify-center bg-background/80 p-1.5 backdrop-blur md:hidden\">\n {content}\n </div>,\n document.body,\n )\n }\n\n return content\n}\n\n// ---------------------------------------------------------------------------\n// Botão de iniciar\n// ---------------------------------------------------------------------------\n\nexport interface ScribeStartButtonProps {\n labels: ScribeLabels\n disabled?: boolean\n onStart: () => void\n variant?: 'default' | 'outline'\n size?: 'sm' | 'default'\n}\n\nexport function ScribeStartButton({ labels, disabled, onStart, variant = 'default', size = 'sm' }: ScribeStartButtonProps) {\n const state = useScribeStore((s) => s.state)\n const busy = state === 'recording' || state === 'paused' || state === 'interrupted' || state === 'requesting'\n\n return (\n <Button variant={variant} size={size} disabled={disabled || busy} onClick={onStart} title={labels.start}>\n <Mic className=\"mr-1.5 h-4 w-4\" />\n {labels.start}\n </Button>\n )\n}\n","// The READ side: the screen reads what the SERVER knows, never what local\n// IndexedDB thinks. That is what makes a session recoverable from another\n// device — laptop dies, open the phone, the transcript is there. The WRITE side\n// of the audio is the pump.\n\nimport { getSupabaseClientOptional } from '@fayz-ai/core'\nimport { T, AUDIO_BUCKET, sessionPrefix } from './tables'\nimport type { ScribeGeneration, ScribeSegment, ScribeSession } from '../types'\n\nfunction client(): any {\n const supabase = getSupabaseClientOptional() as any\n if (!supabase) throw new Error('Supabase não inicializado')\n return supabase\n}\n\nfunction mapSession(r: any): ScribeSession {\n return {\n id: r.id,\n tenantId: r.tenant_id,\n subjectId: r.subject_id ?? undefined,\n contextKind: r.context_kind,\n appointmentId: r.appointment_id ?? undefined,\n ownerUserId: r.owner_user_id,\n status: r.status,\n locale: r.locale,\n mimeType: r.mime_type ?? undefined,\n startedAt: r.started_at,\n endedAt: r.ended_at ?? undefined,\n wallDurationMs: r.wall_duration_ms ?? 0,\n audioDurationMs: r.audio_duration_ms ?? 0,\n segmentCount: r.segment_count ?? 0,\n uploadedSegmentCount: r.uploaded_segment_count ?? 0,\n transcribedSegmentCount: r.transcribed_segment_count ?? 0,\n transcriptChars: r.transcript_chars ?? 0,\n consentAt: r.consent_at ?? undefined,\n consentMode: r.consent_mode ?? undefined,\n retentionUntil: r.retention_until ?? undefined,\n audioDeletedAt: r.audio_deleted_at ?? undefined,\n error: r.error ?? undefined,\n metadata: r.metadata ?? {},\n }\n}\n\nfunction mapSegment(r: any): ScribeSegment {\n return {\n sessionId: r.session_id,\n segIndex: r.seg_index,\n tenantId: r.tenant_id,\n storagePath: r.storage_path ?? undefined,\n bytes: r.bytes ?? undefined,\n startOffsetMs: r.start_offset_ms ?? 0,\n durationMs: r.duration_ms ?? undefined,\n partial: !!r.partial,\n gap: !!r.gap,\n uploadState: r.upload_state,\n uploadedAt: r.uploaded_at ?? undefined,\n sttState: r.stt_state,\n sttProvider: r.stt_provider ?? undefined,\n sttAttempts: r.stt_attempts ?? 0,\n sttError: r.stt_error ?? undefined,\n text: r.text ?? undefined,\n words: r.words ?? undefined,\n confidence: r.confidence ?? undefined,\n }\n}\n\nfunction mapGeneration(r: any): ScribeGeneration {\n return {\n id: r.id,\n tenantId: r.tenant_id,\n sessionId: r.session_id,\n templateId: r.template_id ?? undefined,\n templateKey: r.template_key ?? undefined,\n tabOrder: r.tab_order ?? 0,\n title: r.title ?? undefined,\n status: r.status,\n markdown: r.markdown ?? undefined,\n markdownEdited: r.markdown_edited ?? undefined,\n facts: r.facts ?? undefined,\n model: r.model ?? undefined,\n promptVersion: r.prompt_version ?? undefined,\n inputTokens: r.input_tokens ?? undefined,\n outputTokens: r.output_tokens ?? undefined,\n error: r.error ?? undefined,\n documentId: r.document_id ?? undefined,\n createdBy: r.created_by ?? undefined,\n createdAt: r.created_at,\n }\n}\n\nexport async function fetchSession(sessionId: string): Promise<ScribeSession | null> {\n const { data, error } = await client().from(T.sessions).select('*').eq('id', sessionId).maybeSingle()\n if (error) throw error\n return data ? mapSession(data) : null\n}\n\nexport async function fetchSegments(sessionId: string): Promise<ScribeSegment[]> {\n const { data, error } = await client()\n .from(T.segments)\n .select('*')\n .eq('session_id', sessionId)\n .order('seg_index', { ascending: true })\n if (error) throw error\n return (data ?? []).map(mapSegment)\n}\n\nexport async function fetchGenerations(sessionId: string): Promise<ScribeGeneration[]> {\n const { data, error } = await client()\n .from(T.generations)\n .select('*')\n .eq('session_id', sessionId)\n .neq('status', 'discarded')\n .order('tab_order', { ascending: true })\n if (error) throw error\n return (data ?? []).map(mapGeneration)\n}\n\n/** Sessões abertas de qualquer dispositivo — a recuperação cross-device. */\nexport async function fetchOpenSessions(userId: string): Promise<ScribeSession[]> {\n const { data, error } = await client()\n .from(T.sessions)\n .select('*')\n .eq('owner_user_id', userId)\n .in('status', ['recording', 'paused', 'interrupted', 'uploading', 'transcribing', 'ready', 'generating'])\n .order('started_at', { ascending: false })\n .limit(20)\n if (error) throw error\n return (data ?? []).map(mapSession)\n}\n\nexport async function fetchSessionsForSubject(subjectId: string): Promise<ScribeSession[]> {\n const { data, error } = await client()\n .from(T.sessions)\n .select('*')\n .eq('subject_id', subjectId)\n .order('started_at', { ascending: false })\n .limit(50)\n if (error) throw error\n return (data ?? []).map(mapSession)\n}\n\n/**\n * Autosave do rascunho. Escreve SÓ `markdown_edited` — `markdown` é a saída do\n * modelo e nunca é tocada. É a diferença entre as duas que prova a revisão e\n * que alimenta o ajuste de prompt.\n */\nexport async function saveDraft(generationId: string, markdownEdited: string): Promise<void> {\n const { error } = await client()\n .from(T.generations)\n .update({ markdown_edited: markdownEdited, updated_at: new Date().toISOString() })\n .eq('id', generationId)\n if (error) throw error\n}\n\nexport async function discardGeneration(generationId: string): Promise<void> {\n const { error } = await client()\n .from(T.generations)\n .update({ status: 'discarded', updated_at: new Date().toISOString() })\n .eq('id', generationId)\n if (error) throw error\n}\n\n/**\n * Grava o documento na ficha. Três escritas encadeadas:\n *\n * documents o arquétipo — é o que o PersonDocumentsWidget lista\n * plg_forms_documents a extensão com o markdown e as seções parseadas\n * generations fecha o ciclo apontando para o documento criado\n *\n * `kind` fica `'form'` de propósito: assim a lista de documentos da pessoa\n * mostra este documento SEM nenhuma mudança no plugin-forms. O que distingue um\n * documento de scribe é `metadata.scribe`, que é o filtro de qualquer visão\n * específica.\n */\n/**\n * The forms template id for a generation, creating it on first use.\n *\n * `plg_forms_documents.template_id` is NOT NULL — forms models a document as an\n * instance of a tenant template — but a generation from a CODE preset had no\n * row, so saving died on the constraint after recording, transcribing and\n * generating. Loosening the constraint would weaken all of forms; materialising\n * the preset matches the truth: \"Anamnese\" IS a document type of that tenant.\n */\nasync function ensureFormsTemplate(args: {\n tenantId: string\n key: string\n name: string\n category?: string\n}): Promise<string> {\n const supabase = client()\n const { tenantId, key, name, category } = args\n\n const { data: found } = await supabase\n .from('plg_forms_templates')\n .select('id')\n .eq('tenant_id', tenantId)\n .eq('metadata->>scribePresetKey', key)\n .maybeSingle()\n if (found?.id) return found.id as string\n\n const { data: created, error } = await supabase\n .from('plg_forms_templates')\n .insert({\n tenant_id: tenantId,\n name,\n category: category ?? 'general',\n // A narrative document has no fields: empty schema so the builder opens\n // without breaking, provenance in metadata.\n schema: { fields: [], layout: { columns: 12 } },\n metadata: { scribePresetKey: key, narrative: true },\n })\n .select('id')\n .single()\n if (error || !created) throw error ?? new Error('falha ao criar o modelo do documento')\n return created.id as string\n}\n\nexport async function commitGeneration(args: {\n generation: ScribeGeneration\n session: ScribeSession\n markdown: string\n title: string\n sections: Record<string, string>\n edited: boolean\n /** Document type name (\"Anamnese\") — becomes the template name. */\n templateName?: string\n /** Preset category, so the document lands in the right forms drawer. */\n templateCategory?: string\n}): Promise<string> {\n const supabase = client()\n const { generation, session, markdown, title, sections, edited } = args\n\n // No template_id, no insert — see ensureFormsTemplate.\n const templateId =\n generation.templateId ??\n (generation.templateKey\n ? await ensureFormsTemplate({\n tenantId: session.tenantId,\n key: generation.templateKey,\n name: args.templateName ?? title,\n category: args.templateCategory,\n })\n : null)\n if (!templateId) throw new Error('geração sem modelo de documento associado')\n\n const { data: doc, error: docErr } = await supabase\n .from('documents')\n .insert({\n tenant_id: session.tenantId,\n kind: 'form',\n person_id: session.subjectId ?? null,\n title,\n status: 'completed',\n metadata: {\n scribe: {\n sessionId: session.id,\n generationId: generation.id,\n model: generation.model,\n promptVersion: generation.promptVersion,\n edited,\n audioDurationMs: session.audioDurationMs,\n },\n },\n })\n .select('id')\n .single()\n if (docErr || !doc) throw docErr ?? new Error('falha ao criar o documento')\n\n const { error: extErr } = await supabase.from('plg_forms_documents').insert({\n document_id: doc.id,\n tenant_id: session.tenantId,\n template_id: templateId,\n data: {\n markdown,\n sections,\n sourceSessionId: session.id,\n generationId: generation.id,\n },\n })\n if (extErr) throw extErr\n\n const { error: genErr } = await supabase\n .from(T.generations)\n .update({ status: 'committed', document_id: doc.id, updated_at: new Date().toISOString() })\n .eq('id', generation.id)\n if (genErr) throw genErr\n\n await maybeCompleteSession(session.id)\n return doc.id\n}\n\n/** A sessão fecha quando toda geração virou documento ou foi descartada. */\nasync function maybeCompleteSession(sessionId: string): Promise<void> {\n const supabase = client()\n const { data } = await supabase.from(T.generations).select('status').eq('session_id', sessionId)\n if (!data || data.length === 0) return\n const open = data.filter((g: any) => !['committed', 'discarded'].includes(g.status))\n if (open.length > 0) return\n await supabase\n .from(T.sessions)\n .update({ status: 'completed', updated_at: new Date().toISOString() })\n .eq('id', sessionId)\n}\n\n/**\n * Apaga o ÁUDIO e nada mais. Transcrição e documento assinado ficam — é o que\n * torna o direito de eliminação (art. 18 VI) exercível sem destruir o registro\n * clínico, que tem prazo de guarda próprio.\n */\nexport async function deleteSessionAudio(session: ScribeSession): Promise<number> {\n const supabase = client()\n const prefix = sessionPrefix({\n tenantId: session.tenantId,\n sessionId: session.id,\n startedAt: new Date(session.startedAt),\n })\n\n const { data: files } = await supabase.storage.from(AUDIO_BUCKET).list(prefix)\n const paths = (files ?? []).map((f: any) => `${prefix}/${f.name}`)\n if (paths.length > 0) await supabase.storage.from(AUDIO_BUCKET).remove(paths)\n\n await supabase\n .from(T.sessions)\n .update({ audio_deleted_at: new Date().toISOString(), updated_at: new Date().toISOString() })\n .eq('id', session.id)\n\n // Path cleared on the segment rows too: a path pointing at a deleted object\n // would make the STT retry download nothing, forever.\n await supabase\n .from(T.segments)\n .update({ storage_path: null, upload_state: 'missing' })\n .eq('session_id', session.id)\n\n return paths.length\n}\n\n// ---------------------------------------------------------------------------\n// Invocação das edge functions\n// ---------------------------------------------------------------------------\n\nexport async function invokeTranscribe(args: {\n endpoint: string\n sessionId: string\n provider: string\n model?: string\n diarize?: boolean\n}): Promise<{ transcribed: number; failed: number; remaining: number }> {\n const { data, error } = await client().functions.invoke(args.endpoint, {\n body: {\n action: 'drain',\n sessionId: args.sessionId,\n provider: args.provider,\n model: args.model,\n diarize: args.diarize,\n },\n })\n if (error) throw error\n if (data?.error) throw new Error(data.error)\n return { transcribed: data?.transcribed ?? 0, failed: data?.failed ?? 0, remaining: data?.remaining ?? 0 }\n}\n\nexport async function invokeGenerate(args: {\n endpoint: string\n sessionId: string\n templateId?: string\n templateKey?: string\n schema?: unknown\n tabOrder?: number\n}): Promise<{ generationId: string; markdown: string }> {\n const { data, error } = await client().functions.invoke(args.endpoint, {\n body: {\n sessionId: args.sessionId,\n templateId: args.templateId,\n templateKey: args.templateKey,\n schema: args.schema,\n tabOrder: args.tabOrder ?? 0,\n },\n })\n if (error) throw error\n if (data?.error) throw new Error(data.error)\n return { generationId: data.generationId, markdown: data.markdown }\n}\n","import type { ScribeSttConfig } from '../types'\n\n// ---------------------------------------------------------------------------\n// Validação da configuração de STT\n// ---------------------------------------------------------------------------\n//\n// Separada de `transport.ts` de propósito: aquele arquivo importa a conexão da\n// plataforma e só existe no browser, e esta regra precisa ser exercitável num\n// teste puro. É a regra que impede o modo de falha mais caro do plugin, então\n// ela não pode depender de alguém abrir uma tela para ser verificada.\n\n/** A configuração pede o broker da plataforma? É o default. */\nexport function usesFayzTransport(stt: Pick<ScribeSttConfig, 'transport'>): boolean {\n return (stt.transport ?? 'fayz') === 'fayz'\n}\n\n/**\n * Lança se a configuração não descreve um caminho de transcrição próprio.\n *\n * Por que lançar na construção do plugin, e não degradar: sem transporte\n * configurado a única alternativa a mão seria o reconhecedor nativo do browser,\n * que envia o áudio do microfone para servidores do Google. Num atendimento\n * isso é um operador não declarado processando dado sensível — sem DPA, sem\n * base documentada de transferência internacional, e sem como honrar um pedido\n * de eliminação, porque não dá para pedir a deleção do que você nunca soube que\n * foi recebido.\n *\n * Falhar alto no boot é infinitamente melhor que degradar em silêncio para o\n * caminho que ninguém escolheu.\n */\nexport function assertSttConfig(stt: ScribeSttConfig | undefined): asserts stt is ScribeSttConfig {\n if (!stt) {\n throw new Error('[plugin-scribe] a opção `stt` é obrigatória.')\n }\n\n if (!usesFayzTransport(stt)) {\n if (!stt.endpoint) {\n throw new Error(\n '[plugin-scribe] com transport: \"edge\" o stt.endpoint é obrigatório. Sem um endpoint ' +\n 'próprio de transcrição o áudio do atendimento sairia para um terceiro não declarado — ' +\n 'o plugin recusa a subir.',\n )\n }\n return\n }\n\n // Deepgram não passa pelo broker da plataforma. Cair para OpenAI em silêncio\n // mudaria custo, diarização e qualidade em pt-BR sem ninguém pedir — então é\n // erro de configuração, alto e no boot.\n if (stt.provider !== 'openai') {\n throw new Error(\n `[plugin-scribe] transport: \"fayz\" só fala com provider: \"openai\" (recebido: \"${stt.provider}\"). ` +\n 'Para Deepgram use transport: \"edge\" com uma edge function própria.',\n )\n }\n}\n","import { resolveFayzAgentConnection } from '@fayz-ai/admin'\nimport type { ScribeSttConfig, ScribeTransportKind } from '../types'\nimport { usesFayzTransport } from './config'\n\n// ---------------------------------------------------------------------------\n// Where the model lives\n// ---------------------------------------------------------------------------\n//\n// Audio→text and transcript→document both need a provider credential, so\n// neither can run in the browser. There are two legitimate homes for that\n// credential, and this file is the seam that makes the choice configuration\n// rather than a rewrite:\n//\n// 'fayz' — the platform API, the SAME broker the app assistant already uses.\n// Authenticates with the project's publishable key and holds the\n// provider key on its side: no secret, no deploy. The default.\n//\n// 'edge' — the app's own Supabase edge function, key in the project secrets.\n// For running off-platform, or for a provider the broker does not\n// offer (today: Deepgram).\n//\n// There is deliberately no third path: with no transport configured the plugin\n// refuses to boot (see the invariant in `createScribePlugin`). The silent\n// alternative would be the browser's native recognizer, which ships session\n// audio to an undeclared third party.\n\nexport interface SttClipResult {\n text: string\n words: Array<{ w: string; s: number; e: number; c?: number; sp?: number }>\n /** 0 when the provider does not report it — callers read 0 as unknown. */\n durationMs: number\n confidence: number\n /** `provider:model`, so the stored row says what produced it. */\n provider: string\n}\n\nexport interface CompletionResult {\n content: string\n model: string\n tokensIn?: number\n tokensOut?: number\n}\n\nexport interface ScribeTransport {\n kind: ScribeTransportKind\n /**\n * Transcribes ONE clip. The caller owns the queue, the retry and the state;\n * the transport only knows how to talk to a model.\n */\n transcribeClip(args: {\n audio: Blob\n mimeType: string\n locale: string\n model?: string\n diarize?: boolean\n /** Tail of the previous clip; rescues the word cut at the seam. */\n context?: string\n }): Promise<SttClipResult>\n /** One completion: no conversation, no history, nothing persisted. */\n complete(args: {\n system: string\n user: string\n model?: string\n temperature?: number\n maxTokens?: number\n }): Promise<CompletionResult>\n}\n\n// ---------------------------------------------------------------------------\n// fayz — the platform broker\n// ---------------------------------------------------------------------------\n\nconst KEY_HEADER = 'x-fayz-agent-key'\n\nfunction createFayzTransport(): ScribeTransport | null {\n const connection = resolveFayzAgentConnection()\n if (!connection) return null\n\n const base = `${connection.apiBaseUrl.replace(/\\/+$/, '')}/public/projects/${connection.projectId}/agents`\n\n async function readError(res: Response): Promise<never> {\n let detail = ''\n try {\n const body = (await res.json()) as { error?: string }\n detail = body?.error ?? ''\n } catch {\n detail = await res.text().catch(() => '')\n }\n throw new Error(`fayz ${res.status}${detail ? `: ${detail}` : ''}`)\n }\n\n return {\n kind: 'fayz',\n\n async transcribeClip({ audio, mimeType, locale, model, context }) {\n const form = new FormData()\n // The filename carries the extension, and the provider picks its demuxer\n // from it. Sending `blob` makes a perfectly valid webm come back as\n // \"unsupported format\".\n form.append('file', audio, `clip.${extOf(mimeType)}`)\n if (model) form.append('model', model)\n form.append('language', locale)\n if (context) form.append('prompt', context)\n\n const res = await fetch(`${base}/transcribe`, {\n method: 'POST',\n headers: { [KEY_HEADER]: connection.publishableKey },\n body: form,\n })\n if (!res.ok) await readError(res)\n\n const json = (await res.json()) as {\n text?: string\n words?: Array<{ w: string; s: number; e: number }>\n durationMs?: number\n provider?: string\n }\n return {\n text: json.text ?? '',\n words: json.words ?? [],\n durationMs: json.durationMs ?? 0,\n // The broker returns no aggregate confidence; 0 means \"not reported\",\n // same as durationMs.\n confidence: 0,\n provider: json.provider ?? 'fayz',\n }\n },\n\n async complete({ system, user, model, temperature, maxTokens }) {\n const res = await fetch(`${base}/complete`, {\n method: 'POST',\n headers: { [KEY_HEADER]: connection.publishableKey, 'Content-Type': 'application/json' },\n body: JSON.stringify({ system, user, model, temperature, maxTokens }),\n })\n if (!res.ok) await readError(res)\n\n const json = (await res.json()) as {\n content?: string\n model?: string\n tokensIn?: number\n tokensOut?: number\n }\n return {\n content: json.content ?? '',\n model: json.model ?? (model ?? 'auto'),\n tokensIn: json.tokensIn,\n tokensOut: json.tokensOut,\n }\n },\n }\n}\n\nfunction extOf(mimeType: string): string {\n const type = mimeType.split(';')[0]?.trim().toLowerCase() ?? ''\n if (type === 'audio/ogg') return 'ogg'\n if (type === 'audio/mp4' || type === 'audio/mpeg') return 'm4a'\n if (type === 'audio/mpga' || type === 'audio/mp3') return 'mp3'\n if (type === 'audio/wav' || type === 'audio/x-wav') return 'wav'\n return 'webm'\n}\n\n// ---------------------------------------------------------------------------\n// Resolution\n// ---------------------------------------------------------------------------\n\n/**\n * The transport actually in use, given what is configured AND what runtime\n * offers.\n *\n * `transport: 'fayz'` with no resolved connection returns null rather than\n * throwing: an app built outside the platform container has no `VITE_FAYZ_*`,\n * and the right answer there is a screen saying transcription is unavailable —\n * not the whole app failing to boot over a tab nobody opened. `'edge'` never\n * reaches here.\n */\nexport function resolveScribeTransport(stt: ScribeSttConfig): ScribeTransport | null {\n if (!usesFayzTransport(stt)) return null\n return createFayzTransport()\n}\n\nexport { usesFayzTransport } from './config'\n","// Strips what the STT invents when there is nothing to transcribe.\n//\n// Whisper learned from internet subtitles and kept their habits: over silence\n// it returns the phrases that end videos — \"Obrigado por assistir\", \"Legendas\n// pela comunidade\", a stray \"e aí\". In a clinical record that is not noise, it\n// is a sentence attributed to a patient who never said it.\n//\n// The main defence is not asking at all (`SILENCE_PEAK` in runtime/capture.ts);\n// this is the second layer, for clips with a little room tone.\n//\n// RULE: only remove when the hallucination is the WHOLE clip. Cutting \"e aí\"\n// out of real speech would mutilate the record, and a missing sentence is worse\n// than noise — noise the reader discounts, absence they cannot guess.\n\n/** Alone in a clip, these are model artifacts and not speech. */\nconst ARTIFACTS = [\n 'obrigado por assistir',\n 'obrigado por assistirem',\n 'obrigada por assistir',\n 'legendas pela comunidade amara.org',\n 'legendas pela comunidade',\n 'inscreva-se no canal',\n 'até o próximo vídeo',\n 'tchau tchau',\n 'thanks for watching',\n 'subtitles by the amara.org community',\n 'thank you',\n]\n\n/** Interjections that carry no content on their own. */\nconst FILLERS = ['e aí', 'e ai', 'aí', 'ai', 'ah', 'ahn', 'hmm', 'hm', 'uhum', 'ué', 'né', 'ó', 'ok', 'tá']\n\n/** Word-level fillers, for the repetition test only. Includes \"e\" because the\n * typical hallucination is \"e aí, e aí, e aí\". */\nconst WORD_FILLERS = new Set([...FILLERS.flatMap((f) => f.split(' ')), 'e'])\n\nfunction normalize(text: string): string {\n return text\n .toLowerCase()\n .normalize('NFD')\n .replace(/[̀-ͯ]/g, '')\n .replace(/[.,!?;:…\"'\\s]+/g, ' ')\n .trim()\n}\n\n/** `null` when the whole clip is artifact — the caller stores empty rather\n * than a sentence nobody said. */\nexport function cleanTranscriptText(text: string | null | undefined): string | null {\n if (!text) return null\n const trimmed = text.trim()\n if (!trimmed) return null\n\n const flat = normalize(trimmed)\n if (!flat) return null\n\n if (ARTIFACTS.some((a) => flat === normalize(a))) return null\n if (FILLERS.includes(flat)) return null\n\n // Pure repetition is the same artifact in disguise: when every word is\n // filler, there is no content.\n const words = flat.split(' ').filter(Boolean)\n if (words.length > 0 && words.length <= 8 && words.every((w) => WORD_FILLERS.has(w))) return null\n\n return trimmed\n}\n","import { getSupabaseClientOptional } from '@fayz-ai/core'\nimport { T, AUDIO_BUCKET } from '../data/tables'\nimport type { ScribeSttConfig } from '../types'\nimport { resolveScribeTransport, type ScribeTransport } from './transport'\nimport { cleanTranscriptText } from './transcript-clean'\n\n// ---------------------------------------------------------------------------\n// Client-side transcription drain\n// ---------------------------------------------------------------------------\n//\n// On the `fayz` transport this runs INSTEAD of the edge function, because that\n// function did two independent things: orchestrate (claim the pending segment,\n// download the audio, mark state, recompute counters, decide `ready`) and talk\n// to the STT provider. Only the second needed a credential. The browser could\n// always do the first: RLS scopes every row and bucket object to the user's\n// tenant, and this segment's audio was recorded by this very tab.\n//\n// What it buys: no secret in the app project and no edge function to deploy —\n// a real block, not a hypothetical one (plugin-agenda's `google-calendar-sync`\n// sits in the repo and was never deployed, for exactly that reason).\n//\n// What it costs: transcription only advances with a tab open. Less than it\n// sounds, because the UPLOAD was already the browser's job and\n// `stt_state='pending'` is durable — any later boot drains what is left.\n//\n// Concurrency: `stt_state='running'` is written BEFORE the download, so two\n// tabs looking at the same session do not pay to transcribe it twice.\n\n/** How many clips transcribe in parallel. */\nconst CONCURRENCY = 3\n/** Attempts before giving up on a clip and moving on. */\nconst MAX_ATTEMPTS = 4\n/** How much of the previous clip is passed as a context hint. */\nconst CONTEXT_TAIL_CHARS = 200\n\nexport interface DrainResult {\n transcribed: number\n failed: number\n remaining: number\n}\n\nfunction client(): any {\n const supabase = getSupabaseClientOptional() as any\n if (!supabase) throw new Error('Supabase not initialized')\n return supabase\n}\n\n/**\n * Transcribes everything pending in a session. Returns `null` when the\n * configured transport is not the browser's — the caller falls back to the edge\n * function.\n */\nexport async function drainSessionViaTransport(\n sessionId: string,\n stt: ScribeSttConfig,\n): Promise<DrainResult | null> {\n const transport = resolveScribeTransport(stt)\n if (!transport) return null\n\n const supabase = client()\n\n const { data: session } = await supabase\n .from(T.sessions)\n .select('id, locale, mime_type, status, ended_at')\n .eq('id', sessionId)\n .maybeSingle()\n if (!session) throw new Error('session not found')\n\n const { data: pending } = await supabase\n .from(T.segments)\n .select('session_id, seg_index, storage_path, stt_attempts')\n .eq('session_id', sessionId)\n .eq('upload_state', 'uploaded')\n .in('stt_state', ['pending', 'failed'])\n .lt('stt_attempts', MAX_ATTEMPTS)\n .order('seg_index', { ascending: true })\n\n if (!pending || pending.length === 0) {\n // Recount only after something changed, or once the session ended and the\n // last clip may have landed on an earlier pass. Doing it on every idle pass\n // was two queries per poll, forever, to reconfirm an untouched number.\n if (session.ended_at) {\n await recomputeCounters(supabase, sessionId)\n await maybeMarkReady(supabase, sessionId)\n }\n return { transcribed: 0, failed: 0, remaining: 0 }\n }\n\n let transcribed = 0\n let failed = 0\n\n for (let i = 0; i < pending.length; i += CONCURRENCY) {\n const batch = pending.slice(i, i + CONCURRENCY)\n const results = await Promise.all(\n batch.map((seg: any) =>\n transcribeOne(supabase, transport, seg, {\n locale: session.locale ?? stt.locale ?? 'pt-BR',\n mimeType: session.mime_type ?? 'audio/webm',\n model: stt.model,\n }),\n ),\n )\n transcribed += results.filter(Boolean).length\n failed += results.filter((ok) => !ok).length\n }\n\n await recomputeCounters(supabase, sessionId)\n await maybeMarkReady(supabase, sessionId)\n\n const { count } = await supabase\n .from(T.segments)\n .select('seg_index', { count: 'exact', head: true })\n .eq('session_id', sessionId)\n .eq('upload_state', 'uploaded')\n .in('stt_state', ['pending', 'failed'])\n .lt('stt_attempts', MAX_ATTEMPTS)\n\n return { transcribed, failed, remaining: count ?? 0 }\n}\n\nasync function transcribeOne(\n supabase: any,\n transport: ScribeTransport,\n seg: { session_id: string; seg_index: number; storage_path: string | null; stt_attempts: number | null },\n opts: { locale: string; mimeType: string; model?: string },\n): Promise<boolean> {\n if (!seg.storage_path) return false\n\n const where = (q: any) => q.eq('session_id', seg.session_id).eq('seg_index', seg.seg_index)\n\n try {\n await where(\n supabase.from(T.segments).update({\n stt_state: 'running',\n stt_attempts: (seg.stt_attempts ?? 0) + 1,\n updated_at: new Date().toISOString(),\n }),\n )\n\n const { data: file, error: dlErr } = await supabase.storage\n .from(AUDIO_BUCKET)\n .download(seg.storage_path)\n if (dlErr || !file) throw new Error(`download failed: ${dlErr?.message ?? 'no body'}`)\n\n const context = await previousTail(supabase, seg.session_id, seg.seg_index)\n const result = await transport.transcribeClip({\n audio: file as Blob,\n mimeType: opts.mimeType,\n locale: opts.locale,\n model: opts.model,\n context,\n })\n\n await where(\n supabase.from(T.segments).update({\n stt_state: 'done',\n stt_provider: result.provider,\n stt_error: null,\n // A clip that came back as pure silence artifact stores empty, not as\n // something the patient said. See lib/transcript-clean.ts.\n text: cleanTranscriptText(result.text),\n words: result.words,\n confidence: result.confidence,\n // AUTHORITATIVE duration: measured in the audio, never counted in JS.\n // A provider that does not report it returns 0, and 0 becomes NULL\n // rather than a zero-minute session.\n duration_ms: result.durationMs > 0 ? result.durationMs : null,\n updated_at: new Date().toISOString(),\n }),\n )\n return true\n } catch (err) {\n await where(\n supabase.from(T.segments).update({\n stt_state: 'failed',\n stt_error: String((err as Error)?.message ?? err).slice(0, 500),\n updated_at: new Date().toISOString(),\n }),\n )\n return false\n }\n}\n\n/** Tail of the previous clip — the context hint that rescues the word at the seam. */\nasync function previousTail(supabase: any, sessionId: string, segIndex: number): Promise<string | undefined> {\n if (segIndex === 0) return undefined\n const { data } = await supabase\n .from(T.segments)\n .select('text')\n .eq('session_id', sessionId)\n .eq('seg_index', segIndex - 1)\n .maybeSingle()\n const text = data?.text as string | undefined\n return text ? text.slice(-CONTEXT_TAIL_CHARS) : undefined\n}\n\n/**\n * Recomputes counters from the segments — same reason as the pump's RPC:\n * derive, never increment, so an idempotent replay converges.\n */\nasync function recomputeCounters(supabase: any, sessionId: string): Promise<void> {\n const { data: segs } = await supabase\n .from(T.segments)\n .select('stt_state, duration_ms, text, gap')\n .eq('session_id', sessionId)\n if (!segs) return\n\n await supabase\n .from(T.sessions)\n .update({\n transcribed_segment_count: segs.filter((s: any) => s.stt_state === 'done').length,\n audio_duration_ms: segs\n .filter((s: any) => !s.gap)\n .reduce((acc: number, s: any) => acc + (s.duration_ms ?? 0), 0),\n transcript_chars: segs.reduce((acc: number, s: any) => acc + (s.text?.length ?? 0), 0),\n updated_at: new Date().toISOString(),\n })\n .eq('id', sessionId)\n}\n\n/**\n * `ready` covers clips that exhausted their attempts: 78 of 80 transcribed is\n * usable, and pinning the session in `transcribing` forever over the other two\n * would be the worse of both worlds.\n */\nasync function maybeMarkReady(supabase: any, sessionId: string): Promise<void> {\n const { data: session } = await supabase\n .from(T.sessions)\n .select('status, ended_at')\n .eq('id', sessionId)\n .maybeSingle()\n if (!session?.ended_at) return\n if (!['uploading', 'transcribing', 'recording', 'paused', 'interrupted'].includes(session.status)) return\n\n const { data: segs } = await supabase\n .from(T.segments)\n .select('stt_state, stt_attempts, upload_state')\n .eq('session_id', sessionId)\n if (!segs || segs.length === 0) return\n\n const outstanding = segs.filter(\n (s: any) =>\n s.upload_state === 'uploaded' &&\n ['pending', 'running', 'failed'].includes(s.stt_state) &&\n (s.stt_attempts ?? 0) < MAX_ATTEMPTS,\n )\n await supabase\n .from(T.sessions)\n .update({\n status: outstanding.length === 0 ? 'ready' : 'transcribing',\n updated_at: new Date().toISOString(),\n })\n .eq('id', sessionId)\n}\n","import { create } from 'zustand'\nimport { fetchSegments } from '../data/supabase'\nimport { drainSessionViaTransport } from './drain'\nimport type { ScribeSegment, ScribeSttConfig } from '../types'\n\n// The running session's transcript, in one place.\n//\n// Two consumers need it and neither can own it: the panel (alive only while its\n// tab is open) and the agent context (needed with the tab closed, or asking\n// \"what did he say so far?\" answers about nothing).\n//\n// THE DRAIN RUNS HERE TOO, which is the difference between text appearing and\n// nothing appearing: on the `fayz` transport the browser is the transcriber\n// (lib/drain.ts), and that caller only existed inside the session PAGE — so\n// during a live encounter clips uploaded and sat there, pending, forever.\n//\n// Cadence is per CLIP, not per word: 30s chunks transcribed after upload.\n// Word-by-word needs a streaming STT transport, not a smaller interval.\n\n// Adaptive: a clip closes every 30s, so most of the time there is nothing to\n// fetch. A fixed 6s poll spent five passes discovering that nothing changed.\nconst POLL_BUSY_MS = 3000\nconst POLL_IDLE_MS = 15000\n\ninterface LiveTranscriptState {\n sessionId: string | null\n segments: ScribeSegment[]\n}\n\nexport const useLiveTranscript = create<LiveTranscriptState>(() => ({\n sessionId: null,\n segments: [],\n}))\n\nlet timer: ReturnType<typeof setTimeout> | null = null\nlet polling: string | null = null\nlet sttConfig: ScribeSttConfig | null = null\nlet draining = false\n/** Last result's signature — avoids rewriting the store with no change. */\nlet lastSignature = ''\n\n/** @returns quanto esperar até a próxima volta. */\nasync function load(sessionId: string): Promise<number> {\n let pendingWork = false\n\n // Drain before reading. One pass at a time: two concurrent rounds would have\n // two clients transcribing (and paying for) the same clip.\n if (sttConfig && !draining) {\n draining = true\n try {\n const result = await drainSessionViaTransport(sessionId, sttConfig)\n // Pending work means come back soon; nothing pending means the next clip\n // is still being recorded.\n pendingWork = !!result && (result.transcribed > 0 || result.remaining > 0)\n } catch {\n // A próxima volta tenta de novo; `stt_state` é durável.\n } finally {\n draining = false\n }\n }\n\n try {\n const segments = await fetchSegments(sessionId)\n // A late response from a previous session must not repaint this one.\n if (polling === sessionId) {\n const signature = segments.map((s) => `${s.segIndex}:${s.text ? s.text.length : 0}`).join('|')\n // No change, no setState: each write wakes the panel, the agent context\n // and the composer to repaint identical text.\n if (signature !== lastSignature) {\n lastSignature = signature\n useLiveTranscript.setState({ sessionId, segments })\n }\n }\n } catch {\n // A failed read never clears the screen — the previous text is still the\n // best thing we have.\n }\n\n return pendingWork ? POLL_BUSY_MS : POLL_IDLE_MS\n}\n\n/** Self-scheduled rather than `setInterval`: the delay depends on what the\n * previous pass found. */\nfunction schedule(sessionId: string, delay: number): void {\n timer = setTimeout(() => {\n if (polling !== sessionId) return\n void load(sessionId).then((next) => {\n if (polling === sessionId) schedule(sessionId, next)\n })\n }, delay)\n}\n\n/** Start (or switch) watching. `null` stops. */\nexport function watchTranscript(sessionId: string | null, stt?: ScribeSttConfig): void {\n if (stt) sttConfig = stt\n if (polling === sessionId) return\n polling = sessionId\n if (timer) {\n clearTimeout(timer)\n timer = null\n }\n lastSignature = ''\n if (!sessionId) {\n useLiveTranscript.setState({ sessionId: null, segments: [] })\n return\n }\n useLiveTranscript.setState({ sessionId, segments: [] })\n void load(sessionId).then((next) => {\n if (polling === sessionId) schedule(sessionId, next)\n })\n}\n\n/** Flat text for the agent context. Holes are declared: text that reads as\n * continuous when it is not is worth less than an admitted gap. */\nexport function transcriptText(): string {\n return useLiveTranscript\n .getState()\n .segments.map((s) => (s.gap ? '[trecho perdido]' : (s.text ?? '').trim()))\n .filter(Boolean)\n .join(' ')\n}\n","import { openAssistantAs } from '@fayz-ai/admin'\nimport type { AgentRole } from '@fayz-ai/core'\n\n// ---------------------------------------------------------------------------\n// Como uma gravação começa — um caminho só, de onde quer que se peça.\n//\n// O botão na ficha do cliente, a oferta na thread da auxiliar e qualquer tela\n// futura chamam a MESMA função. Antes cada ponto de partida repetia a sequência\n// (abrir o assistente, disparar o evento) e era ali que a divergência nascia:\n// um deles abria o painel, o outro não, e o consentimento aparecia sobre uma\n// tela que ia sumir.\n//\n// O evento continua existindo por baixo porque ele é o desacoplamento que\n// permite a agenda ganhar um botão de gravar sem depender do scribe.\n// ---------------------------------------------------------------------------\n\nexport interface ScribeStartEventDetail {\n subjectId?: string\n subjectName?: string\n appointmentId?: string\n templateKeys?: string[]\n}\n\nexport const SCRIBE_START_EVENT = 'scribe:start'\n\n/**\n * Quem, na equipe do app, é dona da gravação.\n *\n * O plugin declara o PAPEL; o app dá o nome. No salão ela é a Auxiliar de\n * Atendimento, numa clínica pode ser outra pessoa — e nenhum dos dois toca em\n * uma linha deste plugin para isso.\n */\nexport const SCRIBE_AGENT_ROLE: AgentRole = 'encounter'\n\n/** Lets any screen start a session without importing the plugin. */\nexport function requestScribeStart(detail: ScribeStartEventDetail): void {\n window.dispatchEvent(new CustomEvent(SCRIBE_START_EVENT, { detail }))\n}\n\n/**\n * Onde uma gravação é lida, por padrão: DENTRO da ficha de quem foi atendido.\n *\n * Uma gravação não é um lugar — é uma coisa que o cadastro de uma pessoa\n * contém, como um documento ou uma fatura. A rota solta `#/scribe/:id` trocava\n * a página inteira e levava junto o nome do cliente, as outras abas e o caminho\n * de volta; sobra só para a sessão que ainda não tem titular, que é justamente\n * o que a captura `standalone` produz.\n *\n * O id do titular É o id do cadastro: a tabela de extensão usa o id do\n * arquétipo como chave primária (ver createArchetypeProvider), então\n * `clients.id` e a linha de `people` são o mesmo uuid.\n */\nexport function scribeSessionPath(args: {\n sessionId: string\n subjectId?: string\n /** Nome da entidade onde essa pessoa é cadastrada ('clients', 'patients'). */\n subjectEntity: string\n}): string {\n const { sessionId, subjectId, subjectEntity } = args\n return subjectId ? `/${subjectEntity}/${subjectId}/scribe/${sessionId}` : `/scribe/${sessionId}`\n}\n\n/**\n * Começa uma gravação COM a colega que a conduz: levanta o painel na thread\n * dela e só então pede o consentimento, ali dentro.\n *\n * Quando o app tem a auxiliar no elenco mas ninguém a contratou, o painel abre\n * na apresentação dela e nada é gravado — prometer a gravação e cobrar depois\n * seria entregar o trabalho pago enquanto a tela ainda o está vendendo.\n */\nexport function startScribeSession(detail: ScribeStartEventDetail = {}): void {\n if (!openAssistantAs(SCRIBE_AGENT_ROLE)) return\n requestScribeStart(detail)\n}\n","import * as React from 'react'\nimport { toast } from '@fayz-ai/ui/primitives'\nimport {\n useAuth,\n useAgentSurface,\n usePublishAssistantActivity,\n useRegisterAssistantLauncher,\n type AssistantActivity,\n type AssistantActivityAction,\n} from '@fayz-ai/admin'\nimport { transcriptText, watchTranscript } from '../lib/live-transcript'\nimport { useScribeStore, formatElapsed } from '../store'\nimport {\n beginSession,\n discardSession,\n dismissOrphan,\n endSession,\n getCaptureStream,\n getSnapshot,\n isCaptureSupported,\n orphanDurationMs,\n pauseSession,\n resumeSession,\n} from '../runtime'\nimport {\n SCRIBE_AGENT_ROLE,\n SCRIBE_START_EVENT,\n requestScribeStart,\n type ScribeStartEventDetail,\n} from '../lib/start'\nimport type { ConsentMode, ScribeLabels, ScribeRetentionConfig, ScribeSttConfig } from '../types'\n\n// The recording, from consent to document, seen from inside the assistant.\n//\n// A session is the app's longest-lived act: it survives every navigation, and\n// the screen it began on is gone seconds later. So it does not belong to a\n// screen — it belongs to the one surface that never unmounts and already\n// carries who is doing it. Scribe publishes what is happening (label, elapsed,\n// state, what to do about it) and the shell draws the FAB pill, the panel and\n// the strip. A long import or an agent task publishes the same contract and\n// gets the same face.\n//\n// The consent gate is part of that, and used to be a Modal: a decision on one\n// surface, its consequence on another, with the panel already open behind the\n// dialog. It is now the `pending` state of the same activity — see\n// AssistantActivity.confirm.\n//\n// `elapsedMs` is rounded to the second on purpose: the runtime recomputes it\n// every frame, and publishing every frame would wake every subscriber 60x a\n// second to paint a number that changes once.\n\n/** Repaint driver — the elapsed value lives in the runtime, not in React. */\nfunction useSecondTicker(active: boolean): void {\n const [, force] = React.useReducer((n: number) => n + 1, 0)\n React.useEffect(() => {\n if (!active) return\n // Sampled at 250ms to paint a 1s clock: a 1000ms interval drifts against\n // the second boundary, and a sample that lands just the wrong side of it\n // reads the same second twice and then skips one. Publishing still happens\n // once per second — the activity key only changes when the rounded value\n // does.\n const timer = setInterval(() => {\n if (document.visibilityState === 'visible') force()\n }, 250)\n const onVisible = () => force()\n document.addEventListener('visibilitychange', onVisible)\n return () => {\n clearInterval(timer)\n document.removeEventListener('visibilitychange', onVisible)\n }\n }, [active])\n}\n\nexport interface ScribeAssistantBridgeProps {\n labels: ScribeLabels\n retention: ScribeRetentionConfig\n stt: ScribeSttConfig\n locale: string\n onOpenSession?: (sessionId: string, subjectId?: string) => void\n}\n\n// \"Resume\" expires. After ~30 minutes the encounter is over, and continuing the\n// same recording would join two distinct moments as if they were one.\nconst RESUMABLE_WINDOW_MS = 30 * 60 * 1000\n\nexport function ScribeAssistantBridge({\n labels,\n retention,\n stt,\n locale,\n onOpenSession,\n}: ScribeAssistantBridgeProps) {\n const { user } = useAuth()\n const state = useScribeStore((s) => s.state)\n const sessionId = useScribeStore((s) => s.sessionId)\n const subjectId = useScribeStore((s) => s.subjectId)\n const subjectName = useScribeStore((s) => s.subjectName)\n // Sessions a crash or a reload left behind, found at boot.\n const orphans = useScribeStore((s) => s.orphans)\n\n const live = state === 'recording' || state === 'paused' || state === 'interrupted'\n useSecondTicker(state === 'recording')\n\n // Watching runs in the mount that never unmounts, not in the panel: tied to\n // an open tab, \"what did he say so far?\" would answer about nothing.\n React.useEffect(() => {\n // The STT config rides along: on the `fayz` transport this browser is the\n // transcriber, so watching and transcribing are the same tick.\n watchTranscript(live ? sessionId : null, stt)\n }, [live, sessionId, stt])\n\n // `getSnapshot()` and not the store's `elapsedMs`: that field only changes\n // when the runtime emits, so a per-second read saw the same number twice and\n // the clock jumped 2s. Outside a selector — a continuously moving value would\n // wake every consumer of the store.\n const elapsedMs = live ? Math.floor(getSnapshot().elapsedMs / 1000) * 1000 : 0\n\n // The assistant carries a session while it is HAPPENING, and hands the app\n // back the moment it is not. Everything downstream of the audio — reading the\n // transcript, writing the summary, generating and signing the document — is\n // work with a page, and a 380px column beside the conversation is the worst\n // place in the product to do it.\n //\n // A start that has been asked for and not yet agreed to. `null` the rest of\n // the time, which is why the consent state costs nothing when nobody records.\n const [pending, setPending] = React.useState<ScribeStartEventDetail | null>(null)\n\n const start = React.useCallback(\n async (detail: ScribeStartEventDetail, consentMode: ConsentMode) => {\n setPending(null)\n if (!user?.id) {\n toast.error('Faça login para gravar um atendimento')\n return\n }\n const result = await beginSession({\n userId: user.id,\n subjectId: detail.subjectId,\n subjectName: detail.subjectName,\n appointmentId: detail.appointmentId,\n templateKeys: detail.templateKeys,\n locale,\n consentMode,\n })\n // The reason is always actionable (permission denied, no space, no mic);\n // a generic \"recording failed\" hides that a click would fix it.\n if (!result.ok) toast.error(`Não foi possível iniciar: ${result.reason}`)\n },\n [user?.id, locale],\n )\n\n React.useEffect(() => {\n const onStart = (ev: Event) => {\n const detail = (ev as CustomEvent<ScribeStartEventDetail>).detail ?? {}\n // With consent required, the gate comes BEFORE the first byte. Whether\n // there is a gate at all is the vertical's call, never the screen's.\n if (retention.requireConsent) setPending(detail)\n else void start(detail, retention.defaultConsentMode)\n }\n window.addEventListener(SCRIBE_START_EVENT, onStart)\n return () => window.removeEventListener(SCRIBE_START_EVENT, onStart)\n }, [retention.requireConsent, retention.defaultConsentMode, start])\n\n const activity: AssistantActivity | null = React.useMemo(() => {\n // Asked for, not yet agreed to. Ranked below a live one by the shell, so a\n // second request cannot talk over a recording already running.\n if (pending && !live) {\n const who = pending.subjectName\n return {\n id: 'scribe.session',\n kind: 'recording',\n state: 'pending',\n label: who ? `${labels.sessionSingular} · ${who}` : labels.sessionSingular,\n // A ticked box consents for nobody — the holder does. What it records\n // is the professional ASSERTING they obtained it, which is the fact\n // that has to be provable later (LGPD art. 5º II), stamped on the same\n // row as the audio rather than reconstructed afterwards.\n confirm: {\n acknowledge: `Informei ${who ?? 'o titular'} de que este atendimento será gravado e obtive o consentimento.`,\n note:\n 'O áudio fica guardado de forma privada e pode ser apagado quando você quiser — a transcrição e o documento permanecem.',\n },\n actions: [\n { intent: 'cancel', label: 'Cancelar', run: () => setPending(null) },\n {\n intent: 'start',\n label: labels.start,\n primary: true,\n run: () => void start(pending, retention.defaultConsentMode),\n },\n ],\n }\n }\n\n if (live) {\n const actions: AssistantActivityAction[] = [\n state === 'paused'\n ? { intent: 'resume', label: labels.resume, run: () => void resumeSession() }\n : { intent: 'pause', label: labels.pause, run: () => void pauseSession() },\n {\n intent: 'stop',\n label: labels.finish,\n primary: true,\n // Stopping ends the assistant's part of this, and the app opens the\n // session. Upload and transcription were never waiting on a click —\n // the pump carries them — so an \"encerrado, gerar?\" step in between\n // was a screen asking permission for work already in progress, on the\n // surface least able to do the rest of it.\n run: async () => {\n const id = sessionId\n await endSession()\n if (id) onOpenSession?.(id, subjectId)\n },\n },\n ]\n return {\n id: 'scribe.session',\n kind: 'recording',\n label: subjectName ? `${labels.sessionSingular} · ${subjectName}` : labels.sessionSingular,\n state: state === 'paused' ? 'paused' : 'running',\n elapsedMs,\n actions,\n // Lent, not duplicated: the composer meter draws THIS audio.\n stream: getCaptureStream,\n }\n }\n\n // Recovery is the same report as a live recording — what was captured, how\n // long it ran, what to do with it — so it belongs in the same place. It\n // used to be an amber banner pushing every page down; the audio was never\n // waiting on that decision, only the document was.\n const orphan = orphans[0]\n if (orphan) {\n const canResume = Date.now() - orphan.lastTickEpochMs < RESUMABLE_WINDOW_MS\n const actions: AssistantActivityAction[] = []\n if (canResume) {\n actions.push({ intent: 'resume', label: labels.resume, run: () => void resumeSession() })\n }\n actions.push({\n intent: 'stop',\n label: labels.finish,\n primary: true,\n run: async () => {\n const id = await endSession()\n dismissOrphan(orphan.id)\n if (id) onOpenSession?.(id, orphan.subjectId)\n },\n })\n // Soft discard: `abandoned`, not deleted. \"Discard\" almost always means\n // \"I don't want the document\", and losing an encounter to a misclick has\n // no undo.\n actions.push({\n intent: 'discard',\n label: labels.discard,\n run: () => void discardSession(orphan.id),\n })\n return {\n id: 'scribe.session',\n kind: 'recording',\n // Not `orphan.accumulatedMs` — see orphanDurationMs: older sessions sit\n // at 0, and half an hour of audio announced itself as 00:00.\n elapsedMs: orphanDurationMs(orphan),\n label: orphan.subjectName\n ? `${labels.sessionSingular} interrompido · ${orphan.subjectName}`\n : `${labels.sessionSingular} interrompido`,\n state: 'paused',\n actions,\n }\n }\n\n return null\n }, [\n live, state, elapsedMs, subjectId, subjectName, sessionId, orphans, labels, onOpenSession,\n pending, start, retention.defaultConsentMode,\n ])\n\n usePublishAssistantActivity(activity)\n\n // The recording is CONTEXT, not just an indicator: without this, asking whose\n // session it is — in the panel showing its own running clock — answers \"I\n // don't know\". Stable identity + ref reads, per the useAgentSurface contract:\n // describe() runs at send time, so the current second arrives without\n // re-registering on every tick.\n const liveRef = React.useRef({ live, state, subjectName, sessionId })\n liveRef.current = { live, state, subjectName, sessionId }\n const surface = React.useMemo(\n () => ({\n id: 'scribe.session',\n pluginId: 'scribe',\n describe: () => {\n const snap = liveRef.current\n if (!snap.live) return {}\n const text = transcriptText()\n return {\n title: `${labels.sessionSingular} em gravação`,\n state: {\n sessionId: snap.sessionId,\n subject: snap.subjectName ?? null,\n status: snap.state,\n elapsed: formatElapsed(getSnapshot().elapsedMs),\n // Tail-clipped: during an encounter the questions are about what\n // was just said.\n transcript: text ? text.slice(-6000) : null,\n },\n }\n },\n get instructions() {\n const snap = liveRef.current\n if (!snap.live) return undefined\n const hasText = transcriptText().length > 0\n return (\n `Há um ${labels.sessionSingular.toLowerCase()} sendo gravado agora` +\n (snap.subjectName ? ` com ${snap.subjectName}` : '') +\n '. ' +\n (hasText\n ? `A transcrição parcial está em state.transcript — use SÓ o que está lá e diga que o ` +\n `atendimento ainda está em curso quando responder sobre ele.`\n : `Nenhum trecho foi transcrito ainda: os primeiros saem cerca de 30s depois do início. ` +\n `Não invente o conteúdo.`) +\n ` O documento final fica pronto quando a gravação for finalizada.`\n )\n },\n }),\n [labels.sessionSingular],\n )\n useAgentSurface(surface)\n\n // Her own offer, on her own thread. Starting from here binds no subject: the\n // session attaches to a person later, which is what `standalone` exists for.\n const supported = isCaptureSupported()\n useRegisterAssistantLauncher(\n live || pending\n ? null\n : {\n id: 'scribe.start',\n kind: 'recording',\n role: SCRIBE_AGENT_ROLE,\n label: labels.start,\n disabled: !supported,\n disabledReason: 'Este navegador não suporta gravação de áudio',\n // Already in her thread, so this is the plain request — no need to\n // re-open the panel it is being pressed inside.\n run: () => requestScribeStart({}),\n },\n )\n\n return null\n}\n","import * as React from 'react'\nimport { ScribeRecordingPill } from './ScribeRecordingPill'\nimport { ScribeAssistantBridge } from './ScribeAssistantBridge'\nimport { wireScribeStore } from '../store'\nimport type { ScribeLabels, ScribeRetentionConfig, ScribeSttConfig } from '../types'\n\n// One topbar widget carrying everything that must be global. `shell.topbar.end`\n// is the only zone verifiably rendered and always mounted — spreading pieces\n// across zones would depend on zones nothing renders.\n//\n// What is left here is only the mounting. The session itself — consent, start,\n// the running clock, recovery of what a crash left behind — all belongs to the\n// assistant now, in one component: see ScribeAssistantBridge. This used to also\n// hold the start handler and a consent Modal, which meant a recording began in\n// two places depending on which button you pressed.\n\nexport interface ScribeShellMountProps {\n labels: ScribeLabels\n retention: ScribeRetentionConfig\n stt: ScribeSttConfig\n locale: string\n onOpenSession?: (sessionId: string, subjectId?: string) => void\n}\n\nexport function ScribeShellMount({ labels, retention, stt, locale, onOpenSession }: ScribeShellMountProps) {\n React.useEffect(() => {\n wireScribeStore()\n }, [])\n\n return (\n <>\n {/* Recording state lives in the assistant. The topbar pill survives only\n where the FAB may not be (small screens): visible proof of an ongoing\n recording is a legal obligation, not decoration. */}\n <ScribeAssistantBridge\n labels={labels}\n retention={retention}\n stt={stt}\n locale={locale}\n onOpenSession={onOpenSession}\n />\n <ScribeRecordingPill labels={labels} onOpenSession={onOpenSession} mobileOnly />\n </>\n )\n}\n\nexport { SCRIBE_START_EVENT, requestScribeStart, startScribeSession } from '../lib/start'\nexport type { ScribeStartEventDetail } from '../lib/start'\n","// ---------------------------------------------------------------------------\n// Construção do prompt. Puro, sem dependência — a edge function importa daqui\n// para que o prompt tenha UMA definição só. Duas cópias divergindo é como um\n// documento gerado no servidor deixa de bater com o que a tela prometeu.\n// ---------------------------------------------------------------------------\n\nimport type { NarrativeSchema, NarrativeSectionDef } from '../types'\n\n/**\n * Versão do prompt, gravada em `plg_scribe_generations.prompt_version`. Subir\n * este número a cada mudança de forma é o que torna possível responder \"por que\n * os documentos de março saíram diferentes dos de abril\".\n */\nexport const PROMPT_VERSION = 'scribe/narrative@1'\n\nexport interface TranscriptLine {\n startOffsetMs: number\n text: string\n gap: boolean\n speaker?: number\n}\n\n/** `mm:ss` — a marcação que o profissional usa para achar o trecho no áudio. */\nexport function formatOffset(ms: number): string {\n const total = Math.floor(ms / 1000)\n const mm = String(Math.floor(total / 60)).padStart(2, '0')\n const ss = String(total % 60).padStart(2, '0')\n return `${mm}:${ss}`\n}\n\n/**\n * Renderiza a transcrição para o modelo. Buracos entram como linha EXPLÍCITA:\n * o modelo precisa saber que houve descontinuidade, senão ele costura os dois\n * lados numa narrativa contínua e inventa a ponte.\n */\nexport function renderTranscript(lines: TranscriptLine[]): string {\n return lines\n .filter((l) => l.gap || l.text?.trim())\n .map((l) => {\n const at = formatOffset(l.startOffsetMs)\n if (l.gap) return `[${at}] (((TRECHO AUSENTE — a gravação foi interrompida aqui)))`\n const who = l.speaker !== undefined ? `Falante ${l.speaker + 1}: ` : ''\n return `[${at}] ${who}${l.text.trim()}`\n })\n .join('\\n')\n}\n\nfunction shapeInstruction(section: NarrativeSectionDef): string {\n switch (section.shape) {\n case 'bullets':\n return 'Formato: lista com hífen. Itens curtos, um fato por item.'\n case 'keyvalue':\n // Lista com rótulo em negrito, e não tabela, porque o renderer do SDK não\n // tem tabela e o print A4 sai quebrado.\n return 'Formato: lista com hífen no padrão `- **Rótulo:** valor`. Um par por linha.'\n default:\n return 'Formato: prosa corrida, sem listas.'\n }\n}\n\nexport function buildSystemPrompt(schema: NarrativeSchema): string {\n const parts: string[] = []\n\n parts.push(\n 'Você redige documentos a partir da transcrição de um atendimento gravado.',\n 'Escreva SOMENTE o que a transcrição sustenta. Você não é um assistente conversacional: sua saída é o documento, nada mais.',\n )\n\n if (schema.style) parts.push(`Estilo: ${schema.style}`)\n if (schema.outputLocale) parts.push(`Escreva o documento em ${schema.outputLocale}, mesmo que a transcrição esteja em outro idioma.`)\n\n parts.push(\n '',\n 'REGRAS INEGOCIÁVEIS:',\n '- Nunca invente fato, número, data, nome ou medida que não esteja na transcrição.',\n '- Quando a transcrição não cobre algo que a seção pede, escreva \"não informado\". Não deduza a partir do contexto.',\n '- Onde a transcrição indicar `(((TRECHO AUSENTE)))`, NÃO costure os dois lados como se fossem contínuos. Se aquilo afeta o que a seção diz, registre a lacuna.',\n '- Não dirija a palavra ao leitor, não comente o próprio trabalho, não peça confirmação.',\n )\n\n if (schema.neverInfer?.length) {\n parts.push(\n `- Estes itens NUNCA podem ser inferidos, apenas transcritos quando ditos explicitamente: ${schema.neverInfer.join(', ')}.`,\n )\n }\n\n parts.push(\n '',\n 'FORMATO DA SAÍDA (markdown restrito):',\n '- Cada seção começa com `## ` seguido exatamente do título fornecido.',\n '- Use no máximo `###` para subtítulo. Nunca `####` ou mais fundo.',\n '- PROIBIDO: tabela, link, imagem, bloco de código cercado por crases, HTML.',\n '- Permitido: parágrafo, lista com hífen, lista numerada, **negrito**, *itálico*.',\n '- Não escreva preâmbulo nem fecho. A primeira linha da resposta é o primeiro `## `.',\n )\n\n return parts.join('\\n')\n}\n\nexport function buildUserPrompt(args: {\n schema: NarrativeSchema\n transcript: string\n context?: Record<string, string | undefined>\n}): string {\n const { schema, transcript, context } = args\n const parts: string[] = []\n\n const ctx = Object.entries(context ?? {}).filter(([, v]) => v)\n if (ctx.length > 0) {\n // Contexto é DADO, resolvido do banco. Vai como referência para o modelo\n // situar o texto — nunca como algo a ser reproduzido no corpo, senão ele\n // repete o cabeçalho que a tela já renderiza.\n parts.push('DADOS DO ATENDIMENTO (referência; não repita como seção):')\n for (const [k, v] of ctx) parts.push(`- ${k}: ${v}`)\n parts.push('')\n }\n\n parts.push('TRANSCRIÇÃO:', '\"\"\"', transcript, '\"\"\"', '')\n parts.push('SEÇÕES A ESCREVER, nesta ordem:')\n\n schema.sections.forEach((section, i) => {\n parts.push('')\n parts.push(`${i + 1}. ## ${section.heading}`)\n parts.push(` O que entra: ${section.guidance}`)\n parts.push(` ${shapeInstruction(section)}`)\n if (section.maxWords) parts.push(` Limite: cerca de ${section.maxWords} palavras.`)\n if (section.required) parts.push(' Obrigatória: escreva-a mesmo que só para registrar \"não informado\".')\n else if (section.omitWhenEmpty) parts.push(' Se a transcrição não disser nada sobre isto, OMITA a seção inteira, título incluso.')\n })\n\n return parts.join('\\n')\n}\n\n/**\n * Quebra o markdown de volta em seções por `id`, casando os títulos `## `. Serve\n * para o commit gravar `data.sections` e para o assistente reescrever UMA seção\n * sem tocar no resto.\n *\n * Casa por título normalizado porque é o único âncora que o modelo devolve —\n * daí o `id` da seção nunca ser traduzido: ele é a chave, o título é a etiqueta.\n */\nexport function parseSections(markdown: string, schema: NarrativeSchema): Record<string, string> {\n const out: Record<string, string> = {}\n if (!markdown) return out\n\n const norm = (s: string) => s.trim().toLowerCase().replace(/\\s+/g, ' ').replace(/[:.]+$/, '')\n const byHeading = new Map(schema.sections.map((s) => [norm(s.heading), s.id]))\n\n const lines = markdown.split('\\n')\n let currentId: string | null = null\n let buffer: string[] = []\n\n const flush = () => {\n if (currentId) out[currentId] = buffer.join('\\n').trim()\n buffer = []\n }\n\n for (const line of lines) {\n const match = /^##\\s+(.+?)\\s*$/.exec(line)\n if (match && !line.startsWith('###')) {\n flush()\n currentId = byHeading.get(norm(match[1])) ?? null\n continue\n }\n if (currentId) buffer.push(line)\n }\n flush()\n return out\n}\n\n/** Título do documento: primeira seção com conteúdo, encurtada. Cai no nome do template. */\nexport function deriveTitle(markdown: string, fallback: string, maxLen = 80): string {\n const firstBody = markdown\n .split('\\n')\n .find((l) => l.trim() && !l.startsWith('#') && !l.startsWith('-') && !l.startsWith('>'))\n if (!firstBody) return fallback\n const clean = firstBody.replace(/[*_`]/g, '').trim()\n return clean.length > maxLen ? `${clean.slice(0, maxLen - 1)}…` : clean\n}\n","import * as React from 'react'\nimport { AlertTriangle, Loader2 } from 'lucide-react'\nimport { Badge } from '@fayz-ai/ui/primitives'\nimport { formatOffset } from '../lib/prompt'\nimport type { ScribeSegment } from '../types'\n\n// The transcript, timestamped.\n//\n// One requirement here is not cosmetic: A GAP MUST SHOW. A `gap` segment\n// renders as a visible line with the hole's duration. Without it the text reads\n// as continuous when it is not, and in a clinical record a hidden hole is worse\n// than a declared one — a declared hole the reader can weigh.\n\nexport interface TranscriptPanelProps {\n segments: ScribeSegment[]\n /** A segment is still uploading or transcribing. */\n pending: boolean\n emptyLabel?: string\n}\n\nexport function TranscriptPanel({ segments, pending, emptyLabel }: TranscriptPanelProps) {\n const visible = segments.filter((s) => s.gap || s.text?.trim())\n\n // Spinner and not skeleton while recording: a skeleton promises the shape of\n // something already on its way, and here the text depends on someone still\n // talking. Skeletons belong to the detail load, where the data exists.\n if (visible.length === 0) {\n return (\n <div className=\"flex flex-col items-center justify-center gap-2 py-12 text-center text-sm text-muted-foreground\">\n {pending ? (\n <>\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n <p>Transcrevendo…</p>\n <p className=\"text-xs\">O texto aparece conforme os trechos são processados.</p>\n </>\n ) : (\n <p>{emptyLabel ?? 'Nenhuma transcrição ainda.'}</p>\n )}\n </div>\n )\n }\n\n return (\n <div className=\"space-y-3\">\n {visible.map((seg) => {\n if (seg.gap) {\n return (\n <div\n key={seg.segIndex}\n className=\"flex items-center gap-2 rounded-md border border-dashed border-amber-500/40 bg-amber-500/5 px-3 py-2 text-xs text-amber-700 dark:text-amber-400\"\n >\n <AlertTriangle className=\"h-3.5 w-3.5 shrink-0\" />\n <span className=\"font-mono\">{formatOffset(seg.startOffsetMs)}</span>\n <span>\n Gravação interrompida\n {seg.durationMs ? ` por ${Math.round(seg.durationMs / 1000)}s` : ''}\n </span>\n </div>\n )\n }\n\n const speaker = seg.words?.[0]?.sp\n return (\n <div key={seg.segIndex} className=\"flex gap-3\">\n <span className=\"w-12 shrink-0 pt-0.5 font-mono text-xs text-muted-foreground\">\n {formatOffset(seg.startOffsetMs)}\n </span>\n <div className=\"min-w-0 flex-1\">\n {speaker !== undefined && (\n <Badge variant=\"secondary\" className=\"mb-1 text-[10px]\">\n Falante {speaker + 1}\n </Badge>\n )}\n <p className=\"text-sm leading-relaxed\">\n {seg.text}\n {seg.partial && (\n <span className=\"ml-1 text-xs text-muted-foreground\">(trecho parcial)</span>\n )}\n </p>\n </div>\n </div>\n )\n })}\n\n {pending && (\n <div className=\"flex items-center gap-2 pl-15 text-xs text-muted-foreground\">\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n Transcrevendo o restante…\n </div>\n )}\n </div>\n )\n}\n","import * as React from 'react'\nimport { Check, Loader2, RefreshCw, Trash2 } from 'lucide-react'\nimport { Badge, Button, MarkdownEditor, toast } from '@fayz-ai/ui/primitives'\nimport { parseSections } from '../lib/prompt'\nimport { commitGeneration, discardGeneration, saveDraft } from '../data/supabase'\nimport type { NarrativeSchema, ScribeGeneration, ScribeLabels, ScribeSession } from '../types'\n\n// ---------------------------------------------------------------------------\n// O rascunho: gerar → revisar → salvar na ficha.\n//\n// O texto do modelo (`markdown`) nunca é sobrescrito. O que o usuário edita vai\n// para `markdown_edited`, e a diferença entre os dois é o produto secundário\n// mais útil de toda a feature: é o selo de \"revisado pelo profissional\", é o\n// diff que diz se o prompt está bom, e é a proveniência que um registro\n// médico-legal precisa ter.\n//\n// O autosave é debounced em 2 s. Salvar a cada tecla castigaria o banco; salvar\n// só no commit perderia a edição de quem fechou a aba no meio da revisão — que\n// é justamente o modo de falha que esta feature inteira existe para não ter.\n// ---------------------------------------------------------------------------\n\nconst AUTOSAVE_DEBOUNCE_MS = 2_000\n\nexport interface DraftPanelProps {\n generation: ScribeGeneration\n session: ScribeSession\n schema: NarrativeSchema\n templateName: string\n /** Preset category — travels with the template created on first use. */\n templateCategory?: string\n labels: ScribeLabels\n onRegenerate: () => void\n onCommitted: (documentId: string) => void\n onDiscarded: () => void\n}\n\nexport function DraftPanel({\n generation,\n session,\n schema,\n templateName,\n templateCategory,\n labels,\n onRegenerate,\n onCommitted,\n onDiscarded,\n}: DraftPanelProps) {\n const original = generation.markdown ?? ''\n const [value, setValue] = React.useState(generation.markdownEdited ?? original)\n const [saving, setSaving] = React.useState(false)\n const [committing, setCommitting] = React.useState(false)\n const timer = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n\n // Uma geração nova (regenerar, trocar de aba) reseta o editor. Sem isto o\n // texto da geração anterior ficaria na tela sobre o conteúdo novo.\n React.useEffect(() => {\n setValue(generation.markdownEdited ?? generation.markdown ?? '')\n }, [generation.id, generation.markdown, generation.markdownEdited])\n\n const edited = value.trim() !== original.trim()\n\n const handleChange = React.useCallback(\n (next: string) => {\n setValue(next)\n if (timer.current) clearTimeout(timer.current)\n timer.current = setTimeout(() => {\n setSaving(true)\n saveDraft(generation.id, next)\n .catch(() => toast.error('Não foi possível salvar o rascunho'))\n .finally(() => setSaving(false))\n }, AUTOSAVE_DEBOUNCE_MS)\n },\n [generation.id],\n )\n\n // Flush no unmount: navegar para outra aba não pode engolir os últimos 2 s de\n // digitação.\n React.useEffect(() => {\n return () => {\n if (timer.current) {\n clearTimeout(timer.current)\n void saveDraft(generation.id, value).catch(() => undefined)\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [generation.id, value])\n\n const handleCommit = React.useCallback(async () => {\n setCommitting(true)\n try {\n if (timer.current) clearTimeout(timer.current)\n await saveDraft(generation.id, value)\n const documentId = await commitGeneration({\n generation,\n session,\n markdown: value,\n // The document's name is its TYPE. `deriveTitle` took the first line\n // of the text, so the record listed \"Paciente refere dor importante\n // em cotovelo…\" where it should list \"Anamnese\" — and two of them for\n // the same patient were indistinguishable.\n title: templateName,\n sections: parseSections(value, schema),\n edited,\n templateName,\n templateCategory,\n })\n toast.success('Documento salvo na ficha')\n onCommitted(documentId)\n } catch (err) {\n toast.error(`Não foi possível salvar: ${(err as Error)?.message ?? err}`)\n } finally {\n setCommitting(false)\n }\n }, [generation, session, value, schema, templateName, templateCategory, edited, onCommitted])\n\n if (generation.status === 'running' || generation.status === 'pending') {\n return (\n <div className=\"flex flex-col items-center justify-center gap-2 py-16 text-sm text-muted-foreground\">\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n <p>Gerando {templateName.toLowerCase()}…</p>\n </div>\n )\n }\n\n if (generation.status === 'failed') {\n return (\n <div className=\"flex flex-col items-center justify-center gap-3 py-16 text-sm\">\n <p className=\"text-destructive\">{generation.error ?? 'A geração falhou.'}</p>\n <Button size=\"sm\" variant=\"outline\" onClick={onRegenerate}>\n <RefreshCw className=\"mr-1.5 h-4 w-4\" />\n Tentar novamente\n </Button>\n </div>\n )\n }\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n {edited ? (\n <Badge variant=\"secondary\">Editado</Badge>\n ) : (\n <Badge variant=\"outline\">Gerado por IA</Badge>\n )}\n {saving && (\n <span className=\"flex items-center gap-1\">\n <Loader2 className=\"h-3 w-3 animate-spin\" />\n salvando…\n </span>\n )}\n {generation.model && <span className=\"hidden sm:inline\">· {generation.model}</span>}\n </div>\n\n <div className=\"flex gap-1.5\">\n <Button size=\"sm\" variant=\"ghost\" onClick={onRegenerate} disabled={committing}>\n <RefreshCw className=\"mr-1.5 h-3.5 w-3.5\" />\n Regerar\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={committing}\n onClick={() =>\n void discardGeneration(generation.id).then(onDiscarded).catch(() => toast.error('Falha ao descartar'))\n }\n >\n <Trash2 className=\"mr-1.5 h-3.5 w-3.5\" />\n {labels.discard}\n </Button>\n <Button size=\"sm\" onClick={() => void handleCommit()} disabled={committing || !value.trim()}>\n {committing ? (\n <Loader2 className=\"mr-1.5 h-3.5 w-3.5 animate-spin\" />\n ) : (\n <Check className=\"mr-1.5 h-3.5 w-3.5\" />\n )}\n Salvar na ficha\n </Button>\n </div>\n </div>\n\n <MarkdownEditor\n value={value}\n onChange={handleChange}\n minRows={20}\n defaultMode=\"preview\"\n editLabel=\"Editar\"\n previewLabel=\"Visualizar\"\n />\n\n {schema.disclaimer && (\n <p className=\"text-xs italic text-muted-foreground\">{schema.disclaimer}</p>\n )}\n </div>\n )\n}\n","import { getSupabaseClientOptional } from '@fayz-ai/core'\nimport { T } from '../data/tables'\nimport type { NarrativeSchema, ScribeSttConfig } from '../types'\nimport {\n PROMPT_VERSION,\n buildSystemPrompt,\n buildUserPrompt,\n renderTranscript,\n type TranscriptLine,\n} from './prompt'\nimport { resolveScribeTransport } from './transport'\n\n// Generation through the platform broker.\n//\n// Same dismantling as the STT drain: the edge function orchestrated AND held\n// the key, and only the key had to stay out of the client's reach. The prompt\n// is built by the same `lib/prompt.ts` on both sides, which is why it never\n// imported anything from Deno or the browser.\n//\n// What stays intact, because it is what makes a generation a DOCUMENT and not a\n// conversation:\n//\n// - the row is born `running` BEFORE the model call. A generation that hangs\n// without a trace is indistinguishable from one never asked for.\n// - `markdown` is written once and never again; human edits go to\n// `markdown_edited`. The difference between them is the proof somebody\n// reviewed it, and the metric that says whether the model is good.\n// - `prompt_version`, `model` and tokens are stored, so it is auditable.\n//\n// None of that fits in a chat turn, which is why generation stays outside the\n// assistant even now that the model comes from the same broker.\n\n/** Reproducing the same note twice matters more here than sounding creative. */\nconst TEMPERATURE = 0.2\nconst MAX_TOKENS = 4000\n\nfunction client(): any {\n const supabase = getSupabaseClientOptional() as any\n if (!supabase) throw new Error('Supabase not initialised')\n return supabase\n}\n\nexport interface GenerateResult {\n generationId: string\n markdown: string\n model: string\n}\n\n/**\n * Generates a document from the session transcript.\n *\n * Returns `null` when the configured transport is not the browser one — the\n * caller must use the edge function then.\n */\nexport async function generateViaTransport(args: {\n sessionId: string\n schema: NarrativeSchema\n templateName: string\n templateId?: string\n templateKey?: string\n tabOrder?: number\n stt: ScribeSttConfig\n model?: string\n}): Promise<GenerateResult | null> {\n const transport = resolveScribeTransport(args.stt)\n if (!transport) return null\n\n const supabase = client()\n\n const { data: userData } = await supabase.auth.getUser()\n const userId = userData?.user?.id\n if (!userId) throw new Error('não autenticado')\n\n const { data: session } = await supabase\n .from(T.sessions)\n .select('id, tenant_id, subject_id, started_at, audio_duration_ms, locale')\n .eq('id', args.sessionId)\n .maybeSingle()\n if (!session) throw new Error('sessão não encontrada')\n\n const lines = await loadTranscript(supabase, args.sessionId)\n if (!lines.some((l) => !l.gap && l.text.trim())) {\n throw new Error('não há transcrição para gerar o documento')\n }\n\n const { data: created, error: insErr } = await supabase\n .from(T.generations)\n .insert({\n tenant_id: session.tenant_id,\n session_id: args.sessionId,\n template_id: args.templateId ?? null,\n template_key: args.templateKey ?? null,\n tab_order: args.tabOrder ?? 0,\n status: 'running',\n prompt_version: PROMPT_VERSION,\n created_by: userId,\n })\n .select('id')\n .single()\n if (insErr || !created) throw new Error(`falha ao criar a geração: ${insErr?.message}`)\n\n const generationId: string = created.id\n\n try {\n let subjectName: string | undefined\n if (session.subject_id) {\n const { data: person } = await supabase\n .from('people')\n .select('name')\n .eq('id', session.subject_id)\n .maybeSingle()\n subjectName = person?.name\n }\n\n const result = await transport.complete({\n system: buildSystemPrompt(args.schema),\n user: buildUserPrompt({\n schema: args.schema,\n transcript: renderTranscript(lines),\n context: {\n Titular: subjectName,\n Data: new Date(session.started_at).toLocaleDateString(session.locale ?? 'pt-BR'),\n 'Duração (min)': String(Math.round((session.audio_duration_ms ?? 0) / 60000)),\n },\n }),\n model: args.model,\n temperature: TEMPERATURE,\n maxTokens: MAX_TOKENS,\n })\n\n const markdown = result.content.trim()\n if (!markdown) throw new Error('o modelo devolveu um documento vazio')\n\n await supabase\n .from(T.generations)\n .update({\n status: 'ready',\n markdown,\n // The document TYPE, not a summary of it: \"Anamnese\" identifies it;\n // the first sentence changes with every generation.\n title: args.templateName,\n model: result.model,\n input_tokens: result.tokensIn ?? null,\n output_tokens: result.tokensOut ?? null,\n updated_at: new Date().toISOString(),\n })\n .eq('id', generationId)\n\n await supabase\n .from(T.sessions)\n .update({ status: 'generating', updated_at: new Date().toISOString() })\n .eq('id', args.sessionId)\n\n return { generationId, markdown, model: result.model }\n } catch (err) {\n const message = String((err as Error)?.message ?? err)\n await supabase\n .from(T.generations)\n .update({ status: 'failed', error: message.slice(0, 500), updated_at: new Date().toISOString() })\n .eq('id', generationId)\n throw err\n }\n}\n\nasync function loadTranscript(supabase: any, sessionId: string): Promise<TranscriptLine[]> {\n const { data } = await supabase\n .from(T.segments)\n .select('seg_index, start_offset_ms, text, gap, words')\n .eq('session_id', sessionId)\n .order('seg_index', { ascending: true })\n if (!data) return []\n\n return data.map((s: any) => ({\n startOffsetMs: s.start_offset_ms ?? 0,\n text: s.text ?? '',\n gap: !!s.gap,\n speaker: Array.isArray(s.words) && s.words.length > 0 ? s.words[0]?.sp : undefined,\n }))\n}\n","import * as React from 'react'\nimport { FileText, Loader2, Sparkles } from 'lucide-react'\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n Skeleton,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n toast,\n} from '@fayz-ai/ui/primitives'\nimport { SubpageHeader } from '@fayz-ai/ui/layout'\nimport { useAgentSurface } from '@fayz-ai/admin'\nimport { TranscriptPanel } from './TranscriptPanel'\nimport { DraftPanel } from './DraftPanel'\nimport {\n fetchGenerations,\n fetchSegments,\n fetchSession,\n invokeGenerate,\n invokeTranscribe,\n} from '../data/supabase'\nimport { drainSessionViaTransport } from '../lib/drain'\nimport { generateViaTransport } from '../lib/generate'\nimport { formatElapsed } from '../store'\nimport type {\n NarrativeTemplatePreset,\n ScribeGeneration,\n ScribeLabels,\n ScribeSegment,\n ScribeSession,\n ScribeSttConfig,\n} from '../types'\n\n// ---------------------------------------------------------------------------\n// A tela da sessão.\n//\n// Ela faz POLLING enquanto há trabalho pendente, e isso é uma escolha, não uma\n// preguiça: o SDK não tem infraestrutura de job em background, então quem\n// empurra a fila de STT é o cliente que estiver com a tela aberta. O desenho\n// tolera isso porque `stt_state='pending'` PERSISTE — se ninguém estiver\n// olhando, o trabalho fica na fila e o próximo cliente a abrir a sessão drena.\n// Nada se perde por ninguém estar olhando; só demora mais.\n//\n// (O caminho definitivo é um trigger + pg_net disparando a function, como o\n// google-calendar-sync faz. Fica para quando `pg_net` estiver confirmado no\n// pool — está listado como item de verificação do plano.)\n// ---------------------------------------------------------------------------\n\nconst POLL_MS = 5_000\n\nexport interface ScribeSessionPageProps {\n sessionId: string\n labels: ScribeLabels\n presets: NarrativeTemplatePreset[]\n stt: ScribeSttConfig\n generateEndpoint: string\n onOpenDocument?: (documentId: string) => void\n onBack?: () => void\n /** Fires once the session row is known. The standalone route uses it to send\n * a recording that HAS a holder to the holder's own page — see index.ts. */\n onSessionLoaded?: (session: ScribeSession) => void\n /** Rendered INSIDE another screen rather than as its own page: keeps the\n * back navigation with the content instead of hoisting it to the shell bar,\n * which still belongs to the record. */\n embedded?: boolean\n}\n\nexport function ScribeSessionPage({\n sessionId,\n embedded,\n labels,\n presets,\n stt,\n generateEndpoint,\n onOpenDocument,\n onBack,\n onSessionLoaded,\n}: ScribeSessionPageProps) {\n const [session, setSession] = React.useState<ScribeSession | null>(null)\n const [segments, setSegments] = React.useState<ScribeSegment[]>([])\n const [generations, setGenerations] = React.useState<ScribeGeneration[]>([])\n const [loading, setLoading] = React.useState(true)\n const [generating, setGenerating] = React.useState(false)\n const [tab, setTab] = React.useState('transcript')\n const draining = React.useRef(false)\n\n const refresh = React.useCallback(async () => {\n const [s, segs, gens] = await Promise.all([\n fetchSession(sessionId),\n fetchSegments(sessionId),\n fetchGenerations(sessionId),\n ])\n setSession(s)\n if (s) onSessionLoaded?.(s)\n setSegments(segs)\n setGenerations(gens)\n return { session: s, segments: segs }\n }, [sessionId, onSessionLoaded])\n\n React.useEffect(() => {\n setLoading(true)\n void refresh().finally(() => setLoading(false))\n }, [refresh])\n\n // Há trabalho pendente? Segmento que subiu e ainda não voltou do STT.\n const pendingStt = React.useMemo(\n () => segments.filter((s) => s.uploadState === 'uploaded' && ['pending', 'failed'].includes(s.sttState) && s.sttAttempts < 4),\n [segments],\n )\n const busy = pendingStt.length > 0 || segments.some((s) => s.sttState === 'running')\n\n React.useEffect(() => {\n if (!busy) return\n const id = setInterval(() => {\n void (async () => {\n // Um guard contra reentrância: a chamada de drain leva mais que o\n // intervalo do poll, e disparar a segunda por cima da primeira faria\n // dois clientes transcreverem o mesmo segmento.\n if (draining.current) return\n draining.current = true\n try {\n if (pendingStt.length > 0) {\n // `null` = a configuração escolheu a edge function; qualquer outra\n // coisa e o dreno já rodou aqui, contra o broker da plataforma.\n const drained = await drainSessionViaTransport(sessionId, stt)\n if (drained === null) {\n await invokeTranscribe({\n endpoint: stt.endpoint!,\n sessionId,\n provider: stt.provider,\n model: stt.model,\n diarize: stt.diarize,\n })\n }\n }\n await refresh()\n } catch {\n // Falha de drain não pode derrubar a tela; a próxima volta tenta.\n } finally {\n draining.current = false\n }\n })()\n }, POLL_MS)\n return () => clearInterval(id)\n }, [busy, pendingStt.length, sessionId, stt, refresh])\n\n const hasTranscript = segments.some((s) => !s.gap && s.text?.trim())\n\n const handleGenerate = React.useCallback(\n async (preset: NarrativeTemplatePreset) => {\n setGenerating(true)\n try {\n const viaTransport = await generateViaTransport({\n sessionId,\n schema: preset.schema,\n templateName: typeof preset.name === 'string' ? preset.name : 'Documento',\n templateKey: preset.key,\n tabOrder: generations.length,\n stt,\n })\n const generationId =\n viaTransport?.generationId ??\n (\n await invokeGenerate({\n endpoint: generateEndpoint,\n sessionId,\n templateKey: preset.key,\n schema: preset.schema,\n tabOrder: generations.length,\n })\n ).generationId\n await refresh()\n setTab(generationId)\n } catch (err) {\n toast.error(`Não foi possível gerar: ${(err as Error)?.message ?? err}`)\n } finally {\n setGenerating(false)\n }\n },\n [generateEndpoint, sessionId, generations.length, refresh, stt],\n )\n\n // -------------------------------------------------------------------------\n // Agent surface — o assistente nativo, sem infraestrutura nova.\n //\n // A geração NÃO passa por aqui (o turn loop tem teto de 4 rodadas, vive num\n // hook que morre na navegação e persistiria a transcrição no histórico de\n // conversa do broker). O que passa é a conversa DEPOIS: \"o que foi dito sobre\n // a dor?\", \"resuma para o paciente\". Isso é pequeno, limitado e cabe folgado.\n // -------------------------------------------------------------------------\n const activeGeneration = generations.find((g) => g.id === tab)\n useAgentSurface(\n React.useMemo(\n () => ({\n id: 'scribe.session',\n describe: () => ({\n title: `${labels.sessionSingular} — ${session?.startedAt ? new Date(session.startedAt).toLocaleDateString() : ''}`,\n state: {\n durationMinutes: Math.round((session?.audioDurationMs ?? 0) / 60000),\n transcriptExcerpt: segments\n .filter((s) => s.text)\n .map((s) => s.text)\n .join(' ')\n .slice(0, 4000),\n documentMarkdown: activeGeneration?.markdownEdited ?? activeGeneration?.markdown,\n documentStatus: activeGeneration?.status,\n },\n record: session?.subjectId\n ? { entity: 'person', id: session.subjectId, label: labels.subject }\n : undefined,\n }),\n instructions: [\n `O usuário está revisando um ${labels.sessionSingular.toLowerCase()} gravado e transcrito.`,\n 'Responda \"o que foi dito sobre X\" citando o trecho da transcrição, sempre com o minuto.',\n 'Nunca afirme algo que a transcrição não sustente — diga que não foi mencionado.',\n ].join(' '),\n }),\n [session, segments, activeGeneration, labels],\n ) as any,\n )\n\n if (loading) {\n return (\n // Same silhouette as the loaded screen, so nothing jumps on arrival.\n <div className=\"space-y-4\" aria-busy=\"true\">\n <div className=\"flex gap-2\">\n <Skeleton className=\"h-5 w-40 rounded-full\" />\n <Skeleton className=\"h-5 w-28 rounded-full\" />\n <Skeleton className=\"h-5 w-20 rounded-full\" />\n </div>\n <Skeleton className=\"h-8 w-32 rounded-lg\" />\n <div className=\"space-y-3 rounded-xl border p-4\">\n {[0, 1, 2].map((row) => (\n <div key={row} className=\"flex gap-3\">\n <Skeleton className=\"h-3 w-10 shrink-0\" />\n <div className=\"min-w-0 flex-1 space-y-1.5\">\n <Skeleton className=\"h-3 w-full\" />\n <Skeleton className={row === 1 ? 'h-3 w-2/5' : 'h-3 w-4/5'} />\n </div>\n </div>\n ))}\n </div>\n </div>\n )\n }\n\n if (!session) {\n return <p className=\"py-16 text-center text-sm text-muted-foreground\">Sessão não encontrada.</p>\n }\n\n const uploadPending = session.segmentCount - session.uploadedSegmentCount\n\n return (\n <div className=\"space-y-4\">\n <SubpageHeader\n title={labels.sessionSingular}\n parentLabel={onBack ? labels.sessionPlural : undefined}\n onBack={onBack}\n // Opened inside a tab, the page never changed: the back link stays\n // beside the content instead of rewriting the record's own path.\n inline={embedded}\n />\n\n <div className=\"flex flex-wrap items-center gap-2 text-sm text-muted-foreground\">\n <Badge variant=\"outline\">{new Date(session.startedAt).toLocaleString()}</Badge>\n <Badge variant=\"outline\">{formatElapsed(session.audioDurationMs)} de áudio</Badge>\n <Badge variant={session.status === 'ready' || session.status === 'completed' ? 'secondary' : 'outline'}>\n {statusLabel(session.status)}\n </Badge>\n {uploadPending > 0 && (\n <Badge variant=\"outline\" className=\"text-amber-600\">\n {uploadPending} trecho{uploadPending > 1 ? 's' : ''} enviando\n </Badge>\n )}\n </div>\n\n <Tabs value={tab} onValueChange={setTab}>\n <TabsList className=\"max-w-full justify-start overflow-x-auto scrollbar-none\">\n <TabsTrigger value=\"transcript\" className=\"gap-1.5\">\n <FileText className=\"h-3.5 w-3.5\" />\n {labels.transcript}\n </TabsTrigger>\n {generations.map((g) => (\n <TabsTrigger key={g.id} value={g.id} className=\"gap-1.5\">\n <Sparkles className=\"h-3.5 w-3.5\" />\n {g.title ?? presets.find((p) => p.key === g.templateKey)?.name ?? 'Documento'}\n </TabsTrigger>\n ))}\n </TabsList>\n\n <TabsContent value=\"transcript\" className=\"mt-4\">\n <Card>\n <CardContent className=\"pt-6\">\n <TranscriptPanel segments={segments} pending={busy} />\n </CardContent>\n </Card>\n\n {hasTranscript && (\n <div className=\"mt-4 flex flex-wrap gap-2\">\n {presets.map((preset) => (\n <Button\n key={preset.key}\n size=\"sm\"\n variant={generations.length === 0 ? 'default' : 'outline'}\n disabled={generating}\n onClick={() => void handleGenerate(preset)}\n >\n {generating ? (\n <Loader2 className=\"mr-1.5 h-4 w-4 animate-spin\" />\n ) : (\n <Sparkles className=\"mr-1.5 h-4 w-4\" />\n )}\n {labels.generate}: {preset.name}\n </Button>\n ))}\n </div>\n )}\n </TabsContent>\n\n {generations.map((g) => {\n const preset = presets.find((p) => p.key === g.templateKey)\n if (!preset) return null\n return (\n <TabsContent key={g.id} value={g.id} className=\"mt-4\">\n <DraftPanel\n generation={g}\n session={session}\n schema={preset.schema}\n templateName={preset.name}\n templateCategory={preset.category}\n labels={labels}\n onRegenerate={() => void handleGenerate(preset)}\n onCommitted={(documentId) => {\n void refresh()\n onOpenDocument?.(documentId)\n }}\n onDiscarded={() => {\n setTab('transcript')\n void refresh()\n }}\n />\n </TabsContent>\n )\n })}\n </Tabs>\n </div>\n )\n}\n\nfunction statusLabel(status: ScribeSession['status']): string {\n const map: Record<string, string> = {\n recording: 'Gravando',\n paused: 'Pausado',\n interrupted: 'Interrompido',\n uploading: 'Enviando',\n transcribing: 'Transcrevendo',\n ready: 'Pronto',\n generating: 'Rascunho',\n completed: 'Concluído',\n abandoned: 'Descartado',\n failed: 'Falhou',\n }\n return map[status] ?? status\n}\n","import * as React from 'react'\nimport { Mic } from 'lucide-react'\nimport { useAccessOptional, useHeaderAction } from '@fayz-ai/admin'\nimport { isCaptureSupported } from '../runtime'\nimport { startScribeSession } from '../lib/start'\nimport { useScribeStore } from '../store'\n\nimport type { ScribeLabels } from '../types'\n\n// The record verb where it belongs: on the name row, next to Edit, via the\n// `<entity>.detail.header.actions` zone.\n//\n// The only reachable zone used to be BELOW the tabs, so the app hid the button\n// inside a tab — and a tab is a place, not an action. Coming from the plugin it\n// also cannot exist without its engine: the app-registered tab showed up with\n// scribe disabled, and the click fired `scribe:start` at nobody.\n//\n// Pressing it hands the client over to the colleague who does this: the panel\n// opens on HER thread, and the consent is asked there. See lib/start.ts — this\n// button knows nothing about the sequence, only who it is for.\n\nexport interface ScribeHeaderActionProps {\n labels: ScribeLabels\n /** The record CrudDetailPage is showing. */\n item?: { id?: string; personId?: string; name?: string; full_name?: string }\n /** Which entity's detail this is — the zone is registered per entity. */\n entityId?: string\n embedded?: boolean\n}\n\nexport function ScribeHeaderAction({ labels, item, entityId }: ScribeHeaderActionProps) {\n // Extension tables point at the `people` row that owns the session.\n const subjectId = item?.personId ?? item?.id\n const subjectName = item?.name ?? item?.full_name\n // Recording costs real money per minute, so the plan decides whether it\n // exists here — otherwise the button invites people into a wall.\n const { entitled } = useAccessOptional()\n const busy = useScribeStore((s) =>\n s.state === 'recording' || s.state === 'paused' || s.state === 'interrupted' || s.state === 'requesting')\n\n const available = !!subjectId && entitled('scribe') && isCaptureSupported()\n\n // Declared, not drawn. As a button it stayed in the corner of the header while\n // every other action moved into the caret — and on a 393px screen it pushed\n // the record's own name out of the crumb beside it.\n useHeaderAction(\n entityId ? `${entityId}.detail.header.actions` : '',\n available\n ? {\n id: 'scribe-start',\n label: labels.start,\n icon: Mic,\n intent: 'secondary',\n disabled: busy,\n onSelect: () => startScribeSession({ subjectId: subjectId!, subjectName }),\n }\n : null,\n )\n\n return null\n}\n","// AUTO-GENERATED from 001_scribe_base.sql, 002_the_definer_function_stops_trusting_the_path.sql, 003_the_template_link_stops_being_a_foreign_key.sql — regenerate with scripts/embed-migrations.mjs\n// SQL files are the source of truth; this inline copy lets the manifest declare\n// migrations as data. Do not edit by hand — run the embed script instead.\n\nexport const MIGRATION_001_SCRIBE_BASE = `-- plugin-scribe 001: gravar um atendimento inteiro sem poder perdê-lo.\n--\n-- O problema que este schema resolve não é \"guardar áudio\". É que o profissional\n-- aperta gravar, navega o app por 40 minutos, e QUALQUER coisa pode acontecer no\n-- meio: reload, aba morta, wifi caindo, tampa fechando. Perder a consulta é uma\n-- falha irrecuperável — não dá para pedir para o paciente repetir.\n--\n-- Por isso o desenho não tem \"o arquivo da sessão\". Tem um MANIFESTO: a sessão\n-- é uma linha, e cada 30 segundos de áudio é OUTRA linha que sobe sozinha e é\n-- transcrita sozinha. O que chegou, chegou. O cliente pode morrer entre dois\n-- segmentos e o que já subiu continua íntegro e já transcrito.\n--\n-- ---------------------------------------------------------------------------\n-- Duas durações, e elas discordam de propósito\n-- ---------------------------------------------------------------------------\n-- \\`wall_duration_ms\\` é o relógio de parede que o browser reportou. Serve para o\n-- cronômetro na tela e para mais nada.\n--\n-- \\`audio_duration_ms\\` é SUM(segments.duration_ms) — soma do áudio que de fato\n-- existe, com a duração vinda da resposta do STT, não de um \\`setInterval\\`.\n--\n-- Elas divergem sempre que a aba congela: notebook dormindo por 10 minutos faz\n-- o relógio de parede contar a soneca inteira enquanto o MediaRecorder não\n-- emitiu um byte. Registro clínico, medidor de cota e fatura usam a SEGUNDA.\n-- Confundir as duas é cobrar do cliente pelo cochilo do notebook.\n--\n-- ---------------------------------------------------------------------------\n-- O contador nunca é escrito pelo browser\n-- ---------------------------------------------------------------------------\n-- \\`scribe_record_segment\\` (seção 5) faz o upsert do segmento e RECALCULA os\n-- contadores da sessão a partir da tabela de segmentos. Um cliente que sobe o\n-- mesmo segmento duas vezes (replay depois de queda de rede) converge para o\n-- mesmo número em vez de somar duas vezes.\n\n-- ---------------------------------------------------------------------------\n-- 1. sessions — o encontro\n-- ---------------------------------------------------------------------------\n-- \\`id\\` NÃO tem DEFAULT gen_random_uuid(). É gerado no cliente, antes do primeiro\n-- byte, e usado como chave no IndexedDB e no caminho do Storage. Sem isso a\n-- gravação teria que esperar um round-trip para saber onde escrever — e uma\n-- gravação que depende da rede para COMEÇAR já nasce frágil.\nCREATE TABLE IF NOT EXISTS public.plg_scribe_sessions (\n id uuid PRIMARY KEY,\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n\n -- O titular. SET NULL e não CASCADE: apagar a pessoa não pode apagar em\n -- silêncio o registro do atendimento que aconteceu.\n subject_id uuid REFERENCES public.people(id) ON DELETE SET NULL,\n context_kind text NOT NULL DEFAULT 'person'\n CHECK (context_kind IN ('person', 'appointment', 'standalone')),\n appointment_id uuid REFERENCES public.appointments(id) ON DELETE SET NULL,\n owner_user_id uuid NOT NULL,\n\n status text NOT NULL DEFAULT 'recording'\n CHECK (status IN ('recording','paused','interrupted','uploading','transcribing',\n 'ready','generating','completed','abandoned','failed')),\n locale text NOT NULL DEFAULT 'pt-BR',\n mime_type text,\n\n started_at timestamptz NOT NULL DEFAULT now(),\n ended_at timestamptz,\n wall_duration_ms integer NOT NULL DEFAULT 0,\n audio_duration_ms integer NOT NULL DEFAULT 0,\n\n segment_count integer NOT NULL DEFAULT 0,\n uploaded_segment_count integer NOT NULL DEFAULT 0,\n transcribed_segment_count integer NOT NULL DEFAULT 0,\n transcript_chars integer NOT NULL DEFAULT 0,\n\n -- Base legal, carimbada antes do primeiro byte. \"Quando\" e \"como\" é o que se\n -- prova depois; guardar isso na mesma linha da gravação é o que torna a prova\n -- barata.\n consent_at timestamptz,\n consent_mode text CHECK (consent_mode IN ('verbal','written','implied_contract')),\n\n -- Retenção: o áudio some, a transcrição e o documento assinado ficam.\n retention_until date,\n audio_deleted_at timestamptz,\n\n error text,\n metadata jsonb NOT NULL DEFAULT '{}'::jsonb,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\nALTER TABLE public.plg_scribe_sessions ENABLE ROW LEVEL SECURITY;\n\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_sessions_tenant\n ON public.plg_scribe_sessions(tenant_id);\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_sessions_subject\n ON public.plg_scribe_sessions(subject_id) WHERE subject_id IS NOT NULL;\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_sessions_appointment\n ON public.plg_scribe_sessions(appointment_id) WHERE appointment_id IS NOT NULL;\n\n-- A consulta da recuperação: \"este usuário tem alguma sessão aberta?\". Roda em\n-- todo boot do app, então é índice parcial e não varredura.\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_sessions_open\n ON public.plg_scribe_sessions(tenant_id, owner_user_id, status)\n WHERE status IN ('recording','paused','interrupted','uploading','transcribing','ready','generating');\n\n-- A consulta do job de retenção.\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_sessions_retention\n ON public.plg_scribe_sessions(retention_until)\n WHERE audio_deleted_at IS NULL AND retention_until IS NOT NULL;\n\n-- ---------------------------------------------------------------------------\n-- 2. segments — o manifesto dos pedaços\n-- ---------------------------------------------------------------------------\n-- A PK é (session_id, seg_index) e não um uuid: o índice do segmento já é\n-- único dentro da sessão, e usar isso como chave faz o re-upload do mesmo\n-- pedaço ser um ON CONFLICT em vez de uma linha duplicada. Idempotência de\n-- graça, que é exatamente o que um pump com retry infinito precisa.\nCREATE TABLE IF NOT EXISTS public.plg_scribe_segments (\n session_id uuid NOT NULL REFERENCES public.plg_scribe_sessions(id) ON DELETE CASCADE,\n seg_index integer NOT NULL,\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n\n storage_path text,\n bytes integer,\n start_offset_ms integer NOT NULL,\n -- Vem da resposta do STT. O relógio do JS não é testemunha confiável de\n -- quanto áudio existe: ele conta tempo, não amostras.\n duration_ms integer,\n\n -- Montado a partir de pieces de um segmento cortado no meio (crash). Vale\n -- transcrever mesmo assim: 22 segundos de consulta é melhor que zero.\n partial boolean NOT NULL DEFAULT false,\n\n -- Linha SINTÉTICA: não tem áudio, marca um buraco (sono, mic perdido,\n -- permissão revogada). Existe para a transcrição ADMITIR o buraco em vez de\n -- emendar dois momentos como se fossem contínuos. Isso é requisito\n -- médico-legal: um registro que esconde a costura é pior que um com furo.\n gap boolean NOT NULL DEFAULT false,\n\n upload_state text NOT NULL DEFAULT 'pending'\n CHECK (upload_state IN ('pending','uploaded','missing')),\n uploaded_at timestamptz,\n\n stt_state text NOT NULL DEFAULT 'pending'\n CHECK (stt_state IN ('pending','running','done','failed','skipped')),\n stt_provider text,\n stt_attempts smallint NOT NULL DEFAULT 0,\n stt_error text,\n\n text text,\n -- [{w,s,e,c,sp}] com offsets RELATIVOS a este segmento. Absoluto se calcula\n -- somando start_offset_ms; guardar relativo é o que deixa reprocessar um\n -- segmento sozinho sem reescrever os vizinhos.\n words jsonb,\n confidence real,\n\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now(),\n PRIMARY KEY (session_id, seg_index)\n);\nALTER TABLE public.plg_scribe_segments ENABLE ROW LEVEL SECURITY;\n\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_segments_tenant\n ON public.plg_scribe_segments(tenant_id);\n\n-- A fila do STT. Parcial porque só interessa o que já subiu e ainda não foi\n-- transcrito — que é uma fração minúscula da tabela em regime permanente.\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_segments_stt_queue\n ON public.plg_scribe_segments(tenant_id, stt_state, created_at)\n WHERE upload_state = 'uploaded' AND stt_state IN ('pending','failed');\n\n-- ---------------------------------------------------------------------------\n-- 3. generations — uma tentativa de documento\n-- ---------------------------------------------------------------------------\n-- DUAS colunas de markdown, e isso não é redundância.\n--\n-- \\`markdown\\` saída do modelo, escrita uma vez, nunca mutada\n-- \\`markdown_edited\\` a cópia de trabalho do humano\n--\n-- A diferença entre elas é a única prova de que alguém revisou. Ela sustenta\n-- três coisas: o selo \"revisado pelo profissional\", o diff que diz se o prompt\n-- está bom (documento muito editado = prompt ruim), e a cadeia de proveniência\n-- que um registro médico-legal precisa ter. Guardar só o texto final apaga tudo\n-- isso para economizar uma coluna de texto.\nCREATE TABLE IF NOT EXISTS public.plg_scribe_generations (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n session_id uuid NOT NULL REFERENCES public.plg_scribe_sessions(id) ON DELETE CASCADE,\n\n -- Um dos dois. \\`template_id\\` quando o tenant forkou e tem linha própria;\n -- \\`template_key\\` quando é preset de código (que de propósito não tem linha,\n -- para não consumir a cota de templates do plano).\n template_id uuid,\n template_key text,\n\n tab_order integer NOT NULL DEFAULT 0,\n title text,\n status text NOT NULL DEFAULT 'pending'\n CHECK (status IN ('pending','running','ready','failed','committed','discarded')),\n\n markdown text,\n markdown_edited text,\n\n -- Extração stage-1 do modo two-stage. A coluna entra agora, vazia, porque a\n -- interface já nasce com esse formato — quando as abas de multi-documento\n -- chegarem, é preencher, não migrar.\n facts jsonb,\n\n model text,\n prompt_version text,\n input_tokens integer,\n output_tokens integer,\n error text,\n\n document_id uuid,\n created_by uuid,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\nALTER TABLE public.plg_scribe_generations ENABLE ROW LEVEL SECURITY;\n\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_generations_session\n ON public.plg_scribe_generations(session_id, tab_order);\nCREATE INDEX IF NOT EXISTS idx_plg_scribe_generations_tenant\n ON public.plg_scribe_generations(tenant_id);\n\n-- FKs para plugin-forms e para o arquétipo de documentos são condicionais: o\n-- scribe declara \\`dependencies: ['custom_forms']\\`, mas a ordem de aplicação\n-- entre plugins não é garantida e um pool pode aplicar este arquivo antes.\n-- Guardado, o re-apply converge; sem guarda, quebraria o passo inteiro.\nDO $$\nBEGIN\n IF to_regclass('public.plg_forms_templates') IS NOT NULL\n AND NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'plg_scribe_generations_template_fk') THEN\n ALTER TABLE public.plg_scribe_generations\n ADD CONSTRAINT plg_scribe_generations_template_fk\n FOREIGN KEY (template_id) REFERENCES public.plg_forms_templates(id) ON DELETE SET NULL;\n END IF;\n\n IF to_regclass('public.documents') IS NOT NULL\n AND NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'plg_scribe_generations_document_fk') THEN\n ALTER TABLE public.plg_scribe_generations\n ADD CONSTRAINT plg_scribe_generations_document_fk\n FOREIGN KEY (document_id) REFERENCES public.documents(id) ON DELETE SET NULL;\n END IF;\nEND $$;\n\n-- ---------------------------------------------------------------------------\n-- 4. RLS — a forma canônica deste repo, três tabelas x quatro verbos\n-- ---------------------------------------------------------------------------\nDROP POLICY IF EXISTS plg_scribe_sessions_select ON public.plg_scribe_sessions;\nCREATE POLICY plg_scribe_sessions_select ON public.plg_scribe_sessions FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_sessions_insert ON public.plg_scribe_sessions;\nCREATE POLICY plg_scribe_sessions_insert ON public.plg_scribe_sessions FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_sessions_update ON public.plg_scribe_sessions;\nCREATE POLICY plg_scribe_sessions_update ON public.plg_scribe_sessions FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_sessions_delete ON public.plg_scribe_sessions;\nCREATE POLICY plg_scribe_sessions_delete ON public.plg_scribe_sessions FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_scribe_sessions TO authenticated;\n\nDROP POLICY IF EXISTS plg_scribe_segments_select ON public.plg_scribe_segments;\nCREATE POLICY plg_scribe_segments_select ON public.plg_scribe_segments FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_segments_insert ON public.plg_scribe_segments;\nCREATE POLICY plg_scribe_segments_insert ON public.plg_scribe_segments FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_segments_update ON public.plg_scribe_segments;\nCREATE POLICY plg_scribe_segments_update ON public.plg_scribe_segments FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_segments_delete ON public.plg_scribe_segments;\nCREATE POLICY plg_scribe_segments_delete ON public.plg_scribe_segments FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_scribe_segments TO authenticated;\n\nDROP POLICY IF EXISTS plg_scribe_generations_select ON public.plg_scribe_generations;\nCREATE POLICY plg_scribe_generations_select ON public.plg_scribe_generations FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_generations_insert ON public.plg_scribe_generations;\nCREATE POLICY plg_scribe_generations_insert ON public.plg_scribe_generations FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_generations_update ON public.plg_scribe_generations;\nCREATE POLICY plg_scribe_generations_update ON public.plg_scribe_generations FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nDROP POLICY IF EXISTS plg_scribe_generations_delete ON public.plg_scribe_generations;\nCREATE POLICY plg_scribe_generations_delete ON public.plg_scribe_generations FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_scribe_generations TO authenticated;\n\n-- ---------------------------------------------------------------------------\n-- 5. scribe_record_segment — o browser registra, o banco conta\n-- ---------------------------------------------------------------------------\n-- SECURITY INVOKER: a RLS acima já amarra o tenant. Não há nada aqui que\n-- precise de privilégio elevado, e uma função SECURITY DEFINER que não precisa\n-- ser é só superfície de ataque a mais.\n--\n-- O ponto da função é a segunda metade: recalcular os contadores da sessão a\n-- partir de um SELECT sobre os segmentos, nunca de um \\`+1\\` vindo do cliente.\n-- Um pump com retry infinito vai reenviar o mesmo segmento, e um contador\n-- incremental transformaria isso em números que crescem sem o áudio crescer.\nCREATE OR REPLACE FUNCTION public.scribe_record_segment(p_payload jsonb)\nRETURNS jsonb\nLANGUAGE plpgsql\nSECURITY INVOKER\nSET search_path TO 'public'\nAS $function$\nDECLARE\n v_session_id uuid;\n v_seg_index integer;\n v_tenant_id uuid;\n v_result jsonb;\nBEGIN\n v_session_id := (p_payload->>'session_id')::uuid;\n v_seg_index := (p_payload->>'seg_index')::integer;\n\n IF v_session_id IS NULL OR v_seg_index IS NULL THEN\n RAISE EXCEPTION 'session_id and seg_index are required';\n END IF;\n\n -- O tenant vem da SESSÃO, nunca do payload. Se o cliente pudesse informar o\n -- tenant, ele escolheria em que pasta escrever.\n SELECT s.tenant_id INTO v_tenant_id\n FROM plg_scribe_sessions s WHERE s.id = v_session_id;\n\n IF v_tenant_id IS NULL THEN\n -- Sessão inexistente OU invisível pela RLS: a mesma resposta nos dois\n -- casos, de propósito — não vaza a existência de sessão de outro tenant.\n RAISE EXCEPTION 'unknown session';\n END IF;\n\n INSERT INTO plg_scribe_segments (\n session_id, seg_index, tenant_id, storage_path, bytes,\n start_offset_ms, duration_ms, partial, gap, upload_state, uploaded_at, stt_state\n ) VALUES (\n v_session_id, v_seg_index, v_tenant_id,\n p_payload->>'storage_path',\n (p_payload->>'bytes')::integer,\n COALESCE((p_payload->>'start_offset_ms')::integer, 0),\n (p_payload->>'duration_ms')::integer,\n COALESCE((p_payload->>'partial')::boolean, false),\n COALESCE((p_payload->>'gap')::boolean, false),\n CASE WHEN COALESCE((p_payload->>'gap')::boolean, false) THEN 'missing' ELSE 'uploaded' END,\n now(),\n -- Buraco não vai para o STT: não há áudio para transcrever, e mandá-lo\n -- queimaria uma chamada para receber string vazia.\n CASE WHEN COALESCE((p_payload->>'gap')::boolean, false) THEN 'skipped' ELSE 'pending' END\n )\n ON CONFLICT (session_id, seg_index) DO UPDATE SET\n storage_path = COALESCE(EXCLUDED.storage_path, plg_scribe_segments.storage_path),\n bytes = COALESCE(EXCLUDED.bytes, plg_scribe_segments.bytes),\n duration_ms = COALESCE(EXCLUDED.duration_ms, plg_scribe_segments.duration_ms),\n partial = EXCLUDED.partial,\n upload_state = EXCLUDED.upload_state,\n uploaded_at = COALESCE(plg_scribe_segments.uploaded_at, EXCLUDED.uploaded_at),\n updated_at = now();\n\n -- Contadores derivados. \\`audio_duration_ms\\` soma só o que tem duração medida,\n -- então um segmento que ainda não voltou do STT não infla o total.\n UPDATE plg_scribe_sessions s SET\n segment_count = agg.total,\n uploaded_segment_count = agg.uploaded,\n transcribed_segment_count = agg.transcribed,\n audio_duration_ms = agg.audio_ms,\n transcript_chars = agg.chars,\n updated_at = now()\n FROM (\n SELECT\n count(*)::int AS total,\n count(*) FILTER (WHERE upload_state = 'uploaded')::int AS uploaded,\n count(*) FILTER (WHERE stt_state = 'done')::int AS transcribed,\n COALESCE(sum(duration_ms) FILTER (WHERE NOT gap), 0)::int AS audio_ms,\n COALESCE(sum(length(text)), 0)::int AS chars\n FROM plg_scribe_segments WHERE session_id = v_session_id\n ) agg\n WHERE s.id = v_session_id;\n\n SELECT to_jsonb(x) INTO v_result FROM (\n SELECT s.segment_count, s.uploaded_segment_count,\n s.transcribed_segment_count, s.audio_duration_ms, s.status\n FROM plg_scribe_sessions s WHERE s.id = v_session_id\n ) x;\n\n RETURN v_result;\nEND;\n$function$;\n\nCOMMENT ON FUNCTION public.scribe_record_segment(jsonb) IS\n 'Idempotent upsert of one audio segment + recomputation of the parent session counters from the segments table. Called by the browser upload pump after each successful Storage write.';\n\nGRANT EXECUTE ON FUNCTION public.scribe_record_segment(jsonb) TO authenticated;\n\n-- ---------------------------------------------------------------------------\n-- 6. o bucket\n-- ---------------------------------------------------------------------------\n-- PRIVADO, sem discussão: é a voz de uma pessoa falando da própria saúde. URL\n-- pública seria um endereço eterno para isso. O backoffice lê por URL assinada\n-- de curta duração; a edge function de transcrição lê com service key.\n--\n-- 5 MB por SEGMENTO é folgado por design: 30 s de Opus mono a 24 kbps dá ~90 KB.\n-- O limite existe para barrar um cliente com bug mandando um arquivo inteiro,\n-- não para apertar o caso normal.\nINSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)\nVALUES (\n 'scribe-audio',\n 'scribe-audio',\n false,\n 5242880,\n ARRAY['audio/webm','audio/ogg','audio/mp4','audio/mpeg','audio/m4a','audio/x-m4a']\n)\nON CONFLICT (id) DO UPDATE SET\n public = EXCLUDED.public,\n file_size_limit = EXCLUDED.file_size_limit,\n allowed_mime_types = EXCLUDED.allowed_mime_types;\n\n-- ---------------------------------------------------------------------------\n-- 7. o caminho é válido?\n-- ---------------------------------------------------------------------------\n-- Gramática (o SDK constrói, o banco confere):\n--\n-- {tenant_id}/{yyyy}/{mm}/{session_id}/{seg_index:05d}.{ext}\n-- 1 2 3 4 5\n--\n-- 1 tenant_id chave do isolamento — toda policy pendura aqui\n-- 2,3 yyyy/mm varredura de retenção por prefixo (LGPD), pasta listável\n-- 4 session_id uuid do cliente: agrupa a sessão e é imprevisível\n-- 5 seg_index zero-padded, então list() já volta em ordem cronológica\n--\n-- SECURITY DEFINER porque o EXISTS olha \\`tenants\\`, que tem RLS.\nCREATE OR REPLACE FUNCTION public.scribe_audio_path_ok(p_name text)\nRETURNS boolean\nLANGUAGE sql\nSTABLE\nSECURITY DEFINER\nSET search_path TO 'public'\nAS $function$\n SELECT\n p_name IS NOT NULL\n AND length(p_name) <= 200\n AND p_name ~ ('^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'\n || '/[0-9]{4}/[0-9]{2}'\n || '/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'\n || '/[0-9]{5}\\\\.(webm|ogg|m4a|mp3)$')\n -- comparação em texto (nunca \\`::uuid\\`): entrada malformada devolve falso em\n -- vez de derrubar a policy com erro de cast.\n AND EXISTS (\n SELECT 1 FROM public.tenants t WHERE t.id::text = split_part(p_name, '/', 1)\n );\n$function$;\n\nCOMMENT ON FUNCTION public.scribe_audio_path_ok(text) IS\n 'True when a storage object name follows the scribe-audio grammar {tenant}/{yyyy}/{mm}/{session}/{segIndex}.{ext} and segment 1 is a real tenant. Used by the INSERT policy on that bucket.';\n\nGRANT EXECUTE ON FUNCTION public.scribe_audio_path_ok(text) TO authenticated;\n\n-- ---------------------------------------------------------------------------\n-- 8. políticas do bucket\n-- ---------------------------------------------------------------------------\nDROP POLICY IF EXISTS plg_scribe_audio_insert ON storage.objects;\nDROP POLICY IF EXISTS plg_scribe_audio_read ON storage.objects;\nDROP POLICY IF EXISTS plg_scribe_audio_delete ON storage.objects;\n\n-- Escrever: só logado (diferente do bucket do CRM, que aceita visitante anônimo\n-- — aqui quem grava é sempre um profissional autenticado), dentro da gramática\n-- e da própria pasta.\n--\n-- NÃO existe policy de UPDATE, e isso é a trava mais importante das três: sem\n-- ela o \\`upsert\\` falha, e um caminho já gravado NUNCA pode ser trocado por\n-- outro conteúdo. É o que torna o áudio uma testemunha e não um rascunho.\nCREATE POLICY plg_scribe_audio_insert ON storage.objects\n FOR INSERT TO authenticated\n WITH CHECK (\n bucket_id = 'scribe-audio'\n AND public.scribe_audio_path_ok(name)\n AND (storage.foldername(name))[1] IN (SELECT public.user_tenant_ids()::text)\n );\n\nCREATE POLICY plg_scribe_audio_read ON storage.objects\n FOR SELECT TO authenticated\n USING (\n bucket_id = 'scribe-audio'\n AND (storage.foldername(name))[1] IN (SELECT public.user_tenant_ids()::text)\n );\n\n-- Apagar: mesma regra da leitura. É o que torna \"apagar o áudio desta sessão\"\n-- executável por quem é dono dele — o direito de eliminação do art. 18 VI não\n-- vale nada se ninguém no tenant consegue exercê-lo.\nCREATE POLICY plg_scribe_audio_delete ON storage.objects\n FOR DELETE TO authenticated\n USING (\n bucket_id = 'scribe-audio'\n AND (storage.foldername(name))[1] IN (SELECT public.user_tenant_ids()::text)\n );\n`\n\nexport const MIGRATION_002_THE_DEFINER_FUNCTION_STOPS_TRUSTING_THE_PATH = `-- ---------------------------------------------------------------------------\n-- 002_the_definer_function_stops_trusting_the_path — every SECURITY DEFINER function this plugin\n-- owns stops resolving names the caller's way (#283).\n--\n-- The shape being closed: a definer function runs as its OWNER but resolves\n-- unqualified names using the CALLER's \\`search_path\\`. Anyone who can put an\n-- object earlier in that path gets it executed with the owner's rights. 157\n-- removed the ability to plant one in \\`public\\`; this removes the other half,\n-- which is the function trusting the path at all.\n--\n-- ── Why ALTER and not a re-emitted CREATE ───────────────────────────────────\n-- These functions are live in pools. Re-emitting each body to add one clause\n-- would copy hundreds of lines for a one-word change, and every copy is a\n-- chance to alter behaviour by accident. \\`ALTER FUNCTION … SET search_path\\`\n-- changes exactly the property in question and nothing else.\n--\n-- ── Why \\`public, pg_catalog\\` and not '' ─────────────────────────────────────\n-- The empty path is stricter and demands every name be schema-qualified — the\n-- right shape for something written today, and a rewrite of bodies written\n-- before the rule. What closes the hole is that the path is FIXED rather than\n-- inherited; these bodies already read \\`public\\` objects by bare name, so\n-- pinning to what they were written against keeps them correct and stops the\n-- caller having any say. It decides whether an upload path is acceptable — a check worth nothing if the caller can choose how its names resolve.\n-- ---------------------------------------------------------------------------\n\nDO $pin$\nDECLARE\n v_fn record;\n v_pinned integer := 0;\nBEGIN\n FOR v_fn IN\n SELECT p.oid, p.proname, pg_get_function_identity_arguments(p.oid) AS args\n FROM pg_proc p\n JOIN pg_namespace n ON n.oid = p.pronamespace\n WHERE n.nspname = 'public'\n AND p.prosecdef\n AND p.proname = ANY (ARRAY['scribe_audio_path_ok'])\n AND NOT EXISTS (\n SELECT 1 FROM unnest(coalesce(p.proconfig, ARRAY[]::text[])) AS cfg\n WHERE cfg LIKE 'search\\\\_path=%'\n )\n LOOP\n EXECUTE format('ALTER FUNCTION public.%I(%s) SET search_path = public, pg_catalog',\n v_fn.proname, v_fn.args);\n v_pinned := v_pinned + 1;\n END LOOP;\n IF v_pinned > 0 THEN\n RAISE NOTICE 'plugin-scribe: pinned search_path on % definer function(s)', v_pinned;\n END IF;\nEND $pin$;\n`\n\nexport const MIGRATION_003_THE_TEMPLATE_LINK_STOPS_BEING_A_FOREIGN_KEY = `-- ---------------------------------------------------------------------------\n-- 003_the_template_link_stops_being_a_foreign_key.sql — scribe stops reaching\n-- into another plugin's tables (#283, ADR 0015).\n--\n-- 001 put a FOREIGN KEY from \\`plg_scribe_generations.template_id\\` to\n-- \\`plg_forms_templates(id)\\`. It was guarded, so it never broke an install — and\n-- it was still the wrong instrument. A foreign key across two plugin prefixes\n-- couples two release cycles in the database: forms cannot drop or rename that\n-- table while scribe is installed, uninstalling forms fails with a dependency\n-- error rather than a decision, and a cluster that has scribe but not forms\n-- carries a link that means nothing.\n--\n-- What replaces it is not laxity. The column keeps its meaning and keeps\n-- pointing at the same rows; what goes is the DATABASE-LEVEL coupling. Reads go\n-- through forms' own view, and a template that disappears leaves a generation\n-- whose \\`template_id\\` no longer resolves — which the view reports as absent,\n-- exactly what \\`ON DELETE SET NULL\\` was arranging more expensively.\n--\n-- The general rule, stated once: a plugin owns its prefix. Anything it needs\n-- from another plugin comes through that plugin's view, its RPC, or an event —\n-- three doors that can all be closed without a migration.\n-- ---------------------------------------------------------------------------\n\nDO $$\nBEGIN\n IF EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'plg_scribe_generations_template_fk') THEN\n ALTER TABLE public.plg_scribe_generations\n DROP CONSTRAINT plg_scribe_generations_template_fk;\n RAISE NOTICE 'plugin-scribe 003: dropped the cross-prefix FK into plg_forms_templates';\n END IF;\nEND $$;\n\nCOMMENT ON COLUMN public.plg_scribe_generations.template_id IS\n 'The forms template this generation came from, by id and not by foreign key (003, ADR 0015). A soft reference on purpose: forms owns that table and must be able to release it without scribe''s permission. A row whose template is gone reads as absent, which is what the old ON DELETE SET NULL was arranging.';\n\n-- The read scribe actually needs, in one place, so no caller is tempted to join\n-- the other plugin's table by hand and re-create the coupling in SQL.\nDO $$\nBEGIN\n IF to_regclass('public.plg_scribe_generations') IS NULL THEN RETURN; END IF;\n\n IF to_regclass('public.plg_forms_templates') IS NOT NULL THEN\n EXECUTE $v$\n CREATE OR REPLACE VIEW public.v_scribe_generations\n WITH (security_invoker = true) AS\n SELECT g.*,\n t.id IS NOT NULL AS template_exists,\n t.name AS template_name\n FROM public.plg_scribe_generations g\n LEFT JOIN public.plg_forms_templates t ON t.id = g.template_id\n $v$;\n ELSE\n -- No forms on this cluster: the same columns, honestly empty, so a caller\n -- written against the view keeps working instead of failing to compile.\n EXECUTE $v$\n CREATE OR REPLACE VIEW public.v_scribe_generations\n WITH (security_invoker = true) AS\n SELECT g.*,\n false AS template_exists,\n NULL::text AS template_name\n FROM public.plg_scribe_generations g\n $v$;\n END IF;\n\n EXECUTE 'GRANT SELECT ON public.v_scribe_generations TO authenticated, service_role';\nEND $$;\n\nCOMMENT ON VIEW public.v_scribe_generations IS\n 'Generations with their forms template resolved by LEFT JOIN rather than by foreign key (003). \\`template_exists\\` is the honest answer on a cluster where forms is not installed, or where the template was deleted.';\n`\n\nexport const MIGRATIONS: Array<{ id: string; sql: string }> = [\n { id: \"001_scribe_base\", sql: MIGRATION_001_SCRIBE_BASE },\n { id: \"002_the_definer_function_stops_trusting_the_path\", sql: MIGRATION_002_THE_DEFINER_FUNCTION_STOPS_TRUSTING_THE_PATH },\n { id: \"003_the_template_link_stops_being_a_foreign_key\", sql: MIGRATION_003_THE_TEMPLATE_LINK_STOPS_BEING_A_FOREIGN_KEY },\n]\n","import { getSupabaseClientOptional } from '@fayz-ai/core'\nimport { registerTimelineSource, type TimelineItem } from '@fayz-ai/admin'\n\nconst STATUS_LABELS: Record<string, string> = {\n recording: 'Gravando',\n paused: 'Pausado',\n interrupted: 'Interrompido',\n uploading: 'Enviando',\n transcribing: 'Transcrevendo',\n ready: 'Pronto',\n generating: 'Rascunho',\n completed: 'Concluído',\n abandoned: 'Descartado',\n failed: 'Falhou',\n}\n\nfunction minutes(ms: unknown): string | undefined {\n const value = Number(ms)\n if (!Number.isFinite(value) || value <= 0) return undefined\n const total = Math.round(value / 60000)\n return total < 1 ? 'menos de 1 min' : `${total} min de áudio`\n}\n\n/**\n * Gravações de atendimento na linha do tempo da pessoa.\n *\n * `started_at` e não `created_at`: o que a pessoa procura é o dia do\n * atendimento, e uma gravação recuperada de uma sessão interrompida pode ter\n * sido criada bem depois de ter acontecido.\n */\nexport function registerScribeTimelineSource(): void {\n registerTimelineSource({\n id: 'recordings',\n label: 'Gravações',\n icon: 'Mic',\n // `magic` é o tom que o SDK já reserva para o que a IA produz.\n tone: 'magic',\n async fetch(args): Promise<TimelineItem[]> {\n const db = getSupabaseClientOptional() as any\n if (!db) return []\n\n let query = db\n .from('plg_scribe_sessions')\n .select('id, status, started_at, audio_duration_ms, transcript_chars')\n .eq('tenant_id', args.tenantId)\n .eq('subject_id', args.personId)\n .order('started_at', { ascending: false })\n .limit(args.limit)\n\n if (args.before) query = query.lt('started_at', args.before)\n\n const { data, error } = await query\n if (error) return []\n\n return (data ?? []).map((row: any) => ({\n id: `scribe:${row.id}`,\n sourceId: 'recordings',\n occurredAt: row.started_at,\n title: 'Atendimento gravado',\n description: minutes(row.audio_duration_ms),\n badge: STATUS_LABELS[row.status] ?? row.status ?? undefined,\n icon: 'Mic',\n targetTab: 'scribe',\n }))\n },\n })\n}\n","import * as React from 'react'\nimport { Mic } from 'lucide-react'\nimport {\n Button,\n Checkbox,\n Modal,\n ModalBody,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalTitle,\n} from '@fayz-ai/ui/primitives'\nimport { getActiveDeviceId, setPreferredDeviceId, switchInputDevice } from '../runtime'\n\n// Pick the microphone — with a level meter on EVERY row.\n//\n// A consulting room has four audio inputs and the browser picks one alone. A\n// list of names does not help: \"MacBook Pro Microphone\" and \"BlackHole 16ch\"\n// are equally plausible. Seeing which one is picking up sound does.\n//\n// The cost is one analysis stream per device while the dialog is open —\n// acceptable because it is ephemeral and closed on unmount.\n\nconst BARS = 8\n\ninterface DeviceRow {\n deviceId: string\n label: string\n}\n\n/** One meter per device, each on its own analysis stream. */\nfunction DeviceLevel({ deviceId, onSignal }: { deviceId: string; onSignal: (has: boolean) => void }) {\n const barsRef = React.useRef<Array<HTMLSpanElement | null>>([])\n\n React.useEffect(() => {\n let cancelled = false\n let stream: MediaStream | null = null\n let context: AudioContext | null = null\n let frame = 0\n let sawSignal = false\n\n const AudioCtor: typeof AudioContext | undefined =\n typeof window === 'undefined'\n ? undefined\n : window.AudioContext ??\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext\n if (!AudioCtor) return\n\n void navigator.mediaDevices\n .getUserMedia({ audio: { deviceId: { exact: deviceId } } })\n .then((granted) => {\n if (cancelled) {\n granted.getTracks().forEach((t) => t.stop())\n return\n }\n stream = granted\n context = new AudioCtor()\n const analyser = context.createAnalyser()\n analyser.fftSize = 128\n analyser.smoothingTimeConstant = 0.7\n context.createMediaStreamSource(granted).connect(analyser)\n const spectrum = new Uint8Array(analyser.frequencyBinCount)\n\n const tick = () => {\n if (cancelled) return\n analyser.getByteFrequencyData(spectrum)\n let sum = 0\n for (const v of spectrum) sum += v\n const level = sum / spectrum.length / 255\n if (!sawSignal && level > 0.02) {\n sawSignal = true\n onSignal(true)\n }\n for (let i = 0; i < BARS; i++) {\n const bar = barsRef.current[i]\n if (!bar) continue\n const lit = level * BARS > i\n bar.style.opacity = lit ? '1' : '0.25'\n }\n frame = requestAnimationFrame(tick)\n }\n frame = requestAnimationFrame(tick)\n })\n .catch(() => {\n /* device busy or denied — the row renders without a meter */\n })\n\n return () => {\n cancelled = true\n if (frame) cancelAnimationFrame(frame)\n stream?.getTracks().forEach((t) => t.stop())\n void context?.close().catch(() => {})\n }\n }, [deviceId, onSignal])\n\n return (\n <div className=\"flex items-center gap-[2px]\" aria-hidden>\n {Array.from({ length: BARS }, (_, i) => (\n <span\n key={i}\n ref={(el) => {\n barsRef.current[i] = el\n }}\n className=\"h-3.5 w-[3px] rounded-sm bg-current opacity-25 transition-opacity duration-75\"\n />\n ))}\n </div>\n )\n}\n\nexport interface ScribeDevicePickerProps {\n open: boolean\n onOpenChange: (open: boolean) => void\n}\n\nexport function ScribeDevicePicker({ open, onOpenChange }: ScribeDevicePickerProps) {\n const [devices, setDevices] = React.useState<DeviceRow[]>([])\n const [selected, setSelected] = React.useState<string | null>(null)\n const [remember, setRemember] = React.useState(true)\n const [signals, setSignals] = React.useState<Record<string, boolean>>({})\n const [busy, setBusy] = React.useState(false)\n\n React.useEffect(() => {\n if (!open) return\n let cancelled = false\n const load = async () => {\n try {\n // Labels only exist once permission is granted; without it the list\n // comes back nameless and helps nobody.\n const probe = await navigator.mediaDevices.getUserMedia({ audio: true })\n probe.getTracks().forEach((t) => t.stop())\n const all = await navigator.mediaDevices.enumerateDevices()\n if (cancelled) return\n const inputs = all\n .filter((d) => d.kind === 'audioinput' && d.deviceId)\n .map((d, i) => ({ deviceId: d.deviceId, label: d.label || `Microfone ${i + 1}` }))\n setDevices(inputs)\n setSelected(getActiveDeviceId() ?? inputs[0]?.deviceId ?? null)\n } catch {\n setDevices([])\n }\n }\n void load()\n return () => {\n cancelled = true\n }\n }, [open])\n\n const markSignal = React.useCallback((deviceId: string) => {\n setSignals((prev) => (prev[deviceId] ? prev : { ...prev, [deviceId]: true }))\n }, [])\n\n const confirm = async () => {\n if (!selected) return\n setBusy(true)\n try {\n await switchInputDevice(selected)\n setPreferredDeviceId(remember ? selected : null)\n onOpenChange(false)\n } finally {\n setBusy(false)\n }\n }\n\n return (\n <Modal open={open} onOpenChange={onOpenChange}>\n <ModalContent className=\"max-w-md\">\n <ModalHeader>\n <ModalTitle className=\"flex items-center gap-2\">\n <Mic className=\"h-4 w-4\" />\n Selecionar microfone\n </ModalTitle>\n </ModalHeader>\n <ModalBody className=\"space-y-3\">\n <p className=\"text-xs text-muted-foreground\">\n {devices.length} dispositivo{devices.length === 1 ? '' : 's'} encontrado\n {devices.length === 1 ? '' : 's'}\n </p>\n\n <div className=\"max-h-64 space-y-1.5 overflow-y-auto\">\n {devices.map((device) => (\n <button\n key={device.deviceId}\n type=\"button\"\n onClick={() => setSelected(device.deviceId)}\n className={`flex w-full items-center gap-3 rounded-lg border p-2.5 text-left transition-colors ${\n selected === device.deviceId\n ? 'border-primary bg-primary/5'\n : 'border-border hover:bg-muted/50'\n }`}\n >\n <Mic\n className={`h-4 w-4 shrink-0 ${signals[device.deviceId] ? 'text-primary' : 'text-muted-foreground'}`}\n />\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate text-[13px] font-medium text-foreground\">{device.label}</span>\n <span className=\"block text-[11px] text-muted-foreground\">\n {signals[device.deviceId] ? 'Captando áudio' : 'Sem áudio detectado'}\n </span>\n </span>\n {open && (\n <span className=\"shrink-0 text-primary\">\n <DeviceLevel deviceId={device.deviceId} onSignal={() => markSignal(device.deviceId)} />\n </span>\n )}\n </button>\n ))}\n {devices.length === 0 && (\n <p className=\"py-6 text-center text-xs text-muted-foreground\">\n Nenhum microfone disponível. Verifique a permissão do navegador.\n </p>\n )}\n </div>\n\n <label className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <Checkbox checked={remember} onChange={setRemember} />\n Lembrar minha escolha\n </label>\n </ModalBody>\n\n <ModalFooter>\n <Button variant=\"outline\" size=\"sm\" onClick={() => onOpenChange(false)}>\n Cancelar\n </Button>\n <Button size=\"sm\" disabled={!selected || busy} onClick={() => void confirm()}>\n Confirmar\n </Button>\n </ModalFooter>\n </ModalContent>\n </Modal>\n )\n}\n","import * as React from 'react'\nimport { Mic, Pause, Play, Settings2, Square } from 'lucide-react'\nimport { Button } from '@fayz-ai/ui/primitives'\nimport { useScribeStore, formatElapsed } from '../store'\nimport { endSession, getSnapshot, pauseSession, resumeSession } from '../runtime'\nimport { useLiveTranscript } from '../lib/live-transcript'\nimport { TranscriptPanel } from './TranscriptPanel'\nimport { ScribeDevicePicker } from './ScribeDevicePicker'\nimport type { ScribeLabels } from '../types'\n\n// The encounter as it happens: controls plus the transcript growing.\n//\n// ON \"REAL TIME\": the pipeline transcribes 30s CLIPS after upload, not word by\n// word, so text arrives in blocks. Faking a stream — letters appearing out of a\n// block that already arrived whole — would be an animation lying about the\n// system's latency. Real word-by-word needs a streaming STT transport.\n\nexport interface ScribeLivePanelProps {\n labels: ScribeLabels\n onOpenSession?: (sessionId: string) => void\n}\n\nexport function ScribeLivePanel({ labels, onOpenSession }: ScribeLivePanelProps) {\n const state = useScribeStore((s) => s.state)\n const sessionId = useScribeStore((s) => s.sessionId)\n const subjectName = useScribeStore((s) => s.subjectName)\n // From the shared watcher: the agent needs the transcript with this screen\n // closed, so the fetching cannot live here.\n const segments = useLiveTranscript((s) => s.segments)\n const [busy, setBusy] = React.useState(false)\n const [pickerOpen, setPickerOpen] = React.useState(false)\n const [, force] = React.useReducer((n: number) => n + 1, 0)\n\n const live = state === 'recording' || state === 'paused' || state === 'interrupted'\n\n // Sample faster than the unit shown, and read elapsed fresh.\n React.useEffect(() => {\n if (state !== 'recording') return\n const timer = setInterval(() => {\n if (document.visibilityState === 'visible') force()\n }, 250)\n return () => clearInterval(timer)\n }, [state])\n\n const elapsed = live ? getSnapshot().elapsedMs : 0\n const transcribed = segments.filter((s) => s.text?.trim()).length\n const pending = live || segments.some((s) => !s.gap && !s.text)\n\n const toggle = async () => {\n setBusy(true)\n try {\n if (state === 'recording') await pauseSession()\n else await resumeSession()\n } finally {\n setBusy(false)\n }\n }\n\n const finish = async () => {\n setBusy(true)\n try {\n const id = await endSession()\n if (id) onOpenSession?.(id)\n } finally {\n setBusy(false)\n }\n }\n\n if (!sessionId) {\n return (\n <div className=\"flex flex-1 flex-col items-center justify-center gap-2 p-6 text-center text-sm text-muted-foreground\">\n <Mic className=\"h-5 w-5\" />\n <p className=\"font-medium text-foreground\">{labels.sessionPlural}</p>\n <p className=\"text-xs\">\n Inicie um {labels.sessionSingular.toLowerCase()} pela ficha do {labels.subject.toLowerCase()} ou pelo\n botão no chat. A transcrição aparece aqui enquanto você atende.\n </p>\n </div>\n )\n }\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <div className=\"flex items-center gap-2 border-b border-border/40 px-3 py-2\">\n <span className=\"relative inline-flex h-2 w-2 shrink-0\">\n {state === 'recording' && (\n <span className=\"absolute inline-flex h-full w-full animate-ping rounded-full bg-destructive opacity-60\" />\n )}\n <span className=\"relative inline-flex h-2 w-2 rounded-full bg-destructive\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-[12.5px] font-medium text-foreground\">\n {subjectName ?? labels.sessionSingular}\n </p>\n <p className=\"text-[11px] tabular-nums text-muted-foreground\">\n {formatElapsed(elapsed)}\n {transcribed > 0 && ` · ${transcribed} trecho${transcribed > 1 ? 's' : ''}`}\n </p>\n </div>\n {live && (\n <>\n <Button variant=\"ghost\" size=\"sm\" className=\"h-7 px-2\" disabled={busy} onClick={() => void toggle()}>\n {state === 'recording' ? <Pause className=\"h-3.5 w-3.5\" /> : <Play className=\"h-3.5 w-3.5\" />}\n <span className=\"ml-1 text-[11px]\">{state === 'recording' ? labels.pause : labels.resume}</span>\n </Button>\n <Button variant=\"ghost\" size=\"sm\" className=\"h-7 px-2\" disabled={busy} onClick={() => void finish()}>\n <Square className=\"h-3.5 w-3.5\" />\n <span className=\"ml-1 text-[11px]\">{labels.finish}</span>\n </Button>\n {/* Swapping mid-session: finding out later is finding out when the\n audio is already gone. */}\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-7 w-7 p-0\"\n title=\"Selecionar microfone\"\n aria-label=\"Selecionar microfone\"\n onClick={() => setPickerOpen(true)}\n >\n <Settings2 className=\"h-3.5 w-3.5\" />\n </Button>\n </>\n )}\n </div>\n\n <div className=\"min-h-0 flex-1 overflow-y-auto p-3\">\n <TranscriptPanel\n segments={segments}\n pending={pending}\n emptyLabel={`Nenhuma transcrição ainda neste ${labels.sessionSingular.toLowerCase()}.`}\n />\n </div>\n\n <ScribeDevicePicker open={pickerOpen} onOpenChange={setPickerOpen} />\n </div>\n )\n}\n","// ---------------------------------------------------------------------------\n// Scribe vocabulary — deliberately vertical-neutral.\n//\n// A \"session\" is a recorded encounter: a salon appointment, a clinic visit, a\n// meeting at a law firm. The \"subject\" is the person it is about (a `people`\n// row), the \"operator\" runs it. If a type here needs a clinical word, the\n// abstraction failed — the vertical supplies those through `labels`.\n// ---------------------------------------------------------------------------\n\n/**\n * SERVER-side session state. Advances only on durable facts: a segment that\n * reached Storage, a transcript that came back. Client state, which can die at\n * any moment, lives separately in `CaptureState`.\n */\nexport type SessionStatus =\n | 'recording'\n | 'paused'\n | 'interrupted'\n | 'uploading'\n | 'transcribing'\n | 'ready'\n | 'generating'\n | 'completed'\n | 'abandoned'\n | 'failed'\n\n/** States where the session is still someone's — used by the partial index and recovery. */\nexport const OPEN_SESSION_STATUSES: SessionStatus[] = [\n 'recording', 'paused', 'interrupted', 'uploading', 'transcribing', 'ready', 'generating',\n]\n\n/**\n * CLIENT-side capture state. `interrupted` is the honest state between \"the mic\n * is gone\" and \"the user decided what to do\" — collapsing it into `paused` would\n * lose the distinction between intent and accident, which the transcript needs\n * in order to record the hole.\n */\nexport type CaptureState =\n | 'idle'\n | 'consent'\n | 'requesting'\n | 'recording'\n | 'paused'\n | 'interrupted'\n | 'stopping'\n | 'error'\n\n/** What the encounter is attached to. `standalone` records first, attaches later. */\nexport type SessionContextKind = 'person' | 'appointment' | 'standalone'\n\n/**\n * Legal basis recorded at capture time. Consent is what separates the recording\n * from a problem, and \"when\" and \"how\" is what gets proven later.\n * `implied_contract` covers recording as part of a contracted service.\n */\nexport type ConsentMode = 'verbal' | 'written' | 'implied_contract'\n\nexport type UploadState = 'pending' | 'uploading' | 'uploaded' | 'failed' | 'missing'\nexport type SttState = 'pending' | 'running' | 'done' | 'failed' | 'skipped'\nexport type GenerationStatus = 'pending' | 'running' | 'ready' | 'failed' | 'committed' | 'discarded'\n\n// ---------------------------------------------------------------------------\n// Server rows\n// ---------------------------------------------------------------------------\n\nexport interface ScribeSession {\n id: string\n tenantId: string\n subjectId?: string\n subjectName?: string\n contextKind: SessionContextKind\n appointmentId?: string\n ownerUserId: string\n status: SessionStatus\n locale: string\n mimeType?: string\n startedAt: string\n endedAt?: string\n /** Wall clock reported by the client. Moves the pill; not a record. */\n wallDurationMs: number\n /** SUM(segments.duration_ms) — the only duration that counts for records, limits and billing. */\n audioDurationMs: number\n segmentCount: number\n uploadedSegmentCount: number\n transcribedSegmentCount: number\n transcriptChars: number\n consentAt?: string\n consentMode?: ConsentMode\n retentionUntil?: string\n audioDeletedAt?: string\n error?: string\n metadata: Record<string, unknown>\n}\n\n/** A word, offset RELATIVE to the start of its segment. */\nexport interface TranscriptWord {\n /** word */\n w: string\n /** start ms, relative to the segment */\n s: number\n /** end ms, relative to the segment */\n e: number\n /** confidence 0..1 */\n c?: number\n /** speaker index, when diarized */\n sp?: number\n}\n\nexport interface ScribeSegment {\n sessionId: string\n segIndex: number\n tenantId: string\n storagePath?: string\n bytes?: number\n /** Offset of this segment from the start of the session. */\n startOffsetMs: number\n /** From the STT response, not a JS clock. */\n durationMs?: number\n /** Assembled from pieces of a segment interrupted mid-way. */\n partial: boolean\n /**\n * SYNTHETIC row marking a hole (sleep, mic lost, permission revoked). Carries\n * no audio. It exists so the transcript admits the hole instead of silently\n * splicing two moments together — a medico-legal requirement, not polish.\n */\n gap: boolean\n uploadState: UploadState\n uploadedAt?: string\n sttState: SttState\n sttProvider?: string\n sttAttempts: number\n sttError?: string\n text?: string\n words?: TranscriptWord[]\n confidence?: number\n}\n\nexport interface ScribeGeneration {\n id: string\n tenantId: string\n sessionId: string\n /** Tenant template row. NULL when the generation came from a code preset. */\n templateId?: string\n /** Preset key (`beauty.anamnesis.v1`) when there is no row. */\n templateKey?: string\n tabOrder: number\n title?: string\n status: GenerationStatus\n /** Model output. Written once, NEVER mutated. */\n markdown?: string\n /** The human's working copy. The difference between the two is what proves review. */\n markdownEdited?: string\n /** Stage-1 extraction (two-stage). Reserved; the column exists from v0 on purpose. */\n facts?: Record<string, unknown>\n model?: string\n promptVersion?: string\n inputTokens?: number\n outputTokens?: number\n error?: string\n documentId?: string\n createdBy?: string\n createdAt: string\n}\n\n// ---------------------------------------------------------------------------\n// Narrative templates\n//\n// A narrative template describes PROSE SECTIONS, not form fields — the exact\n// point where a vertical expresses itself. The same capture, transcription and\n// generation engines serve an anamnesis and a legal draft; only the section list\n// changes. Stored in `plg_forms_templates.schema`, discriminated by `kind`.\n// ---------------------------------------------------------------------------\n\nexport type SectionShape = 'prose' | 'bullets' | 'keyvalue'\n\nexport interface NarrativeSectionDef {\n /** Stable machine key. NEVER translated — it outlives heading renames. */\n id: string\n /** Becomes `## <heading>` in the markdown. Translatable. */\n heading: string\n /**\n * The one per-section prompt the model sees. Write it as an instruction to a\n * junior: what belongs here, what does not, in which voice.\n */\n guidance: string\n /**\n * Controls the SHAPE of the markdown. `keyvalue` exists so structured data\n * comes out as `- **Label:** value` rather than a pipe table — the SDK\n * renderer has no table, and a broken A4 prints garbage.\n */\n shape?: SectionShape\n required?: boolean\n /** Drops the whole section, heading included, when the transcript said nothing. */\n omitWhenEmpty?: boolean\n maxWords?: number\n /** Fact keys this section consumes in two-stage mode. Reserved. */\n facts?: string[]\n}\n\n/** Where each header field is pulled from. Resolved from DATA, never asked of the model. */\nexport type NarrativeHeaderSource =\n | 'subject.name'\n | 'subject.documentNumber'\n | 'subject.birthDate'\n | 'session.startedAt'\n | 'session.durationMinutes'\n | 'operator.name'\n | (string & {})\n\nexport interface NarrativeSchema {\n kind: 'narrative'\n sections: NarrativeSectionDef[]\n /** Tone, person, tense, jargon level. Injected ahead of the sections. */\n style?: string\n /** OUTPUT language, independent of the transcript's. */\n outputLocale?: string\n /** Block above the first section, resolved from data. */\n header?: { fields: Array<{ label: string; source: NarrativeHeaderSource }> }\n /** Footer, e.g. 'AI-generated from a recording, reviewed by the professional.' */\n disclaimer?: string\n /**\n * Things the model must report as \"not stated\" instead of inferring, e.g.\n * ['diagnosis', 'dosage', 'ICD']. The line between summarising and inventing.\n */\n neverInfer?: string[]\n}\n\nexport function isNarrativeSchema(schema: unknown): schema is NarrativeSchema {\n return !!schema && typeof schema === 'object' && (schema as NarrativeSchema).kind === 'narrative'\n}\n\n/**\n * A system template, defined in CODE rather than in a SQL seed. Fixing a prompt\n * becomes a release instead of a migration editing a row the user may have\n * changed, and the default does not eat the tenant's template quota. Customising\n * forks it, and the fork is the row.\n */\nexport interface NarrativeTemplatePreset {\n /** Versioned in the key itself: `beauty.anamnesis.v1`. */\n key: string\n name: string\n description?: string\n /** plugin-forms category: anamnesis | evolution | report | contract | general. */\n category: string\n specialty?: string\n schema: NarrativeSchema\n}\n\n// ---------------------------------------------------------------------------\n// Plugin configuration — a vertical's whole surface\n// ---------------------------------------------------------------------------\n\nexport interface ScribeLabels {\n /** \"Appointment\" / \"Visit\" / \"Meeting\" */\n sessionSingular: string\n sessionPlural: string\n /** \"Start appointment\" */\n start: string\n pause: string\n resume: string\n finish: string\n discard: string\n /** \"Generate document\" / \"Generate draft\" */\n generate: string\n /** \"Client\" / \"Patient\" */\n subject: string\n transcript: string\n settingsTitle: string\n}\n\n/**\n * Where the model credential lives. See `lib/transport.ts` for why there are\n * exactly these two values and no third.\n */\nexport type ScribeTransportKind = 'fayz' | 'edge'\n\nexport interface ScribeSttConfig {\n /**\n * Defaults to `'fayz'`: the platform API, the same broker the app assistant\n * already uses — no secret and no deploy on the app side. `'edge'` keeps the\n * own-edge-function path, for anyone running off-platform or needing a\n * provider the broker does not offer.\n */\n transport?: ScribeTransportKind\n /**\n * Provider behind a one-method seam — swapping must not be a refactor. The\n * platform broker speaks OpenAI; Deepgram requires `transport: 'edge'`.\n */\n provider: 'deepgram' | 'openai'\n /**\n * Explicit, with no hidden default, because the choice shows: `whisper-1`\n * returns duration and per-word offsets, while the `gpt-4o-*-transcribe`\n * models are usually better at pt-BR but return text only — and then the\n * authoritative duration has to come from somewhere else.\n */\n model?: string\n locale: string\n /** Only Deepgram diarizes today; ignored on the `fayz` transport. */\n diarize?: boolean\n /**\n * Transcription edge function name. REQUIRED when `transport: 'edge'`: with no\n * transport configured the plugin refuses to boot, because the only remaining\n * fallback would be the browser's native recognizer, which ships the session\n * audio to an undeclared third party. See the invariant in `createScribePlugin`.\n */\n endpoint?: string\n}\n\nexport interface ScribeRetentionConfig {\n /** Days until the audio may be deleted. Transcript and document survive. */\n audioDays: number\n /** When true, no byte is recorded before the consent gate. */\n requireConsent: boolean\n defaultConsentMode: ConsentMode\n}\n\nexport interface ScribeLimitsConfig {\n minutesMonth?: string\n documentsMonth?: string\n}\n\nexport interface ScribePluginOptions {\n /** `kind` of the `people` row allowed as subject ('customer', 'client', …). */\n subjectKind?: string\n contextEntities?: string[]\n /**\n * Detail pages that get the \"start session\" button in their header.\n * Defaults to the person-like entity names the pools use today.\n */\n subjectEntities?: string[]\n /**\n * Where a recording is READ.\n *\n * A recording is not a place of its own — it is something a person's record\n * contains, like a document or an invoice. The default therefore opens it on\n * the holder's own page (`/clients/:subjectId/scribe/:sessionId`, from the\n * first `subjectEntities` name), and only falls back to the standalone route\n * when the session has no holder yet — which is exactly what `standalone`\n * capture is for.\n *\n * A vertical whose people do not live at `/{entity}` says so here.\n */\n sessionPath?: (args: { sessionId: string; subjectId?: string }) => string\n /**\n * Activate for every tenant of this app unless a binding says otherwise.\n *\n * Ships OFF: recording audio is not something a generic app should acquire by\n * accident. But leaving it off in a vertical built around the encounter makes\n * it unreachable — the only screen that could enable a plugin is its own\n * settings tab, which does not render until it is enabled.\n */\n enabledByDefault?: boolean\n labels?: Partial<ScribeLabels>\n /** The vertical's default templates — the ONLY new code a vertical writes. */\n templatePresets?: NarrativeTemplatePreset[]\n stt: ScribeSttConfig\n retention?: Partial<ScribeRetentionConfig>\n limits?: ScribeLimitsConfig\n /** Generation edge function name. */\n generateEndpoint?: string\n scope?: 'universal' | 'vertical'\n verticalId?: string\n}\n","import React from 'react'\nimport type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'\nimport { registerTranslations } from '@fayz-ai/core'\nimport { scribeLocales } from './locales'\nimport { initScribeRuntime } from './runtime'\nimport { wireScribeStore } from './store'\nimport { ScribeShellMount } from './components/ScribeShellMount'\nimport { SCRIBE_AGENT_ROLE, scribeSessionPath } from './lib/start'\nimport { ScribeSessionPage } from './components/ScribeSessionPage'\nimport { ScribeHeaderAction } from './components/ScribeHeaderAction'\nimport { MIGRATION_001_SCRIBE_BASE } from './migrations'\nimport { assertSttConfig } from './lib/config'\nimport { registerScribeTimelineSource } from './lib/timeline-source'\nimport type {\n ScribeLabels,\n ScribePluginOptions,\n ScribeRetentionConfig,\n} from './types'\n\n// The plugin's vocabulary names what IT owns: a recording. \"Atendimento\" is the\n// encounter — what the agenda books, what the client pays for — and belongs to\n// the app. While scribe called itself that, its tab competed with the record's\n// own encounter tab and nobody could tell them apart.\nconst DEFAULT_LABELS: ScribeLabels = {\n sessionSingular: 'Gravação',\n sessionPlural: 'Gravações',\n start: 'Gravar atendimento',\n pause: 'Pausar',\n resume: 'Retomar',\n finish: 'Finalizar',\n discard: 'Descartar',\n generate: 'Gerar documento',\n subject: 'Cliente',\n transcript: 'Transcrição',\n settingsTitle: 'Gravação de atendimentos',\n}\n\nconst DEFAULT_RETENTION: ScribeRetentionConfig = {\n audioDays: 90,\n requireConsent: true,\n defaultConsentMode: 'verbal',\n}\n\nexport function createScribePlugin(options: ScribePluginOptions): PluginManifest {\n // -------------------------------------------------------------------------\n // O invariante que justifica lançar na construção.\n //\n // Sem um transporte de STT próprio, a única alternativa a mão seria o\n // reconhecedor nativo do browser — que envia o áudio do microfone para\n // servidores do Google. Para um atendimento isso significa um operador não\n // declarado processando dado sensível, sem DPA, sem base documentada de\n // transferência internacional, e sem como honrar um pedido de eliminação:\n // não dá para pedir a deleção do que você nunca soube que foi recebido.\n //\n // Falhar aqui, alto e no boot, é infinitamente melhor que degradar em\n // silêncio para o caminho que ninguém escolheu.\n //\n // `transport: 'fayz'` (o default) satisfaz o invariante sozinho: o broker da\n // plataforma É o endpoint próprio. `transport: 'edge'` continua exigindo o\n // nome da função.\n // -------------------------------------------------------------------------\n assertSttConfig(options?.stt)\n\n const labels: ScribeLabels = { ...DEFAULT_LABELS, ...options.labels }\n const retention: ScribeRetentionConfig = { ...DEFAULT_RETENTION, ...options.retention }\n const presets = options.templatePresets ?? []\n const generateEndpoint = options.generateEndpoint ?? 'scribe-generate'\n const locale = options.stt.locale ?? 'pt-BR'\n\n registerTranslations(scribeLocales)\n\n // Gravação de atendimento passa a aparecer na linha do tempo do cliente.\n registerScribeTimelineSource()\n\n // O runtime sobe AQUI, na construção do manifesto — não num efeito de\n // componente. É o que faz a recuperação de uma sessão de ontem começar antes\n // de qualquer tela existir.\n initScribeRuntime({ retention })\n wireScribeStore()\n\n // Which record types can be a session's subject. The plugin cannot know what\n // a vertical calls its people, so the vertical says.\n const subjectEntities = options.subjectEntities ?? ['clients', 'patients', 'people', 'persons', 'customers']\n\n // A gravação abre DENTRO da ficha de quem foi atendido — ver scribeSessionPath.\n // A rota solta continua existindo para a sessão que ainda não tem titular, e\n // como destino de link antigo.\n const sessionPath =\n options.sessionPath ??\n ((args: { sessionId: string; subjectId?: string }) =>\n scribeSessionPath({ ...args, subjectEntity: subjectEntities[0] }))\n\n const openSession = (sessionId: string, subjectId?: string) => {\n window.location.hash = sessionPath({ sessionId, subjectId })\n }\n\n const ShellMount: React.FC = () =>\n React.createElement(ScribeShellMount, { labels, retention, stt: options.stt, locale, onOpenSession: openSession })\n ShellMount.displayName = 'ScribeShellMount'\n\n // A rota solta, agora só para a gravação SEM titular — e para curar link\n // antigo: assim que a linha chega e tem titular, a tela se redireciona para a\n // ficha dele, onde a gravação de fato mora.\n const SessionRoute: React.FC = () => {\n const hash = typeof window !== 'undefined' ? window.location.hash : ''\n const sessionId = hash.split('/scribe/')[1]?.split('/')[0] ?? ''\n if (!sessionId) return null\n return React.createElement(ScribeSessionPage, {\n sessionId,\n labels,\n presets,\n stt: options.stt,\n generateEndpoint,\n onSessionLoaded: (session) => {\n if (session.subjectId) openSession(session.id, session.subjectId)\n },\n onBack: () => {\n window.location.hash = `/${subjectEntities[0]}`\n },\n onOpenDocument: () => {\n window.location.hash = `/${subjectEntities[0]}`\n },\n })\n }\n SessionRoute.displayName = 'ScribeSessionRoute'\n\n const HeaderAction: React.FC<Record<string, unknown>> = (props) =>\n React.createElement(ScribeHeaderAction, { ...(props as object), labels })\n HeaderAction.displayName = 'ScribeHeaderAction'\n\n return {\n id: 'scribe',\n name: labels.sessionPlural,\n icon: 'Mic',\n version: '0.1.0',\n scope: (options.scope ?? 'universal') as PluginScope,\n verticalId: options.verticalId as VerticalId | undefined,\n defaultEnabled: options.enabledByDefault ?? false,\n // O scribe EMITE documento mas não é dono do conceito. plugin-forms é a\n // engine de documento; este é uma engine de aquisição. A dependência aponta\n // nessa direção e não na inversa — senão todo app que só quer uma anamnese\n // impressa passaria a depender de bucket, duas edge functions e chave de\n // modelo.\n dependencies: ['custom_forms'],\n\n // O trabalho deste plugin é de quem está NA SALA — grava o atendimento e o\n // transforma em documento. O plugin diz o papel; o app diz o nome (no salão,\n // a Auxiliar de Atendimento). Ver AgentRole em @fayz-ai/core.\n defaultAgentRole: SCRIBE_AGENT_ROLE,\n\n navigation: [],\n\n routes: [\n {\n path: '/scribe/*',\n component: SessionRoute as unknown as React.ComponentType<unknown>,\n },\n ],\n\n widgets: [\n {\n id: 'scribe-shell-mount',\n // A única zona global verificadamente renderizada (AdminShell.tsx:979-980)\n // e que fica FORA do switch de rota. Navegar não a desmonta.\n zone: 'shell.topbar.end',\n component: ShellMount as unknown as React.ComponentType<unknown>,\n order: -10,\n },\n // Next to Edit on the person record. From the plugin on purpose: it only\n // renders where the engine listening for `scribe:start` is also mounted,\n // so there is no version of it that does nothing.\n ...subjectEntities.map((entity) => ({\n id: `scribe-start-${entity}`,\n zone: `${entity}.detail.header.actions`,\n component: HeaderAction as unknown as React.ComponentType<unknown>,\n order: -10,\n })),\n ],\n\n declaredFeatures: [\n { id: 'scribe', label: labels.settingsTitle, actions: ['read', 'create', 'edit', 'delete'] },\n ],\n\n declaredLimits: [\n // Cota entra no dia um, não depois: a 1000 atendimentos/mês o STT bruto\n // já é a maior linha de custo variável do produto.\n {\n key: options.limits?.minutesMonth ?? 'scribe_minutes_month',\n label: 'Minutos de gravação por mês',\n table: 'plg_scribe_sessions',\n period: 'month',\n },\n {\n key: options.limits?.documentsMonth ?? 'scribe_documents_month',\n label: 'Documentos gerados por mês',\n table: 'plg_scribe_generations',\n period: 'month',\n },\n ] as PluginManifest['declaredLimits'],\n\n aiTools: [\n {\n id: 'scribe.list-sessions',\n name: 'listScribeSessions',\n description: `Lista ${labels.sessionPlural.toLowerCase()} gravados, com data, duração e status.`,\n icon: 'Mic',\n mode: 'read' as const,\n category: labels.sessionPlural,\n permission: { feature: 'scribe', action: 'read' as const },\n parameters: {\n type: 'object' as const,\n properties: {\n subjectId: { type: 'string' as const, description: 'Filtra pelo id do titular' },\n status: { type: 'string' as const, description: 'Filtra pelo status da sessão' },\n },\n },\n suggestions: [\n { label: `Quais ${labels.sessionPlural.toLowerCase()} ainda não viraram documento?` },\n ],\n },\n ],\n\n migrations: [\n {\n id: 'scribe-001-base',\n version: '0.1.0',\n sql: MIGRATION_001_SCRIBE_BASE,\n description:\n 'Cria plg_scribe_sessions/_segments/_generations, o bucket privado scribe-audio com guard de caminho, e o RPC scribe_record_segment',\n },\n ],\n\n locales: scribeLocales,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Re-exports\n// ---------------------------------------------------------------------------\n\nexport {\n requestScribeStart,\n startScribeSession,\n scribeSessionPath,\n SCRIBE_START_EVENT,\n SCRIBE_AGENT_ROLE,\n} from './lib/start'\nexport type { ScribeStartEventDetail } from './lib/start'\nexport { ScribeStartButton } from './components/ScribeRecordingPill'\nexport { ScribeLivePanel } from './components/ScribeLivePanel'\nexport { ScribeDevicePicker } from './components/ScribeDevicePicker'\nexport { ScribeSessionPage } from './components/ScribeSessionPage'\nexport { useScribeStore, formatElapsed } from './store'\nexport { PROMPT_VERSION, formatOffset, parseSections, renderTranscript } from './lib/prompt'\nexport {\n beginSession,\n endSession,\n pauseSession,\n resumeSession,\n discardSession,\n isCaptureSupported,\n} from './runtime'\nexport {\n fetchSession,\n fetchSegments,\n fetchGenerations,\n fetchSessionsForSubject,\n fetchOpenSessions,\n deleteSessionAudio,\n} from './data/supabase'\nexport type {\n ScribePluginOptions,\n ScribeLabels,\n ScribeSession,\n ScribeSegment,\n ScribeGeneration,\n NarrativeSchema,\n NarrativeSectionDef,\n NarrativeTemplatePreset,\n SessionStatus,\n CaptureState,\n ConsentMode,\n} from './types'\nexport { isNarrativeSchema } from './types'\n"]}
|