@fayz-ai/plugin-scribe 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/components/DraftPanel.d.ts +14 -0
- package/dist/components/DraftPanel.d.ts.map +1 -0
- package/dist/components/ScribeConsentDialog.d.ts +12 -0
- package/dist/components/ScribeConsentDialog.d.ts.map +1 -0
- package/dist/components/ScribeRecordingPill.d.ts +16 -0
- package/dist/components/ScribeRecordingPill.d.ts.map +1 -0
- package/dist/components/ScribeRecoveryBanner.d.ts +8 -0
- package/dist/components/ScribeRecoveryBanner.d.ts.map +1 -0
- package/dist/components/ScribeSessionPage.d.ts +13 -0
- package/dist/components/ScribeSessionPage.d.ts.map +1 -0
- package/dist/components/ScribeShellMount.d.ts +19 -0
- package/dist/components/ScribeShellMount.d.ts.map +1 -0
- package/dist/components/TranscriptPanel.d.ts +10 -0
- package/dist/components/TranscriptPanel.d.ts.map +1 -0
- package/dist/data/supabase.d.ts +63 -0
- package/dist/data/supabase.d.ts.map +1 -0
- package/dist/data/tables.d.ts +31 -0
- package/dist/data/tables.d.ts.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2910 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +19 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.test.d.ts +2 -0
- package/dist/lib/config.test.d.ts.map +1 -0
- package/dist/lib/drain.d.ts +13 -0
- package/dist/lib/drain.d.ts.map +1 -0
- package/dist/lib/generate.d.ts +23 -0
- package/dist/lib/generate.d.ts.map +1 -0
- package/dist/lib/prompt.d.ts +39 -0
- package/dist/lib/prompt.d.ts.map +1 -0
- package/dist/lib/prompt.test.d.ts +2 -0
- package/dist/lib/prompt.test.d.ts.map +1 -0
- package/dist/lib/transport.d.ts +59 -0
- package/dist/lib/transport.d.ts.map +1 -0
- package/dist/locales/en.d.ts +2 -0
- package/dist/locales/en.d.ts.map +1 -0
- package/dist/locales/index.d.ts +2 -0
- package/dist/locales/index.d.ts.map +1 -0
- package/dist/locales/pt-BR.d.ts +2 -0
- package/dist/locales/pt-BR.d.ts.map +1 -0
- package/dist/migrations/index.d.ts +6 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/runtime/capture.d.ts +84 -0
- package/dist/runtime/capture.d.ts.map +1 -0
- package/dist/runtime/idb.d.ts +121 -0
- package/dist/runtime/idb.d.ts.map +1 -0
- package/dist/runtime/index.d.ts +48 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/pump.d.ts +11 -0
- package/dist/runtime/pump.d.ts.map +1 -0
- package/dist/store.d.ts +25 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/types.d.ts +266 -0
- package/dist/types.d.ts.map +1 -0
- package/functions/scribe-generate/index.ts +255 -0
- package/functions/scribe-transcribe/index.ts +455 -0
- package/package.json +58 -0
|
@@ -0,0 +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/components/ScribeShellMount.tsx","../src/lib/prompt.ts","../src/components/TranscriptPanel.tsx","../src/data/supabase.ts","../src/components/DraftPanel.tsx","../src/lib/config.ts","../src/lib/transport.ts","../src/lib/drain.ts","../src/lib/generate.ts","../src/components/ScribeSessionPage.tsx","../src/migrations/index.ts","../src/types.ts","../src/index.ts"],"names":["running","getSupabaseClientOptional","orphans","React","elapsedMs","React2","jsxs","jsx","Button","React3","Mic","retention","React4","createPortal","Fragment","AlertTriangle","React5","timer","toast","Loader2","Badge","CONCURRENCY","client"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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;;;AC+NlC,SAAS,kBAAkB,MAAA,EAA4C;AAC5E,EAAA,OAAO,CAAC,CAAC,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAa,OAA2B,IAAA,KAAS,WAAA;AACxF;;;ACrNA,IAAM,cAAA,GAA+B;AAAA,EACnC,eAAA,EAAiB,aAAA;AAAA,EACjB,aAAA,EAAe,cAAA;AAAA,EACf,KAAA,EAAO,qBAAA;AAAA,EACP,KAAA,EAAO,QAAA;AAAA,EACP,MAAA,EAAQ,SAAA;AAAA,EACR,MAAA,EAAQ,WAAA;AAAA,EACR,OAAA,EAAS,WAAA;AAAA,EACT,QAAA,EAAU,iBAAA;AAAA,EACV,OAAA,EAAS,SAAA;AAAA,EACT,UAAA,EAAY,mBAAA;AAAA,EACZ,aAAA,EAAe;AACjB,CAAA;AAEA,IAAM,iBAAA,GAA2C;AAAA,EAC/C,SAAA,EAAW,EAAA;AAAA,EACX,cAAA,EAAgB,IAAA;AAAA,EAChB,kBAAA,EAAoB;AACtB,CAAA;AAEO,SAAS,mBAAmB,OAAA,EAA8C;AAkB/E,EAAA,eAAA,CAAgB,SAAS,GAAG,CAAA;AAE5B,EAAA,MAAM,SAAuB,EAAE,GAAG,cAAA,EAAgB,GAAG,QAAQ,MAAA,EAAO;AACpE,EAAA,MAAMI,aAAmC,EAAE,GAAG,iBAAA,EAAmB,GAAG,QAAQ,SAAA,EAAU;AACtF,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,eAAA,IAAmB,EAAC;AAC5C,EAAA,MAAM,gBAAA,GAAmB,QAAQ,gBAAA,IAAoB,iBAAA;AACrD,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,GAAA,CAAI,MAAA,IAAU,OAAA;AAErC,EAAA,oBAAA,CAAqB,aAAa,CAAA;AAKlC,EAAA,iBAAA,CAAkB,EAAE,SAAA,EAAAA,UAAAA,EAAW,CAAA;AAC/B,EAAA,eAAA,EAAgB;AAEhB,EAAA,MAAM,WAAA,GAAc,CAAC,SAAA,KAAsB;AACzC,IAAA,MAAA,CAAO,QAAA,CAAS,IAAA,GAAO,CAAA,QAAA,EAAW,SAAS,CAAA,CAAA;AAAA,EAC7C,CAAA;AAEA,EAAA,MAAM,UAAA,GAAuB,MAC3BR,eAAAA,CAAM,aAAA,CAAc,gBAAA,EAAkB,EAAE,MAAA,EAAQ,SAAA,EAAAQ,UAAAA,EAAW,MAAA,EAAQ,aAAA,EAAe,WAAA,EAAa,CAAA;AACjG,EAAA,UAAA,CAAW,WAAA,GAAc,kBAAA;AAEzB,EAAA,MAAM,eAAyB,MAAM;AACnC,IAAA,MAAM,OAAO,OAAO,MAAA,KAAW,WAAA,GAAc,MAAA,CAAO,SAAS,IAAA,GAAO,EAAA;AACpE,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,KAAA,CAAM,UAAU,CAAA,CAAE,CAAC,CAAA,EAAG,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,CAAA,IAAK,EAAA;AAC9D,IAAA,IAAI,CAAC,WAAW,OAAO,IAAA;AACvB,IAAA,OAAOR,eAAAA,CAAM,cAAc,iBAAA,EAAmB;AAAA,MAC5C,SAAA;AAAA,MACA,MAAA;AAAA,MACA,OAAA;AAAA,MACA,KAAK,OAAA,CAAQ,GAAA;AAAA,MACb,gBAAA;AAAA,MACA,QAAQ,MAAM;AACZ,QAAA,MAAA,CAAO,SAAS,IAAA,GAAO,UAAA;AAAA,MACzB,CAAA;AAAA,MACA,gBAAgB,MAAM;AACpB,QAAA,MAAA,CAAO,SAAS,IAAA,GAAO,UAAA;AAAA,MACzB;AAAA,KACD,CAAA;AAAA,EACH,CAAA;AACA,EAAA,YAAA,CAAa,WAAA,GAAc,oBAAA;AAE3B,EAAA,OAAO;AAAA,IACL,EAAA,EAAI,QAAA;AAAA,IACJ,MAAM,MAAA,CAAO,aAAA;AAAA,IACb,IAAA,EAAM,KAAA;AAAA,IACN,OAAA,EAAS,OAAA;AAAA,IACT,KAAA,EAAQ,QAAQ,KAAA,IAAS,WAAA;AAAA,IACzB,YAAY,OAAA,CAAQ,UAAA;AAAA,IACpB,cAAA,EAAgB,KAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMhB,YAAA,EAAc,CAAC,cAAc,CAAA;AAAA,IAE7B,YAAY,EAAC;AAAA,IAEb,MAAA,EAAQ;AAAA,MACN;AAAA,QACE,IAAA,EAAM,WAAA;AAAA,QACN,SAAA,EAAW;AAAA;AACb,KACF;AAAA,IAEA,OAAA,EAAS;AAAA,MACP;AAAA,QACE,EAAA,EAAI,oBAAA;AAAA;AAAA;AAAA,QAGJ,IAAA,EAAM,kBAAA;AAAA,QACN,SAAA,EAAW,UAAA;AAAA,QACX,KAAA,EAAO;AAAA;AACT,KACF;AAAA,IAEA,gBAAA,EAAkB;AAAA,MAChB,EAAE,EAAA,EAAI,QAAA,EAAU,KAAA,EAAO,MAAA,CAAO,aAAA,EAAe,OAAA,EAAS,CAAC,MAAA,EAAQ,QAAA,EAAU,MAAA,EAAQ,QAAQ,CAAA;AAAE,KAC7F;AAAA,IAEA,cAAA,EAAgB;AAAA;AAAA;AAAA,MAGd;AAAA,QACE,GAAA,EAAK,OAAA,CAAQ,MAAA,EAAQ,YAAA,IAAgB,sBAAA;AAAA,QACrC,KAAA,EAAO,sCAAA;AAAA,QACP,KAAA,EAAO,qBAAA;AAAA,QACP,MAAA,EAAQ;AAAA,OACV;AAAA,MACA;AAAA,QACE,GAAA,EAAK,OAAA,CAAQ,MAAA,EAAQ,cAAA,IAAkB,wBAAA;AAAA,QACvC,KAAA,EAAO,+BAAA;AAAA,QACP,KAAA,EAAO,wBAAA;AAAA,QACP,MAAA,EAAQ;AAAA;AACV,KACF;AAAA,IAEA,OAAA,EAAS;AAAA,MACP;AAAA,QACE,EAAA,EAAI,sBAAA;AAAA,QACJ,IAAA,EAAM,oBAAA;AAAA,QACN,WAAA,EAAa,CAAA,MAAA,EAAS,MAAA,CAAO,aAAA,CAAc,aAAa,CAAA,4CAAA,CAAA;AAAA,QACxD,IAAA,EAAM,KAAA;AAAA,QACN,IAAA,EAAM,MAAA;AAAA,QACN,UAAU,MAAA,CAAO,aAAA;AAAA,QACjB,UAAA,EAAY,EAAE,OAAA,EAAS,QAAA,EAAU,QAAQ,MAAA,EAAgB;AAAA,QACzD,UAAA,EAAY;AAAA,UACV,IAAA,EAAM,QAAA;AAAA,UACN,UAAA,EAAY;AAAA,YACV,SAAA,EAAW,EAAE,IAAA,EAAM,QAAA,EAAmB,aAAa,2BAAA,EAA4B;AAAA,YAC/E,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAmB,aAAa,iCAAA;AAA+B;AACjF,SACF;AAAA,QACA,WAAA,EAAa;AAAA,UACX,EAAE,KAAA,EAAO,CAAA,MAAA,EAAS,OAAO,aAAA,CAAc,WAAA,EAAa,CAAA,gCAAA,CAAA;AAAgC;AACtF;AACF,KACF;AAAA,IAEA,UAAA,EAAY;AAAA,MACV;AAAA,QACE,EAAA,EAAI,iBAAA;AAAA,QACJ,OAAA,EAAS,OAAA;AAAA,QACT,GAAA,EAAK,yBAAA;AAAA,QACL,WAAA,EACE;AAAA;AACJ,KACF;AAAA,IAEA,OAAA,EAAS;AAAA,GACX;AACF","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 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}\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\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 */\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\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 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: {\n channelCount: 1,\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n sampleRate: 16_000,\n },\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 }\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 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({\n audio: { channelCount: 1, echoCancellation: true, noiseSuppression: true, autoGainControl: true },\n })\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 = 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 }\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 if (pieces.length > 0) await closeSegment(live, true)\n live.session.state = 'interrupted'\n await putSession(live.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 = 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, 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 // 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. */\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 orphans = await findOrphanSessions()\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, 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\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 }: 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 // Ler o elapsed fora do selector: 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 ? useScribeStore.getState().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\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, resumeSession } from '../runtime'\nimport type { ScribeLabels } from '../types'\n\n// ---------------------------------------------------------------------------\n// O banner de recuperação.\n//\n// Ele NÃO é o mecanismo de recuperação. Quando ele aparece, o pump já está\n// subindo os bytes há segundos — recuperar áudio nunca espera decisão humana,\n// porque a decisão pode nunca vir (o usuário fechou o notebook e foi almoçar).\n//\n// O que o banner decide é o que fazer com a SESSÃO: continuar, encerrar e gerar\n// o documento, ou largar. Por isso ele é não-bloqueante: um usuário que o\n// ignore por uma hora ainda encontra todo o áudio no servidor quando voltar.\n//\n// \"Retomar\" tem prazo. Depois de ~30 minutos, retomar deixa de fazer sentido —\n// o encontro acabou, e continuar a mesma gravação produziria um registro que\n// junta dois momentos distintos como se fossem um.\n// ---------------------------------------------------------------------------\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 [busy, setBusy] = React.useState<string | null>(null)\n\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 const durationLabel = formatElapsed(session.accumulatedMs)\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","import * as React from 'react'\nimport { createPortal } from 'react-dom'\nimport { useAuth } from '@fayz-ai/admin'\nimport { toast } from '@fayz-ai/ui/primitives'\nimport { ScribeRecordingPill } from './ScribeRecordingPill'\nimport { ScribeRecoveryBanner } from './ScribeRecoveryBanner'\nimport { ScribeConsentDialog } from './ScribeConsentDialog'\nimport { beginSession } from '../runtime'\nimport { wireScribeStore } from '../store'\nimport type { ConsentMode, ScribeLabels, ScribeRetentionConfig } from '../types'\n\n// ---------------------------------------------------------------------------\n// Um único widget de topbar que carrega tudo que precisa ser global.\n//\n// Por que tudo junto num só: `shell.topbar.end` é a única zona verificadamente\n// renderizada e sempre montada. Espalhar pill, banner e diálogo por zonas\n// diferentes significaria depender de zonas que não existem (`shell.floating`\n// está declarado nos tipos do core mas nada o renderiza).\n//\n// O evento `scribe:start` é o mesmo truque de desacoplamento que a agenda já\n// usa com `agenda:open-booking` (src/config/app.tsx:544 no beauty-saas):\n// qualquer tela dispara, este mount escuta. A agenda ganha um botão \"Iniciar\n// atendimento\" sem ganhar uma dependência 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\nexport interface ScribeShellMountProps {\n labels: ScribeLabels\n retention: ScribeRetentionConfig\n locale: string\n onOpenSession?: (sessionId: string) => void\n}\n\nexport function ScribeShellMount({ labels, retention, locale, onOpenSession }: ScribeShellMountProps) {\n const { user } = useAuth()\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 // Com consentimento exigido, o diálogo entra ANTES do primeiro byte. Sem\n // ele, começa direto — mas a decisão é da vertical, não da tela.\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 // A razão é sempre acionável (permissão negada, sem espaço, sem\n // microfone) — um \"erro ao gravar\" genérico deixaria o usuário sem\n // saber que basta liberar o microfone.\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 <ScribeRecordingPill labels={labels} onOpenSession={onOpenSession} />\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 {/* O banner pertence ao topo da PÁGINA, não ao topbar — mas o único mount\n sempre-vivo é este widget. Portal resolve sem inventar uma zona nova. */}\n {typeof document !== 'undefined' &&\n createPortal(\n <div className=\"pointer-events-auto fixed inset-x-0 top-14 z-40\">\n <ScribeRecoveryBanner labels={labels} onOpenSession={onOpenSession} />\n </div>,\n document.body,\n )}\n </>\n )\n}\n\n/** Helper para qualquer tela disparar o início sem importar o 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// ---------------------------------------------------------------------------\n// A transcrição, com marcação de tempo.\n//\n// O único requisito de desenho que não é estético: BURACO APARECE. Um segmento\n// `gap` vira uma linha visível dizendo que a gravação foi interrompida, com a\n// duração do buraco. Renderizar a transcrição sem ele produziria um texto que\n// parece contínuo e não é — e num registro clínico ou jurídico isso é pior que\n// um texto com furo declarado, porque o furo declarado o leitor consegue\n// avaliar e o furo escondido não.\n// ---------------------------------------------------------------------------\n\nexport interface TranscriptPanelProps {\n segments: ScribeSegment[]\n /** True enquanto ainda há segmento subindo ou transcrevendo. */\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 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","// ---------------------------------------------------------------------------\n// Leitura da sessão a partir do servidor. Este é o lado READ do manifesto — o\n// lado WRITE do áudio é o pump, que fala com Storage e com o RPC.\n//\n// A separação importa: a tela lê o que o servidor sabe, não o que o IndexedDB\n// local acha. É isso que faz a sessão ser recuperável de OUTRO dispositivo —\n// notebook morreu, abre no celular e a transcrição está lá.\n// ---------------------------------------------------------------------------\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 */\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}): Promise<string> {\n const supabase = client()\n const { generation, session, markdown, title, sections, edited } = args\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: generation.templateId ?? null,\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 // Caminho zerado também nas linhas de segmento: deixar um path apontando para\n // objeto inexistente faria o retry do STT baixar o vazio para sempre.\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 * as React from 'react'\nimport { Check, Loader2, RefreshCw, Trash2 } from 'lucide-react'\nimport { Badge, Button, MarkdownEditor, toast } from '@fayz-ai/ui/primitives'\nimport { parseSections, deriveTitle } 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 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 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 title: generation.title ?? deriveTitle(value, templateName),\n sections: parseSections(value, schema),\n edited,\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, 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 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","import { getSupabaseClientOptional } from '@fayz-ai/core'\nimport { T, AUDIO_BUCKET } from '../data/tables'\nimport type { ScribeSttConfig } from '../types'\nimport { resolveScribeTransport, type ScribeTransport } from './transport'\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 await recomputeCounters(supabase, sessionId)\n await maybeMarkReady(supabase, sessionId)\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 text: 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 { 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 deriveTitle,\n renderTranscript,\n type TranscriptLine,\n} from './prompt'\nimport { resolveScribeTransport } from './transport'\n\n// ---------------------------------------------------------------------------\n// Geração pelo broker da plataforma\n// ---------------------------------------------------------------------------\n//\n// Mesma desmontagem do dreno de STT: a edge function orquestrava E segurava a\n// chave; só a chave precisava sair do alcance do cliente. O prompt é montado\n// pelo mesmo `lib/prompt.ts` dos dois lados — é por isso que ele nunca importou\n// nada de Deno nem de browser.\n//\n// O que se mantém intacto, porque é o que faz a geração ser um documento e não\n// uma conversa:\n//\n// - a linha nasce em `running` ANTES da chamada ao modelo. Uma geração que\n// trava sem deixar rastro é indistinguível de uma que nunca foi pedida.\n// - `markdown` é escrito uma vez e nunca mais; a edição do humano vai para\n// `markdown_edited`. A diferença entre os dois é a prova de que alguém\n// revisou — e é a métrica de produto que diz se o modelo está bom.\n// - `prompt_version`, `model` e tokens ficam gravados, então a geração é\n// auditável e reproduzível.\n//\n// Nada disso caberia num turno de chat, e é por isso que a geração continua\n// fora do assistente mesmo agora que o modelo vem do mesmo broker que ele usa.\n\n/** Reproduzir a mesma nota duas vezes vale mais aqui do que soar criativo. */\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 não inicializado')\n return supabase\n}\n\nexport interface GenerateResult {\n generationId: string\n markdown: string\n model: string\n}\n\n/**\n * Gera um documento a partir da transcrição da sessão.\n *\n * Devolve `null` quando o transporte configurado não é o do browser — aí quem\n * chama deve usar a edge function.\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 title: deriveTitle(markdown, 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 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}\n\nexport function ScribeSessionPage({\n sessionId,\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 <div className=\"flex justify-center py-16\">\n <Loader2 className=\"h-6 w-6 animate-spin text-muted-foreground\" />\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 />\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 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","// 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","// ---------------------------------------------------------------------------\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 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 { MIGRATION_001_SCRIBE_BASE } from './migrations'\nimport { assertSttConfig } from './lib/config'\nimport type {\n ScribeLabels,\n ScribePluginOptions,\n ScribeRetentionConfig,\n} from './types'\n\nconst DEFAULT_LABELS: ScribeLabels = {\n sessionSingular: 'Atendimento',\n sessionPlural: 'Atendimentos',\n start: 'Iniciar 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 // 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, 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 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: 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 ],\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 { 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"]}
|