@fayz-ai/plugin-scribe 0.13.20 → 0.14.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.
@@ -0,0 +1,114 @@
1
+ // src/lib/prompt.ts
2
+ var PROMPT_VERSION = "scribe/narrative@1";
3
+ function formatOffset(ms) {
4
+ const total = Math.floor(ms / 1e3);
5
+ const mm = String(Math.floor(total / 60)).padStart(2, "0");
6
+ const ss = String(total % 60).padStart(2, "0");
7
+ return `${mm}:${ss}`;
8
+ }
9
+ function renderTranscript(lines) {
10
+ return lines.filter((l) => l.gap || l.text?.trim()).map((l) => {
11
+ const at = formatOffset(l.startOffsetMs);
12
+ if (l.gap) return `[${at}] (((TRECHO AUSENTE \u2014 a grava\xE7\xE3o foi interrompida aqui)))`;
13
+ const who = l.speaker !== void 0 ? `Falante ${l.speaker + 1}: ` : "";
14
+ return `[${at}] ${who}${l.text.trim()}`;
15
+ }).join("\n");
16
+ }
17
+ function shapeInstruction(section) {
18
+ switch (section.shape) {
19
+ case "bullets":
20
+ return "Formato: lista com h\xEDfen. Itens curtos, um fato por item.";
21
+ case "keyvalue":
22
+ return "Formato: lista com h\xEDfen no padr\xE3o `- **R\xF3tulo:** valor`. Um par por linha.";
23
+ default:
24
+ return "Formato: prosa corrida, sem listas.";
25
+ }
26
+ }
27
+ function buildSystemPrompt(schema) {
28
+ const parts = [];
29
+ parts.push(
30
+ "Voc\xEA redige documentos a partir da transcri\xE7\xE3o de um atendimento gravado.",
31
+ "Escreva SOMENTE o que a transcri\xE7\xE3o sustenta. Voc\xEA n\xE3o \xE9 um assistente conversacional: sua sa\xEDda \xE9 o documento, nada mais."
32
+ );
33
+ if (schema.style) parts.push(`Estilo: ${schema.style}`);
34
+ if (schema.outputLocale) parts.push(`Escreva o documento em ${schema.outputLocale}, mesmo que a transcri\xE7\xE3o esteja em outro idioma.`);
35
+ parts.push(
36
+ "",
37
+ "REGRAS INEGOCI\xC1VEIS:",
38
+ "- Nunca invente fato, n\xFAmero, data, nome ou medida que n\xE3o esteja na transcri\xE7\xE3o.",
39
+ '- Quando a transcri\xE7\xE3o n\xE3o cobre algo que a se\xE7\xE3o pede, escreva "n\xE3o informado". N\xE3o deduza a partir do contexto.',
40
+ "- Onde a transcri\xE7\xE3o indicar `(((TRECHO AUSENTE)))`, N\xC3O costure os dois lados como se fossem cont\xEDnuos. Se aquilo afeta o que a se\xE7\xE3o diz, registre a lacuna.",
41
+ "- N\xE3o dirija a palavra ao leitor, n\xE3o comente o pr\xF3prio trabalho, n\xE3o pe\xE7a confirma\xE7\xE3o."
42
+ );
43
+ if (schema.neverInfer?.length) {
44
+ parts.push(
45
+ `- Estes itens NUNCA podem ser inferidos, apenas transcritos quando ditos explicitamente: ${schema.neverInfer.join(", ")}.`
46
+ );
47
+ }
48
+ parts.push(
49
+ "",
50
+ "FORMATO DA SA\xCDDA (markdown restrito):",
51
+ "- Cada se\xE7\xE3o come\xE7a com `## ` seguido exatamente do t\xEDtulo fornecido.",
52
+ "- Use no m\xE1ximo `###` para subt\xEDtulo. Nunca `####` ou mais fundo.",
53
+ "- PROIBIDO: tabela, link, imagem, bloco de c\xF3digo cercado por crases, HTML.",
54
+ "- Permitido: par\xE1grafo, lista com h\xEDfen, lista numerada, **negrito**, *it\xE1lico*.",
55
+ "- N\xE3o escreva pre\xE2mbulo nem fecho. A primeira linha da resposta \xE9 o primeiro `## `."
56
+ );
57
+ return parts.join("\n");
58
+ }
59
+ function buildUserPrompt(args) {
60
+ const { schema, transcript, context } = args;
61
+ const parts = [];
62
+ const ctx = Object.entries(context ?? {}).filter(([, v]) => v);
63
+ if (ctx.length > 0) {
64
+ parts.push("DADOS DO ATENDIMENTO (refer\xEAncia; n\xE3o repita como se\xE7\xE3o):");
65
+ for (const [k, v] of ctx) parts.push(`- ${k}: ${v}`);
66
+ parts.push("");
67
+ }
68
+ parts.push("TRANSCRI\xC7\xC3O:", '"""', transcript, '"""', "");
69
+ parts.push("SE\xC7\xD5ES A ESCREVER, nesta ordem:");
70
+ schema.sections.forEach((section, i) => {
71
+ parts.push("");
72
+ parts.push(`${i + 1}. ## ${section.heading}`);
73
+ parts.push(` O que entra: ${section.guidance}`);
74
+ parts.push(` ${shapeInstruction(section)}`);
75
+ if (section.maxWords) parts.push(` Limite: cerca de ${section.maxWords} palavras.`);
76
+ if (section.required) parts.push(' Obrigat\xF3ria: escreva-a mesmo que s\xF3 para registrar "n\xE3o informado".');
77
+ else if (section.omitWhenEmpty) parts.push(" Se a transcri\xE7\xE3o n\xE3o disser nada sobre isto, OMITA a se\xE7\xE3o inteira, t\xEDtulo incluso.");
78
+ });
79
+ return parts.join("\n");
80
+ }
81
+ function parseSections(markdown, schema) {
82
+ const out = {};
83
+ if (!markdown) return out;
84
+ const norm = (s) => s.trim().toLowerCase().replace(/\s+/g, " ").replace(/[:.]+$/, "");
85
+ const byHeading = new Map(schema.sections.map((s) => [norm(s.heading), s.id]));
86
+ const lines = markdown.split("\n");
87
+ let currentId = null;
88
+ let buffer = [];
89
+ const flush = () => {
90
+ if (currentId) out[currentId] = buffer.join("\n").trim();
91
+ buffer = [];
92
+ };
93
+ for (const line of lines) {
94
+ const match = /^##\s+(.+?)\s*$/.exec(line);
95
+ if (match && !line.startsWith("###")) {
96
+ flush();
97
+ currentId = byHeading.get(norm(match[1])) ?? null;
98
+ continue;
99
+ }
100
+ if (currentId) buffer.push(line);
101
+ }
102
+ flush();
103
+ return out;
104
+ }
105
+ function deriveTitle(markdown, fallback, maxLen = 80) {
106
+ const firstBody = markdown.split("\n").find((l) => l.trim() && !l.startsWith("#") && !l.startsWith("-") && !l.startsWith(">"));
107
+ if (!firstBody) return fallback;
108
+ const clean = firstBody.replace(/[*_`]/g, "").trim();
109
+ return clean.length > maxLen ? `${clean.slice(0, maxLen - 1)}\u2026` : clean;
110
+ }
111
+
112
+ export { PROMPT_VERSION, buildSystemPrompt, buildUserPrompt, deriveTitle, formatOffset, parseSections, renderTranscript };
113
+ //# sourceMappingURL=chunk-V2YWQDBI.js.map
114
+ //# sourceMappingURL=chunk-V2YWQDBI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/prompt.ts"],"names":[],"mappings":";AAaO,IAAM,cAAA,GAAiB;AAUvB,SAAS,aAAa,EAAA,EAAoB;AAC/C,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,EAAA,GAAK,GAAI,CAAA;AAClC,EAAA,MAAM,EAAA,GAAK,MAAA,CAAO,IAAA,CAAK,KAAA,CAAM,KAAA,GAAQ,EAAE,CAAC,CAAA,CAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAA;AACzD,EAAA,MAAM,KAAK,MAAA,CAAO,KAAA,GAAQ,EAAE,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA;AAC7C,EAAA,OAAO,CAAA,EAAG,EAAE,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA;AACpB;AAOO,SAAS,iBAAiB,KAAA,EAAiC;AAChE,EAAA,OAAO,KAAA,CACJ,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,GAAA,IAAO,CAAA,CAAE,IAAA,EAAM,IAAA,EAAM,CAAA,CACrC,GAAA,CAAI,CAAC,CAAA,KAAM;AACV,IAAA,MAAM,EAAA,GAAK,YAAA,CAAa,CAAA,CAAE,aAAa,CAAA;AACvC,IAAA,IAAI,CAAA,CAAE,GAAA,EAAK,OAAO,CAAA,CAAA,EAAI,EAAE,CAAA,oEAAA,CAAA;AACxB,IAAA,MAAM,GAAA,GAAM,EAAE,OAAA,KAAY,MAAA,GAAY,WAAW,CAAA,CAAE,OAAA,GAAU,CAAC,CAAA,EAAA,CAAA,GAAO,EAAA;AACrE,IAAA,OAAO,CAAA,CAAA,EAAI,EAAE,CAAA,EAAA,EAAK,GAAG,GAAG,CAAA,CAAE,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,EACvC,CAAC,CAAA,CACA,IAAA,CAAK,IAAI,CAAA;AACd;AAEA,SAAS,iBAAiB,OAAA,EAAsC;AAC9D,EAAA,QAAQ,QAAQ,KAAA;AAAO,IACrB,KAAK,SAAA;AACH,MAAA,OAAO,8DAAA;AAAA,IACT,KAAK,UAAA;AAGH,MAAA,OAAO,sFAAA;AAAA,IACT;AACE,MAAA,OAAO,qCAAA;AAAA;AAEb;AAEO,SAAS,kBAAkB,MAAA,EAAiC;AACjE,EAAA,MAAM,QAAkB,EAAC;AAEzB,EAAA,KAAA,CAAM,IAAA;AAAA,IACJ,oFAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,IAAI,OAAO,KAAA,EAAO,KAAA,CAAM,KAAK,CAAA,QAAA,EAAW,MAAA,CAAO,KAAK,CAAA,CAAE,CAAA;AACtD,EAAA,IAAI,OAAO,YAAA,EAAc,KAAA,CAAM,KAAK,CAAA,uBAAA,EAA0B,MAAA,CAAO,YAAY,CAAA,uDAAA,CAAmD,CAAA;AAEpI,EAAA,KAAA,CAAM,IAAA;AAAA,IACJ,EAAA;AAAA,IACA,yBAAA;AAAA,IACA,+FAAA;AAAA,IACA,wIAAA;AAAA,IACA,kLAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,IAAI,MAAA,CAAO,YAAY,MAAA,EAAQ;AAC7B,IAAA,KAAA,CAAM,IAAA;AAAA,MACJ,CAAA,yFAAA,EAA4F,MAAA,CAAO,UAAA,CAAW,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAAA,KAC1H;AAAA,EACF;AAEA,EAAA,KAAA,CAAM,IAAA;AAAA,IACJ,EAAA;AAAA,IACA,0CAAA;AAAA,IACA,mFAAA;AAAA,IACA,yEAAA;AAAA,IACA,gFAAA;AAAA,IACA,2FAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;AAEO,SAAS,gBAAgB,IAAA,EAIrB;AACT,EAAA,MAAM,EAAE,MAAA,EAAQ,UAAA,EAAY,OAAA,EAAQ,GAAI,IAAA;AACxC,EAAA,MAAM,QAAkB,EAAC;AAEzB,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,OAAA,CAAQ,OAAA,IAAW,EAAE,CAAA,CAAE,MAAA,CAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;AAC7D,EAAA,IAAI,GAAA,CAAI,SAAS,CAAA,EAAG;AAIlB,IAAA,KAAA,CAAM,KAAK,uEAA2D,CAAA;AACtE,IAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,CAAA,IAAK,GAAA,EAAK,KAAA,CAAM,IAAA,CAAK,CAAA,EAAA,EAAK,CAAC,CAAA,EAAA,EAAK,CAAC,CAAA,CAAE,CAAA;AACnD,IAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAAA,EACf;AAEA,EAAA,KAAA,CAAM,IAAA,CAAK,oBAAA,EAAgB,KAAA,EAAO,UAAA,EAAY,OAAO,EAAE,CAAA;AACvD,EAAA,KAAA,CAAM,KAAK,uCAAiC,CAAA;AAE5C,EAAA,MAAA,CAAO,QAAA,CAAS,OAAA,CAAQ,CAAC,OAAA,EAAS,CAAA,KAAM;AACtC,IAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,IAAA,KAAA,CAAM,KAAK,CAAA,EAAG,CAAA,GAAI,CAAC,CAAA,KAAA,EAAQ,OAAA,CAAQ,OAAO,CAAA,CAAE,CAAA;AAC5C,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,gBAAA,EAAmB,OAAA,CAAQ,QAAQ,CAAA,CAAE,CAAA;AAChD,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,GAAA,EAAM,gBAAA,CAAiB,OAAO,CAAC,CAAA,CAAE,CAAA;AAC5C,IAAA,IAAI,QAAQ,QAAA,EAAU,KAAA,CAAM,KAAK,CAAA,oBAAA,EAAuB,OAAA,CAAQ,QAAQ,CAAA,UAAA,CAAY,CAAA;AACpF,IAAA,IAAI,OAAA,CAAQ,QAAA,EAAU,KAAA,CAAM,IAAA,CAAK,iFAAwE,CAAA;AAAA,SAAA,IAChG,OAAA,CAAQ,aAAA,EAAe,KAAA,CAAM,IAAA,CAAK,0GAAwF,CAAA;AAAA,EACrI,CAAC,CAAA;AAED,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;AAUO,SAAS,aAAA,CAAc,UAAkB,MAAA,EAAiD;AAC/F,EAAA,MAAM,MAA8B,EAAC;AACrC,EAAA,IAAI,CAAC,UAAU,OAAO,GAAA;AAEtB,EAAA,MAAM,IAAA,GAAO,CAAC,CAAA,KAAc,CAAA,CAAE,MAAK,CAAE,WAAA,EAAY,CAAE,OAAA,CAAQ,MAAA,EAAQ,GAAG,CAAA,CAAE,OAAA,CAAQ,UAAU,EAAE,CAAA;AAC5F,EAAA,MAAM,YAAY,IAAI,GAAA,CAAI,MAAA,CAAO,QAAA,CAAS,IAAI,CAAC,CAAA,KAAM,CAAC,IAAA,CAAK,EAAE,OAAO,CAAA,EAAG,CAAA,CAAE,EAAE,CAAC,CAAC,CAAA;AAE7E,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,KAAA,CAAM,IAAI,CAAA;AACjC,EAAA,IAAI,SAAA,GAA2B,IAAA;AAC/B,EAAA,IAAI,SAAmB,EAAC;AAExB,EAAA,MAAM,QAAQ,MAAM;AAClB,IAAA,IAAI,SAAA,MAAe,SAAS,CAAA,GAAI,OAAO,IAAA,CAAK,IAAI,EAAE,IAAA,EAAK;AACvD,IAAA,MAAA,GAAS,EAAC;AAAA,EACZ,CAAA;AAEA,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,KAAA,GAAQ,iBAAA,CAAkB,IAAA,CAAK,IAAI,CAAA;AACzC,IAAA,IAAI,KAAA,IAAS,CAAC,IAAA,CAAK,UAAA,CAAW,KAAK,CAAA,EAAG;AACpC,MAAA,KAAA,EAAM;AACN,MAAA,SAAA,GAAY,UAAU,GAAA,CAAI,IAAA,CAAK,MAAM,CAAC,CAAC,CAAC,CAAA,IAAK,IAAA;AAC7C,MAAA;AAAA,IACF;AACA,IAAA,IAAI,SAAA,EAAW,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA;AAAA,EACjC;AACA,EAAA,KAAA,EAAM;AACN,EAAA,OAAO,GAAA;AACT;AAGO,SAAS,WAAA,CAAY,QAAA,EAAkB,QAAA,EAAkB,MAAA,GAAS,EAAA,EAAY;AACnF,EAAA,MAAM,SAAA,GAAY,QAAA,CACf,KAAA,CAAM,IAAI,CAAA,CACV,KAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,EAAK,IAAK,CAAC,EAAE,UAAA,CAAW,GAAG,CAAA,IAAK,CAAC,CAAA,CAAE,UAAA,CAAW,GAAG,CAAA,IAAK,CAAC,CAAA,CAAE,UAAA,CAAW,GAAG,CAAC,CAAA;AACzF,EAAA,IAAI,CAAC,WAAW,OAAO,QAAA;AACvB,EAAA,MAAM,QAAQ,SAAA,CAAU,OAAA,CAAQ,QAAA,EAAU,EAAE,EAAE,IAAA,EAAK;AACnD,EAAA,OAAO,KAAA,CAAM,MAAA,GAAS,MAAA,GAAS,CAAA,EAAG,KAAA,CAAM,MAAM,CAAA,EAAG,MAAA,GAAS,CAAC,CAAC,CAAA,MAAA,CAAA,GAAM,KAAA;AACpE","file":"chunk-V2YWQDBI.js","sourcesContent":["// ---------------------------------------------------------------------------\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"]}
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { PROMPT_VERSION, buildUserPrompt, renderTranscript, buildSystemPrompt, formatOffset, parseSections } from './chunk-V2YWQDBI.js';
2
+ export { PROMPT_VERSION, formatOffset, parseSections, renderTranscript } from './chunk-V2YWQDBI.js';
1
3
  import * as React5 from 'react';
2
4
  import React5__default from 'react';
3
5
  import { getActiveTenantId, getSupabaseClientOptional, registerTranslations } from '@fayz-ai/core';
@@ -9,8 +11,6 @@ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
9
11
  import { openAssistantAs, useAgentSurface, registerTimelineSource, useAccessOptional, useHeaderAction, useAuth, usePublishAssistantActivity, useRegisterAssistantLauncher, resolveFayzAgentConnection } from '@fayz-ai/admin';
10
12
  import { SubpageHeader } from '@fayz-ai/ui/layout';
11
13
 
12
- // src/index.ts
13
-
14
14
  // src/locales/en.ts
15
15
  var en = {
16
16
  "scribe.nav.sessions": "Sessions",
@@ -2021,111 +2021,6 @@ function ScribeShellMount({ labels, retention: retention2, stt, locale, onOpenSe
2021
2021
  /* @__PURE__ */ jsx(ScribeRecordingPill, { labels, onOpenSession, mobileOnly: true })
2022
2022
  ] });
2023
2023
  }
2024
-
2025
- // src/lib/prompt.ts
2026
- var PROMPT_VERSION = "scribe/narrative@1";
2027
- function formatOffset(ms) {
2028
- const total = Math.floor(ms / 1e3);
2029
- const mm = String(Math.floor(total / 60)).padStart(2, "0");
2030
- const ss = String(total % 60).padStart(2, "0");
2031
- return `${mm}:${ss}`;
2032
- }
2033
- function renderTranscript(lines) {
2034
- return lines.filter((l) => l.gap || l.text?.trim()).map((l) => {
2035
- const at = formatOffset(l.startOffsetMs);
2036
- if (l.gap) return `[${at}] (((TRECHO AUSENTE \u2014 a grava\xE7\xE3o foi interrompida aqui)))`;
2037
- const who = l.speaker !== void 0 ? `Falante ${l.speaker + 1}: ` : "";
2038
- return `[${at}] ${who}${l.text.trim()}`;
2039
- }).join("\n");
2040
- }
2041
- function shapeInstruction(section) {
2042
- switch (section.shape) {
2043
- case "bullets":
2044
- return "Formato: lista com h\xEDfen. Itens curtos, um fato por item.";
2045
- case "keyvalue":
2046
- return "Formato: lista com h\xEDfen no padr\xE3o `- **R\xF3tulo:** valor`. Um par por linha.";
2047
- default:
2048
- return "Formato: prosa corrida, sem listas.";
2049
- }
2050
- }
2051
- function buildSystemPrompt(schema) {
2052
- const parts = [];
2053
- parts.push(
2054
- "Voc\xEA redige documentos a partir da transcri\xE7\xE3o de um atendimento gravado.",
2055
- "Escreva SOMENTE o que a transcri\xE7\xE3o sustenta. Voc\xEA n\xE3o \xE9 um assistente conversacional: sua sa\xEDda \xE9 o documento, nada mais."
2056
- );
2057
- if (schema.style) parts.push(`Estilo: ${schema.style}`);
2058
- if (schema.outputLocale) parts.push(`Escreva o documento em ${schema.outputLocale}, mesmo que a transcri\xE7\xE3o esteja em outro idioma.`);
2059
- parts.push(
2060
- "",
2061
- "REGRAS INEGOCI\xC1VEIS:",
2062
- "- Nunca invente fato, n\xFAmero, data, nome ou medida que n\xE3o esteja na transcri\xE7\xE3o.",
2063
- '- Quando a transcri\xE7\xE3o n\xE3o cobre algo que a se\xE7\xE3o pede, escreva "n\xE3o informado". N\xE3o deduza a partir do contexto.',
2064
- "- Onde a transcri\xE7\xE3o indicar `(((TRECHO AUSENTE)))`, N\xC3O costure os dois lados como se fossem cont\xEDnuos. Se aquilo afeta o que a se\xE7\xE3o diz, registre a lacuna.",
2065
- "- N\xE3o dirija a palavra ao leitor, n\xE3o comente o pr\xF3prio trabalho, n\xE3o pe\xE7a confirma\xE7\xE3o."
2066
- );
2067
- if (schema.neverInfer?.length) {
2068
- parts.push(
2069
- `- Estes itens NUNCA podem ser inferidos, apenas transcritos quando ditos explicitamente: ${schema.neverInfer.join(", ")}.`
2070
- );
2071
- }
2072
- parts.push(
2073
- "",
2074
- "FORMATO DA SA\xCDDA (markdown restrito):",
2075
- "- Cada se\xE7\xE3o come\xE7a com `## ` seguido exatamente do t\xEDtulo fornecido.",
2076
- "- Use no m\xE1ximo `###` para subt\xEDtulo. Nunca `####` ou mais fundo.",
2077
- "- PROIBIDO: tabela, link, imagem, bloco de c\xF3digo cercado por crases, HTML.",
2078
- "- Permitido: par\xE1grafo, lista com h\xEDfen, lista numerada, **negrito**, *it\xE1lico*.",
2079
- "- N\xE3o escreva pre\xE2mbulo nem fecho. A primeira linha da resposta \xE9 o primeiro `## `."
2080
- );
2081
- return parts.join("\n");
2082
- }
2083
- function buildUserPrompt(args) {
2084
- const { schema, transcript, context } = args;
2085
- const parts = [];
2086
- const ctx = Object.entries(context ?? {}).filter(([, v]) => v);
2087
- if (ctx.length > 0) {
2088
- parts.push("DADOS DO ATENDIMENTO (refer\xEAncia; n\xE3o repita como se\xE7\xE3o):");
2089
- for (const [k, v] of ctx) parts.push(`- ${k}: ${v}`);
2090
- parts.push("");
2091
- }
2092
- parts.push("TRANSCRI\xC7\xC3O:", '"""', transcript, '"""', "");
2093
- parts.push("SE\xC7\xD5ES A ESCREVER, nesta ordem:");
2094
- schema.sections.forEach((section, i) => {
2095
- parts.push("");
2096
- parts.push(`${i + 1}. ## ${section.heading}`);
2097
- parts.push(` O que entra: ${section.guidance}`);
2098
- parts.push(` ${shapeInstruction(section)}`);
2099
- if (section.maxWords) parts.push(` Limite: cerca de ${section.maxWords} palavras.`);
2100
- if (section.required) parts.push(' Obrigat\xF3ria: escreva-a mesmo que s\xF3 para registrar "n\xE3o informado".');
2101
- else if (section.omitWhenEmpty) parts.push(" Se a transcri\xE7\xE3o n\xE3o disser nada sobre isto, OMITA a se\xE7\xE3o inteira, t\xEDtulo incluso.");
2102
- });
2103
- return parts.join("\n");
2104
- }
2105
- function parseSections(markdown, schema) {
2106
- const out = {};
2107
- if (!markdown) return out;
2108
- const norm = (s) => s.trim().toLowerCase().replace(/\s+/g, " ").replace(/[:.]+$/, "");
2109
- const byHeading = new Map(schema.sections.map((s) => [norm(s.heading), s.id]));
2110
- const lines = markdown.split("\n");
2111
- let currentId = null;
2112
- let buffer = [];
2113
- const flush = () => {
2114
- if (currentId) out[currentId] = buffer.join("\n").trim();
2115
- buffer = [];
2116
- };
2117
- for (const line of lines) {
2118
- const match = /^##\s+(.+?)\s*$/.exec(line);
2119
- if (match && !line.startsWith("###")) {
2120
- flush();
2121
- currentId = byHeading.get(norm(match[1])) ?? null;
2122
- continue;
2123
- }
2124
- if (currentId) buffer.push(line);
2125
- }
2126
- flush();
2127
- return out;
2128
- }
2129
2024
  function TranscriptPanel({ segments, pending, emptyLabel }) {
2130
2025
  const visible = segments.filter((s) => s.gap || s.text?.trim());
2131
2026
  if (visible.length === 0) {
@@ -3690,6 +3585,6 @@ function createScribePlugin(options) {
3690
3585
  };
3691
3586
  }
3692
3587
 
3693
- export { PROMPT_VERSION, SCRIBE_AGENT_ROLE, SCRIBE_START_EVENT, ScribeDevicePicker, ScribeLivePanel, ScribeSessionPage, ScribeStartButton, beginSession, createScribePlugin, deleteSessionAudio, discardSession, endSession, fetchGenerations, fetchOpenSessions, fetchSegments, fetchSession, fetchSessionsForSubject, formatElapsed, formatOffset, isCaptureSupported, isNarrativeSchema, parseSections, pauseSession, renderTranscript, requestScribeStart, resumeSession, scribeSessionPath, startScribeSession, useScribeStore };
3588
+ export { SCRIBE_AGENT_ROLE, SCRIBE_START_EVENT, ScribeDevicePicker, ScribeLivePanel, ScribeSessionPage, ScribeStartButton, beginSession, createScribePlugin, deleteSessionAudio, discardSession, endSession, fetchGenerations, fetchOpenSessions, fetchSegments, fetchSession, fetchSessionsForSubject, formatElapsed, isCaptureSupported, isNarrativeSchema, pauseSession, requestScribeStart, resumeSession, scribeSessionPath, startScribeSession, useScribeStore };
3694
3589
  //# sourceMappingURL=index.js.map
3695
3590
  //# sourceMappingURL=index.js.map