evo360-types 1.3.552 → 1.3.555
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apps/evo-chat/ticket/zod-schemas.d.ts +66 -0
- package/dist/apps/evo-chat/ticket/zod-schemas.js +4 -0
- package/dist/apps/evo-chat/ticket/zod-schemas.ts +4 -0
- package/dist/apps/evo-core/zod-schemas.d.ts +30 -0
- package/dist/apps/evo-core/zod-schemas.js +2 -0
- package/dist/apps/evo-core/zod-schemas.ts +2 -0
- package/dist/apps/evo-csq/zod-schemas.d.ts +10414 -0
- package/dist/apps/evo-csq/zod-schemas.js +546 -0
- package/dist/apps/evo-csq/zod-schemas.ts +627 -0
- package/dist/apps/evo-finops/zod-schemas.d.ts +2 -2
- package/dist/apps/evo-hub-ia/v1/zod-schemas.d.ts +161 -0
- package/dist/apps/evo-hub-ia/v1/zod-schemas.js +43 -1
- package/dist/apps/evo-hub-ia/v1/zod-schemas.ts +46 -0
- package/dist/apps/hub-automation/zod-schemas.d.ts +18 -18
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.ts +2 -0
- package/dist/types/evo-chat/ticket/index.d.ts +4 -0
- package/dist/types/evo-chat/ticket/index.ts +5 -0
- package/dist/types/evo-core/index.d.ts +2 -0
- package/dist/types/evo-core/index.ts +2 -0
- package/dist/types/evo-csq/fb_collections.d.ts +5 -0
- package/dist/types/evo-csq/fb_collections.js +13 -0
- package/dist/types/evo-csq/fb_collections.ts +14 -0
- package/dist/types/evo-csq/index.d.ts +624 -0
- package/dist/types/evo-csq/index.js +180 -0
- package/dist/types/evo-csq/index.ts +888 -0
- package/dist/types/evo-hub-ia/v1/index.d.ts +37 -0
- package/dist/types/evo-hub-ia/v1/index.ts +44 -0
- package/package.json +1 -1
|
@@ -0,0 +1,888 @@
|
|
|
1
|
+
// Tipos do CSQ 2.0 (Conversation Sales Quality) — feat-154.
|
|
2
|
+
// Schemas zod correspondentes ficam em src/apps/evo-csq/zod-schemas.ts;
|
|
3
|
+
// estes tipos sao escritos a mao e mantidos em paralelo (convencao do projeto:
|
|
4
|
+
// `apps/` = zod, `types/` = interfaces TS).
|
|
5
|
+
//
|
|
6
|
+
// Contrato de nomes/codigos: hub-medica-docs/features/feat-154-csq-2-0/report-spec.md
|
|
7
|
+
// (§2 estrutura, §3 indicadores, §4 rubrica, §5 claims, §6 alertas) e o
|
|
8
|
+
// "Adendo pos-parecer" do plan.md (§A–§I), que prevalece sobre o corpo do plano.
|
|
9
|
+
|
|
10
|
+
import { IFireDoc } from "../shared";
|
|
11
|
+
|
|
12
|
+
export * from "./fb_collections";
|
|
13
|
+
|
|
14
|
+
// ───────── Versao da rubrica ─────────
|
|
15
|
+
|
|
16
|
+
/** Versao da rubrica da plataforma gravada em cada avaliacao. Pesos e criterios
|
|
17
|
+
* sao da plataforma (nao do tenant); mudar pesos exige nova versao. */
|
|
18
|
+
export const CSQ_RUBRIC_VERSION = "v1";
|
|
19
|
+
|
|
20
|
+
// ───────── Enums / Literals ─────────
|
|
21
|
+
|
|
22
|
+
/** Modo do CSQ por departamento (gravado no binding da HubIA).
|
|
23
|
+
* - `off` : nao avalia (default quando ausente).
|
|
24
|
+
* - `shadow` : avalia e grava o laudo, sem notificar supervisores.
|
|
25
|
+
* - `active` : avalia, grava e notifica quando ha alerta critico novo. */
|
|
26
|
+
export type CsqMode = "off" | "shadow" | "active";
|
|
27
|
+
|
|
28
|
+
/** Status derivado do score (faixas do CSQ 1.0): >=90 Excelente, 80-89 Bom,
|
|
29
|
+
* 65-79 Regular, 50-64 Ruim, <50 Critico. `pending_judge` = laudo F0 gravado
|
|
30
|
+
* sem julgamento do LLM (fase 0 ou falha do juiz). */
|
|
31
|
+
export type CsqStatus =
|
|
32
|
+
| "Excelente"
|
|
33
|
+
| "Bom"
|
|
34
|
+
| "Regular"
|
|
35
|
+
| "Ruim"
|
|
36
|
+
| "Crítico"
|
|
37
|
+
| "pending_judge";
|
|
38
|
+
|
|
39
|
+
/** Severidade do alerta. `critica` dispara notificacao no modo `active`. */
|
|
40
|
+
export type CsqSeverity = "critica" | "alta" | "media" | "baixa";
|
|
41
|
+
|
|
42
|
+
/** Origem do alerta: `deterministic` = so codigo; `llm` = so juiz;
|
|
43
|
+
* `hybrid` = claim do juiz verificado por codigo (report-spec §5). */
|
|
44
|
+
export type CsqAlertSource = "deterministic" | "llm" | "hybrid";
|
|
45
|
+
|
|
46
|
+
/** Escopo do alerta: `ticket` = neste atendimento; `journey` = na jornada. */
|
|
47
|
+
export type CsqAlertScope = "ticket" | "journey";
|
|
48
|
+
|
|
49
|
+
/** Ciclo de vida do alerta. Um alerta da jornada fica `open` no laudo corrente
|
|
50
|
+
* ate uma avaliacao posterior constatar a correcao; entao vira `resolved`. */
|
|
51
|
+
export type CsqAlertLifecycle = "open" | "resolved";
|
|
52
|
+
|
|
53
|
+
/** Estado da rechecagem agendada de agenda (adendo §B). Alertas de agenda
|
|
54
|
+
* nascem `pending` (sem auto-fail nem notificacao) e sao reavaliados em
|
|
55
|
+
* `closed_at + CSQ_DEFAULTS.recheck_delay_min`.
|
|
56
|
+
* - `confirmed_ok` : a agenda passou a bater — alerta resolvido.
|
|
57
|
+
* - `confirmed` : a divergencia persiste — alerta confirmado.
|
|
58
|
+
* - `inconclusive` : nao foi possivel reler a agenda. */
|
|
59
|
+
export type CsqVerificationState =
|
|
60
|
+
| "pending"
|
|
61
|
+
| "confirmed_ok"
|
|
62
|
+
| "confirmed"
|
|
63
|
+
| "inconclusive";
|
|
64
|
+
|
|
65
|
+
/** Status do ledger de execucao (`runs/{eventId}`).
|
|
66
|
+
* `quarantined` = payload invalido (zod), ACK sem retry. */
|
|
67
|
+
export type CsqRunStatus = "running" | "done" | "failed" | "quarantined";
|
|
68
|
+
|
|
69
|
+
/** Escala por criterio: 0 / 50 / 100 / N/A. `NA` sai do denominador. */
|
|
70
|
+
export type CsqCriterionScore = 0 | 50 | 100 | "NA";
|
|
71
|
+
|
|
72
|
+
/** Veredito humano sobre o laudo (adendo §D / Q9=A). */
|
|
73
|
+
export type CsqReviewVerdict = "agree" | "disagree";
|
|
74
|
+
|
|
75
|
+
/** Fase do pipeline: `f0` = so indicadores + alertas deterministicos;
|
|
76
|
+
* `f1` = com o juiz LLM. */
|
|
77
|
+
export type CsqPhase = "f0" | "f1";
|
|
78
|
+
|
|
79
|
+
/** Autor de uma mensagem da transcricao entregue ao juiz. */
|
|
80
|
+
export type CsqAuthorKind =
|
|
81
|
+
| "lead"
|
|
82
|
+
| "human"
|
|
83
|
+
| "automation"
|
|
84
|
+
| "hubia"
|
|
85
|
+
| "event"
|
|
86
|
+
| "note";
|
|
87
|
+
|
|
88
|
+
// ───────── Catalogo de alertas (report-spec §6) ─────────
|
|
89
|
+
|
|
90
|
+
/** Codigos de alerta v1. Os subtipos de `POLITICA_VIOLADA` e
|
|
91
|
+
* `FLUXO_VENDA_NAO_SEGUIDO` vivem em `ICsqAlert.subtype`, nao como codigos. */
|
|
92
|
+
export const CSQ_ALERT_CODES = [
|
|
93
|
+
"AGENDAMENTO_INFORMADO_NAO_REGISTRADO",
|
|
94
|
+
"AGENDAMENTO_DIVERGENTE",
|
|
95
|
+
"AGENDAMENTO_SOBREPOSTO",
|
|
96
|
+
"CONTRADICAO_PREMISSA_ANTERIOR",
|
|
97
|
+
"ORIENTACAO_CLINICA_INDEVIDA",
|
|
98
|
+
"POLITICA_VIOLADA",
|
|
99
|
+
"ULTIMA_MENSAGEM_SEM_RESPOSTA",
|
|
100
|
+
"PERGUNTA_NAO_RESPONDIDA",
|
|
101
|
+
"PROMESSA_NAO_CUMPRIDA",
|
|
102
|
+
"IDENTIDADE_ERRADA",
|
|
103
|
+
"DADO_JA_INFORMADO_RECOLETADO",
|
|
104
|
+
"DADO_OBRIGATORIO_NAO_COLETADO",
|
|
105
|
+
"FLUXO_VENDA_NAO_SEGUIDO",
|
|
106
|
+
"MOTIVO_ENCERRAMENTO_INCOMPATIVEL",
|
|
107
|
+
"SLA_RESPOSTA_ALTO",
|
|
108
|
+
"LEAD_QUENTE_SEM_FOLLOWUP",
|
|
109
|
+
"LEAD_ORIGEM_INCONSISTENTE",
|
|
110
|
+
"LEAD_QUALIFICACAO_INCONSISTENTE",
|
|
111
|
+
"AUTOMACAO_CONFLITANTE",
|
|
112
|
+
"DADO_SENSIVEL_SOLICITADO",
|
|
113
|
+
"URGENCIA_NAO_ENCAMINHADA",
|
|
114
|
+
"NF_NAO_ENVIADA",
|
|
115
|
+
"COBRANCA_NAO_ENVIADA",
|
|
116
|
+
] as const;
|
|
117
|
+
|
|
118
|
+
export type CsqAlertCode = (typeof CSQ_ALERT_CODES)[number];
|
|
119
|
+
|
|
120
|
+
/** Subtipos de `POLITICA_VIOLADA` (report-spec §6). */
|
|
121
|
+
export const CSQ_POLICY_VIOLATION_SUBTYPES = [
|
|
122
|
+
"valor",
|
|
123
|
+
"parcelamento",
|
|
124
|
+
"convenio",
|
|
125
|
+
"dia_horario",
|
|
126
|
+
"procedimento",
|
|
127
|
+
"modalidade",
|
|
128
|
+
] as const;
|
|
129
|
+
|
|
130
|
+
export type CsqPolicyViolationSubtype =
|
|
131
|
+
(typeof CSQ_POLICY_VIOLATION_SUBTYPES)[number];
|
|
132
|
+
|
|
133
|
+
/** Subtipos de `FLUXO_VENDA_NAO_SEGUIDO` (report-spec §6). */
|
|
134
|
+
export const CSQ_SALES_FLOW_SUBTYPES = [
|
|
135
|
+
"objecao_sem_argumento",
|
|
136
|
+
"sem_oferta_concreta",
|
|
137
|
+
"preco_antes_da_queixa",
|
|
138
|
+
] as const;
|
|
139
|
+
|
|
140
|
+
export type CsqSalesFlowSubtype = (typeof CSQ_SALES_FLOW_SUBTYPES)[number];
|
|
141
|
+
|
|
142
|
+
/** Uniao de todos os subtipos aceitos em `ICsqAlert.subtype`. */
|
|
143
|
+
export type CsqAlertSubtype = CsqPolicyViolationSubtype | CsqSalesFlowSubtype;
|
|
144
|
+
|
|
145
|
+
/** Alertas que forcam auto-fail (`status='Crítico'`, `score <= 49`,
|
|
146
|
+
* `escalate=true`). Para `POLITICA_VIOLADA` o auto-fail so vale nos subtipos
|
|
147
|
+
* de `CSQ_AUTO_FAIL_POLICY_SUBTYPES`; alertas de agenda so contam depois da
|
|
148
|
+
* rechecagem (`verification='confirmed'`, adendo §B). */
|
|
149
|
+
export const CSQ_AUTO_FAIL_ALERT_CODES = [
|
|
150
|
+
"AGENDAMENTO_INFORMADO_NAO_REGISTRADO",
|
|
151
|
+
"AGENDAMENTO_DIVERGENTE",
|
|
152
|
+
"CONTRADICAO_PREMISSA_ANTERIOR",
|
|
153
|
+
"ORIENTACAO_CLINICA_INDEVIDA",
|
|
154
|
+
"POLITICA_VIOLADA",
|
|
155
|
+
"ULTIMA_MENSAGEM_SEM_RESPOSTA",
|
|
156
|
+
"DADO_SENSIVEL_SOLICITADO",
|
|
157
|
+
"URGENCIA_NAO_ENCAMINHADA",
|
|
158
|
+
] as const;
|
|
159
|
+
|
|
160
|
+
export type CsqAutoFailAlertCode = (typeof CSQ_AUTO_FAIL_ALERT_CODES)[number];
|
|
161
|
+
|
|
162
|
+
/** Subtipos de `POLITICA_VIOLADA` que caracterizam auto-fail (report-spec §4,
|
|
163
|
+
* auto-fail 4: valor/parcelamento/convenio contra a politica do roteiro). */
|
|
164
|
+
export const CSQ_AUTO_FAIL_POLICY_SUBTYPES = [
|
|
165
|
+
"valor",
|
|
166
|
+
"parcelamento",
|
|
167
|
+
"convenio",
|
|
168
|
+
] as const;
|
|
169
|
+
|
|
170
|
+
// ───────── Rubrica v1 (report-spec §4) ─────────
|
|
171
|
+
|
|
172
|
+
/** Codigo estavel de pilar. */
|
|
173
|
+
export type CsqPillarCode =
|
|
174
|
+
| "coerencia_historico"
|
|
175
|
+
| "roteiro_conversao"
|
|
176
|
+
| "acuracidade"
|
|
177
|
+
| "responsividade"
|
|
178
|
+
| "cordialidade_linguagem";
|
|
179
|
+
|
|
180
|
+
/** Criterio da rubrica: codigo estavel + texto (o que o juiz verifica). */
|
|
181
|
+
export interface ICsqRubricCriterion {
|
|
182
|
+
code: string;
|
|
183
|
+
name: string;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Pilar da rubrica: codigo estavel, nome e peso (soma = 100). */
|
|
187
|
+
export interface ICsqRubricPillar {
|
|
188
|
+
code: CsqPillarCode;
|
|
189
|
+
name: string;
|
|
190
|
+
weight: number;
|
|
191
|
+
criteria: ICsqRubricCriterion[];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Rubrica da plataforma v1. Pesos: 20/25/25/15/15 (report-spec §4).
|
|
195
|
+
* Os `code` sao contrato: aparecem em `ICsqCriterionScore.criterion` e no
|
|
196
|
+
* schema da tool de saida do juiz. */
|
|
197
|
+
export const CSQ_RUBRIC_V1: ICsqRubricPillar[] = [
|
|
198
|
+
{
|
|
199
|
+
code: "coerencia_historico",
|
|
200
|
+
name: "Coerência com o histórico do lead",
|
|
201
|
+
weight: 20,
|
|
202
|
+
criteria: [
|
|
203
|
+
{ code: "nao_contradiz_premissa", name: "Não contradiz premissa declarada (Estado do lead anterior + conversa)" },
|
|
204
|
+
{ code: "nao_repergunta_dado_informado", name: "Não re-pergunta dado já informado" },
|
|
205
|
+
{ code: "reconhece_contexto", name: "Reconhece o contexto (já é paciente, restrições)" },
|
|
206
|
+
{ code: "nomes_corretos", name: "Trata lead e paciente pelos nomes certos" },
|
|
207
|
+
],
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
code: "roteiro_conversao",
|
|
211
|
+
name: "Aderência ao roteiro & fluxo comercial",
|
|
212
|
+
weight: 25,
|
|
213
|
+
criteria: [
|
|
214
|
+
{ code: "segue_fluxo_previsto", name: "Segue o fluxo previsto em binding.behavior" },
|
|
215
|
+
{ code: "objecao_tratada_com_argumento", name: "Objeção tratada com o argumento do roteiro" },
|
|
216
|
+
{ code: "dados_obrigatorios_coletados", name: "Dados obrigatórios (capture-data) coletados" },
|
|
217
|
+
{ code: "motivo_fechamento_coerente", name: "Motivo de fechamento coerente com o desfecho (mcp_description)" },
|
|
218
|
+
{ code: "abertura_encerramento_padrao", name: "Abertura e encerramento padrão" },
|
|
219
|
+
],
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
code: "acuracidade",
|
|
223
|
+
name: "Acuracidade",
|
|
224
|
+
weight: 25,
|
|
225
|
+
criteria: [
|
|
226
|
+
{ code: "fatos_e_politicas_corretos", name: "Fatos e políticas de channel_custom (valores, dias/horários, convênio, inclusos, parcelamento, endereço)" },
|
|
227
|
+
{ code: "datas_horas_coerentes", name: "Datas e horas coerentes" },
|
|
228
|
+
{ code: "agendamento_bate_com_agenda", name: "Agendamento informado bate com a agenda (crítico)" },
|
|
229
|
+
{ code: "nao_promete_o_que_nao_controla", name: "Não promete o que não controla" },
|
|
230
|
+
],
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
code: "responsividade",
|
|
234
|
+
name: "Responsividade",
|
|
235
|
+
weight: 15,
|
|
236
|
+
criteria: [
|
|
237
|
+
{ code: "nenhuma_pergunta_sem_resposta", name: "Nenhuma pergunta sem resposta" },
|
|
238
|
+
{ code: "tempos_dentro_do_limiar", name: "frt_min e max_gap_min dentro do limiar" },
|
|
239
|
+
{ code: "retorno_apos_vou_verificar", name: "'Vou verificar' seguido de retorno efetivo" },
|
|
240
|
+
{ code: "ultima_mensagem_respondida", name: "Última mensagem do lead respondida" },
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
code: "cordialidade_linguagem",
|
|
245
|
+
name: "Cordialidade & linguagem da marca",
|
|
246
|
+
weight: 15,
|
|
247
|
+
criteria: [
|
|
248
|
+
{ code: "persona_e_tom", name: "Persona e tom de channel.identity" },
|
|
249
|
+
{ code: "lexico_obrigatorio_proibido", name: "Léxico obrigatório/proibido" },
|
|
250
|
+
{ code: "clareza_objetividade", name: "Clareza e objetividade" },
|
|
251
|
+
{ code: "ortografia", name: "Ortografia" },
|
|
252
|
+
{ code: "empatia_momento_sensivel", name: "Empatia em momento sensível" },
|
|
253
|
+
],
|
|
254
|
+
},
|
|
255
|
+
];
|
|
256
|
+
|
|
257
|
+
// ───────── Limiares e orcamentos da plataforma ─────────
|
|
258
|
+
|
|
259
|
+
/** Limiares/orcamentos default do CSQ (plataforma, nao por tenant na F1). */
|
|
260
|
+
export const CSQ_DEFAULTS = {
|
|
261
|
+
/** FRT liquido acima disso conta como atraso na rubrica (minutos). */
|
|
262
|
+
frt_min_warn: 15,
|
|
263
|
+
/** Maior espera acima disso gera SLA_RESPOSTA_ALTO severidade `media`. */
|
|
264
|
+
max_gap_min_medium: 60,
|
|
265
|
+
/** Maior espera acima disso gera SLA_RESPOSTA_ALTO severidade `alta`. */
|
|
266
|
+
max_gap_min_high: 240,
|
|
267
|
+
/** Tolerancia de horario ao comparar agendamento informado x agenda (min). */
|
|
268
|
+
appointment_time_tolerance_min: 15,
|
|
269
|
+
/** Janela de dias ao redor da data informada para procurar o agendamento. */
|
|
270
|
+
appointment_day_window: 1,
|
|
271
|
+
/** Janela da jornada considerada (dias). */
|
|
272
|
+
journey_days: 90,
|
|
273
|
+
/** Teto de tickets da jornada carregados por avaliacao. */
|
|
274
|
+
journey_max_tickets: 20,
|
|
275
|
+
/** Teto de mensagens do artefato de transcricao. */
|
|
276
|
+
transcript_max_messages: 500,
|
|
277
|
+
/** Teto de bytes do artefato de transcricao. */
|
|
278
|
+
transcript_max_bytes: 200000,
|
|
279
|
+
/** Atraso da rechecagem agendada da agenda (minutos apos closed_at). */
|
|
280
|
+
recheck_delay_min: 30,
|
|
281
|
+
} as const;
|
|
282
|
+
|
|
283
|
+
// ───────── Evidencia ─────────
|
|
284
|
+
|
|
285
|
+
/** Evidencia citada pelo juiz ou pelo codigo. O `message_id` DEVE existir na
|
|
286
|
+
* transcricao entregue ao juiz; o codigo descarta evidencia invalida. */
|
|
287
|
+
export interface ICsqEvidence {
|
|
288
|
+
message_id: string;
|
|
289
|
+
ticket_id: string;
|
|
290
|
+
ts: Date;
|
|
291
|
+
excerpt: string;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// ───────── Alertas ─────────
|
|
295
|
+
|
|
296
|
+
/** Alerta do laudo (report-spec §6). `fingerprint` =
|
|
297
|
+
* sha1(code|subtype|target|evidence.message_id) e a chave de dedupe e de ciclo
|
|
298
|
+
* de vida entre avaliacoes (adendo §G). */
|
|
299
|
+
export interface ICsqAlert {
|
|
300
|
+
code: CsqAlertCode;
|
|
301
|
+
subtype?: CsqAlertSubtype;
|
|
302
|
+
severity: CsqSeverity;
|
|
303
|
+
source: CsqAlertSource;
|
|
304
|
+
scope: CsqAlertScope;
|
|
305
|
+
status: CsqAlertLifecycle;
|
|
306
|
+
fingerprint: string;
|
|
307
|
+
title: string;
|
|
308
|
+
evidence: ICsqEvidence[];
|
|
309
|
+
/** Texto de acao sugerida. Na F1 e apenas texto: nao cria task nem acao. */
|
|
310
|
+
suggested_action?: string;
|
|
311
|
+
/** So em alertas de agenda: estado da rechecagem agendada (adendo §B). */
|
|
312
|
+
verification?: CsqVerificationState;
|
|
313
|
+
/** Instante em que a rechecagem deve rodar (closed_at + recheck_delay_min). */
|
|
314
|
+
recheck_after?: Date;
|
|
315
|
+
/** Ticket em que o alerta foi constatado como resolvido. */
|
|
316
|
+
resolved_in_ticket?: string;
|
|
317
|
+
/** Ticket em que o alerta apareceu pela primeira vez (herdado x novo). */
|
|
318
|
+
first_seen_ticket_id: string;
|
|
319
|
+
created_at: Date;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// ───────── Indicadores (report-spec §3) ─────────
|
|
323
|
+
|
|
324
|
+
/** Indicadores quantitativos calculados por codigo, por lente
|
|
325
|
+
* ("neste atendimento" e "na jornada"). `null` = nao aplicavel a lente ou
|
|
326
|
+
* indisponivel (ex.: metricas liquidas sem horario comercial configurado). */
|
|
327
|
+
export interface ICsqIndicatorsSet {
|
|
328
|
+
/** 1a mensagem do lead -> 1a resposta humana (liquido). */
|
|
329
|
+
frt_min: number | null;
|
|
330
|
+
/** Mediana de "pergunta do lead -> proxima resposta humana" (liquido). */
|
|
331
|
+
art_median_min: number | null;
|
|
332
|
+
/** p90 de "pergunta do lead -> proxima resposta humana" (liquido). */
|
|
333
|
+
art_p90_min: number | null;
|
|
334
|
+
/** Maior espera do lead por resposta humana (liquido). */
|
|
335
|
+
max_gap_min: number | null;
|
|
336
|
+
/** Perguntas do lead sem resposta humana. */
|
|
337
|
+
unanswered_questions: number | null;
|
|
338
|
+
/** A ultima mensagem do lead ficou sem resposta e o ticket fechou. */
|
|
339
|
+
last_message_unanswered: boolean | null;
|
|
340
|
+
/** 1a mensagem do lead -> 1a oferta concreta de horario (claim do juiz). */
|
|
341
|
+
time_to_first_offer_min: number | null;
|
|
342
|
+
/** 1o contato -> agendamento registrado na agenda. */
|
|
343
|
+
time_to_conversion_h: number | null;
|
|
344
|
+
/** Existe agendamento na agenda apos o 1o contato. */
|
|
345
|
+
converted: boolean | null;
|
|
346
|
+
messages_in: number | null;
|
|
347
|
+
messages_out_human: number | null;
|
|
348
|
+
messages_out_auto: number | null;
|
|
349
|
+
messages_out_ai: number | null;
|
|
350
|
+
/** Numero de humanos distintos que responderam. */
|
|
351
|
+
attendants_distinct: number | null;
|
|
352
|
+
tickets_count: number | null;
|
|
353
|
+
reopen_count: number | null;
|
|
354
|
+
transfer_count: number | null;
|
|
355
|
+
/** Tickets sem nenhuma mensagem humana. */
|
|
356
|
+
automation_only_tickets: number | null;
|
|
357
|
+
/** Audios do lead sem transcricao (reduz `confidence`). */
|
|
358
|
+
audio_in_untranscribed: number | null;
|
|
359
|
+
/** Dias desde a ultima mensagem do lead (indicador de reativacao). */
|
|
360
|
+
silence_days_since_last_inbound: number | null;
|
|
361
|
+
/** Mensagens humanas/templates apos o ultimo inbound. */
|
|
362
|
+
reactivation_attempts: number | null;
|
|
363
|
+
/** `true` quando o conjunto usa heuristica da F0 (ex.: pergunta do lead
|
|
364
|
+
* detectada por "?" em vez do claim `question_requires_answer` do juiz). */
|
|
365
|
+
provisional?: boolean;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** As duas lentes do laudo. */
|
|
369
|
+
export interface ICsqIndicators {
|
|
370
|
+
ticket: ICsqIndicatorsSet;
|
|
371
|
+
journey: ICsqIndicatorsSet;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ───────── Estado do lead (report-spec §2.3) ─────────
|
|
375
|
+
|
|
376
|
+
/** Campo do Estado do lead com valor unico. */
|
|
377
|
+
export interface ICsqLeadStateField {
|
|
378
|
+
value: string | null;
|
|
379
|
+
evidence: ICsqEvidence[];
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** Campo do Estado do lead com multiplos itens (restricoes, promessas...). */
|
|
383
|
+
export interface ICsqLeadStateListField {
|
|
384
|
+
value: string[];
|
|
385
|
+
evidence: ICsqEvidence[];
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/** Premissas extraidas pelo juiz e guardadas pelo codigo. E este bloco que o
|
|
389
|
+
* proximo fechamento recebe como contexto, no lugar de reler toda a jornada. */
|
|
390
|
+
export interface ICsqLeadState {
|
|
391
|
+
/** Quem fala x quem e o paciente. */
|
|
392
|
+
speaker_vs_patient: ICsqLeadStateField;
|
|
393
|
+
/** Queixa / procedimento buscado. */
|
|
394
|
+
complaint_or_procedure: ICsqLeadStateField;
|
|
395
|
+
/** Medica/profissional pretendido. */
|
|
396
|
+
preferred_professional: ICsqLeadStateField;
|
|
397
|
+
/** Primeira consulta ou ja e paciente (tag/qualificacao + conversa). */
|
|
398
|
+
patient_status: ICsqLeadStateField;
|
|
399
|
+
/** Convenio mencionado. */
|
|
400
|
+
insurance: ICsqLeadStateField;
|
|
401
|
+
/** Restricoes declaradas (dias, horarios, local, condicao de saude). */
|
|
402
|
+
constraints: ICsqLeadStateListField;
|
|
403
|
+
/** Preco informado (valor, forma, parcelas, o que inclui). */
|
|
404
|
+
price_informed: ICsqLeadStateField;
|
|
405
|
+
/** Agendamento informado ao lead (data, hora, profissional, modalidade). */
|
|
406
|
+
appointment_informed: ICsqLeadStateField;
|
|
407
|
+
/** Promessas da secretaria (o que, prazo). */
|
|
408
|
+
promises: ICsqLeadStateListField;
|
|
409
|
+
/** Perguntas do lead ainda sem resposta. */
|
|
410
|
+
open_questions: ICsqLeadStateListField;
|
|
411
|
+
/** Objecoes apresentadas e como foram tratadas. */
|
|
412
|
+
objections: ICsqLeadStateListField;
|
|
413
|
+
/** Dados obrigatorios coletados (x dicionario `capture-data`). */
|
|
414
|
+
required_data_collected: ICsqLeadStateListField;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ───────── Claims (report-spec §5) ─────────
|
|
418
|
+
|
|
419
|
+
/** "Informei agendamento em D/H com o profissional P". */
|
|
420
|
+
export interface ICsqClaimAppointmentInformed {
|
|
421
|
+
type: "appointment_informed";
|
|
422
|
+
/** Data informada (YYYY-MM-DD, TZ do tenant). */
|
|
423
|
+
date: string;
|
|
424
|
+
/** Hora informada (HH:mm), quando houve. */
|
|
425
|
+
time?: string;
|
|
426
|
+
professional?: string;
|
|
427
|
+
modality?: string;
|
|
428
|
+
evidence: ICsqEvidence[];
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/** "Prometi X ate quando". */
|
|
432
|
+
export interface ICsqClaimPromise {
|
|
433
|
+
type: "promise";
|
|
434
|
+
what: string;
|
|
435
|
+
/** Prazo declarado, em texto ("de imediato", "ate amanha"). */
|
|
436
|
+
due?: string;
|
|
437
|
+
evidence: ICsqEvidence[];
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/** "Coletei os dados A, B". */
|
|
441
|
+
export interface ICsqClaimDataCollected {
|
|
442
|
+
type: "data_collected";
|
|
443
|
+
fields: string[];
|
|
444
|
+
evidence: ICsqEvidence[];
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/** "O roteiro exige coletar os dados A, B" (adendo §C: o juiz declara o que o
|
|
448
|
+
* roteiro exige; o codigo confronta com `lead.capture_data`). */
|
|
449
|
+
export interface ICsqClaimRequiredData {
|
|
450
|
+
type: "required_data";
|
|
451
|
+
fields: string[];
|
|
452
|
+
evidence: ICsqEvidence[];
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/** "Desfecho: agendou / desistiu por valor / duvida medica...". */
|
|
456
|
+
export interface ICsqClaimOutcome {
|
|
457
|
+
type: "outcome";
|
|
458
|
+
outcome: string;
|
|
459
|
+
/** Codigo de fechamento que o juiz considera coerente com o desfecho. */
|
|
460
|
+
expected_close_code?: string;
|
|
461
|
+
evidence: ICsqEvidence[];
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/** "A pergunta do lead em M exigia resposta". */
|
|
465
|
+
export interface ICsqClaimQuestionRequiresAnswer {
|
|
466
|
+
type: "question_requires_answer";
|
|
467
|
+
message_id: string;
|
|
468
|
+
question: string;
|
|
469
|
+
evidence: ICsqEvidence[];
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/** "1a oferta concreta de horario em M". */
|
|
473
|
+
export interface ICsqClaimFirstConcreteOffer {
|
|
474
|
+
type: "first_concrete_offer";
|
|
475
|
+
message_id: string;
|
|
476
|
+
evidence: ICsqEvidence[];
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/** "Lead e paciente / primeira consulta". */
|
|
480
|
+
export interface ICsqClaimPatientStatus {
|
|
481
|
+
type: "patient_status";
|
|
482
|
+
status: "first_visit" | "existing_patient" | "unknown";
|
|
483
|
+
evidence: ICsqEvidence[];
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/** "Informei preco X na forma Y". */
|
|
487
|
+
export interface ICsqClaimPriceInformed {
|
|
488
|
+
type: "price_informed";
|
|
489
|
+
amount?: string;
|
|
490
|
+
payment_method?: string;
|
|
491
|
+
installments?: string;
|
|
492
|
+
includes?: string;
|
|
493
|
+
evidence: ICsqEvidence[];
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/** Uniao discriminada por `type` — o juiz extrai, o codigo verifica (§5). */
|
|
497
|
+
export type ICsqClaim =
|
|
498
|
+
| ICsqClaimAppointmentInformed
|
|
499
|
+
| ICsqClaimPromise
|
|
500
|
+
| ICsqClaimDataCollected
|
|
501
|
+
| ICsqClaimRequiredData
|
|
502
|
+
| ICsqClaimOutcome
|
|
503
|
+
| ICsqClaimQuestionRequiresAnswer
|
|
504
|
+
| ICsqClaimFirstConcreteOffer
|
|
505
|
+
| ICsqClaimPatientStatus
|
|
506
|
+
| ICsqClaimPriceInformed;
|
|
507
|
+
|
|
508
|
+
export type CsqClaimType = ICsqClaim["type"];
|
|
509
|
+
|
|
510
|
+
/** Linha da tabela "o que foi dito x o que consta" (report-spec §2.5). */
|
|
511
|
+
export interface ICsqCrossCheck {
|
|
512
|
+
claim_type: CsqClaimType;
|
|
513
|
+
/** Resultado da verificacao por codigo. */
|
|
514
|
+
result: "match" | "mismatch" | "missing" | "inconclusive";
|
|
515
|
+
/** O que foi dito (resumo do claim). */
|
|
516
|
+
expected: string;
|
|
517
|
+
/** O que consta no sistema. */
|
|
518
|
+
found: string | null;
|
|
519
|
+
/** Alerta gerado por esta verificacao, quando houve. */
|
|
520
|
+
alert_code?: CsqAlertCode;
|
|
521
|
+
note?: string;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// ───────── Avaliacao (report-spec §4) ─────────
|
|
525
|
+
|
|
526
|
+
/** Nota de um criterio. Todo criterio < 100 exige evidencia valida; sem ela o
|
|
527
|
+
* codigo zera para `null` e tira do denominador. */
|
|
528
|
+
export interface ICsqCriterionScore {
|
|
529
|
+
pillar: CsqPillarCode;
|
|
530
|
+
criterion: string;
|
|
531
|
+
score: CsqCriterionScore;
|
|
532
|
+
evidence: ICsqEvidence[];
|
|
533
|
+
note?: string;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/** Cobertura de criterios de um pilar (quantos foram pontuados x total). */
|
|
537
|
+
export interface ICsqPillarCoverage {
|
|
538
|
+
scored: number;
|
|
539
|
+
total: number;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/** Nota do pilar = media dos criterios nao-N/A. `null` = pilar sem criterio
|
|
543
|
+
* pontuavel (sai do denominador do score). */
|
|
544
|
+
export interface ICsqPillarScore {
|
|
545
|
+
pillar: CsqPillarCode;
|
|
546
|
+
score: number | null;
|
|
547
|
+
coverage: ICsqPillarCoverage;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/** Resultado do `computeScore`. `score` = jornada (decisao do usuario);
|
|
551
|
+
* `ticket_score` = so criterios com evidencia no ticket fechado (Q11=A). */
|
|
552
|
+
export interface ICsqEvaluation {
|
|
553
|
+
pillars: ICsqPillarScore[];
|
|
554
|
+
score: number | null;
|
|
555
|
+
ticket_score: number | null;
|
|
556
|
+
status: CsqStatus;
|
|
557
|
+
/** Codigos de alerta que caracterizaram auto-fail nesta avaliacao. */
|
|
558
|
+
auto_fail: CsqAlertCode[];
|
|
559
|
+
escalate: {
|
|
560
|
+
value: boolean;
|
|
561
|
+
reasons: string[];
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// ───────── Atribuicao e cobertura (adendo §E e §G) ─────────
|
|
566
|
+
|
|
567
|
+
/** Humano que escreveu no ticket/jornada. Sem agregados por pessoa na F1. */
|
|
568
|
+
export interface ICsqContributor {
|
|
569
|
+
user_id: string;
|
|
570
|
+
name: string;
|
|
571
|
+
messages_in_ticket: number;
|
|
572
|
+
messages_in_journey: number;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/** Quanto da jornada e da rubrica a avaliacao conseguiu cobrir. A UI avisa
|
|
576
|
+
* quando a cobertura fica abaixo de 50%. */
|
|
577
|
+
export interface ICsqCoverage {
|
|
578
|
+
/** `single` = apenas a thread do ticket fechado (unico modo na F1). */
|
|
579
|
+
threads: "single" | "all";
|
|
580
|
+
tickets_covered: number;
|
|
581
|
+
tickets_total: number;
|
|
582
|
+
/** Mensagens cortadas por orcamento de tokens/bytes. */
|
|
583
|
+
messages_truncated: number;
|
|
584
|
+
criteria_scored_by_pillar: Record<string, ICsqPillarCoverage>;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/** Sinalizacao de dado ausente que degrada a avaliacao (adendo §G). */
|
|
588
|
+
export interface ICsqDataQuality {
|
|
589
|
+
/** Binding sem regra de horario: metricas liquidas ficam `null`. */
|
|
590
|
+
missing_business_hours?: boolean;
|
|
591
|
+
/** Roteiro nao configurado: pilar Roteiro forcado a N/A. */
|
|
592
|
+
missing_prompt_stack?: boolean;
|
|
593
|
+
/** Agenda indisponivel: verificacoes de agendamento ficam inconclusivas. */
|
|
594
|
+
missing_calendar?: boolean;
|
|
595
|
+
[key: string]: boolean | undefined;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// ───────── Veredito humano (adendo §D / Q9=A) ─────────
|
|
599
|
+
|
|
600
|
+
/** Concordo/Discordo do supervisor + alertas marcados como falsos. Alimenta o
|
|
601
|
+
* conjunto de calibracao (metricas na F2). */
|
|
602
|
+
export interface ICsqReview {
|
|
603
|
+
verdict: CsqReviewVerdict;
|
|
604
|
+
/** Fingerprints dos alertas marcados como falso positivo. */
|
|
605
|
+
false_alert_fingerprints: string[];
|
|
606
|
+
note?: string;
|
|
607
|
+
reviewer_login_id: string;
|
|
608
|
+
reviewed_at: Date;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// ───────── Trilha e denormalizacao ─────────
|
|
612
|
+
|
|
613
|
+
/** Entrada da trilha do lead (so tendencia; a serie completa vai para o BQ). */
|
|
614
|
+
export interface ICsqTrailEntry {
|
|
615
|
+
ticket_id: string;
|
|
616
|
+
closed_at: Date;
|
|
617
|
+
evaluated_at: Date;
|
|
618
|
+
score: number | null;
|
|
619
|
+
status: CsqStatus;
|
|
620
|
+
alerts_critical: number;
|
|
621
|
+
alerts_new: number;
|
|
622
|
+
alerts_resolved: number;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/** Resumo denormalizado gravado em `ticket.csq` (fila e listagem do Omni). */
|
|
626
|
+
export interface ICsqTicketSummary {
|
|
627
|
+
score: number | null;
|
|
628
|
+
status: CsqStatus;
|
|
629
|
+
alerts_critical_open: number;
|
|
630
|
+
evaluated_at: Date;
|
|
631
|
+
rubric_version: string;
|
|
632
|
+
lead_id: string;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// ───────── Transcricao entregue ao juiz ─────────
|
|
636
|
+
|
|
637
|
+
/** Mensagem rotulada da conversa do lead. `unattributed` marca outbound sem
|
|
638
|
+
* `created_by` (atribuido a secretaria do canal por default). */
|
|
639
|
+
export interface ICsqTranscriptMessage {
|
|
640
|
+
message_id: string;
|
|
641
|
+
ticket_id: string;
|
|
642
|
+
ts: Date;
|
|
643
|
+
author_kind: CsqAuthorKind;
|
|
644
|
+
author_label: string;
|
|
645
|
+
type: string;
|
|
646
|
+
text: string;
|
|
647
|
+
unattributed?: boolean;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// ───────── Saida do juiz ─────────
|
|
651
|
+
|
|
652
|
+
/** Uso de tokens da chamada ao juiz (acumulado das tentativas). */
|
|
653
|
+
export interface ICsqLlmUsage {
|
|
654
|
+
input_tokens: number;
|
|
655
|
+
output_tokens: number;
|
|
656
|
+
cache_read_tokens?: number;
|
|
657
|
+
cache_creation_tokens?: number;
|
|
658
|
+
/** Tentativas gastas. Contrato: <= 2 (1 retry por falha de schema). */
|
|
659
|
+
rounds?: number;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/** Saida estruturada da tool `submit_csq_evaluation`. O juiz NAO recebe tools
|
|
663
|
+
* de leitura e nao decide o score final: so pontua, extrai e cita evidencia. */
|
|
664
|
+
export interface ICsqJudgeOutput {
|
|
665
|
+
summary: string;
|
|
666
|
+
lead_state: ICsqLeadState;
|
|
667
|
+
claims: ICsqClaim[];
|
|
668
|
+
criteria: ICsqCriterionScore[];
|
|
669
|
+
/** So alertas de fonte `llm` ou `hybrid`; os deterministicos vem do codigo. */
|
|
670
|
+
alerts: ICsqAlert[];
|
|
671
|
+
/** Fingerprints de alertas anteriores que este atendimento resolveu.
|
|
672
|
+
* So resolve alertas de fonte `llm` (adendo §G). */
|
|
673
|
+
resolved_alert_ids: string[];
|
|
674
|
+
highlights: string[];
|
|
675
|
+
improvements: string[];
|
|
676
|
+
confidence: number;
|
|
677
|
+
notes?: string;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// ───────── Fatos do sistema (report-spec §2.4) ─────────
|
|
681
|
+
|
|
682
|
+
/** Agendamento do(s) paciente(s) vinculado(s) ao lead. */
|
|
683
|
+
export interface ICsqAppointmentFact {
|
|
684
|
+
id: string;
|
|
685
|
+
calendar_id: string;
|
|
686
|
+
patient_id?: string;
|
|
687
|
+
start_at: Date;
|
|
688
|
+
end_at?: Date;
|
|
689
|
+
status?: string;
|
|
690
|
+
status_base?: string;
|
|
691
|
+
professional_name?: string;
|
|
692
|
+
modality?: string;
|
|
693
|
+
created_at?: Date;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/** Sobreposicao detectada entre agendamentos do mesmo profissional. */
|
|
697
|
+
export interface ICsqOverlapFact {
|
|
698
|
+
professional_name?: string;
|
|
699
|
+
appointment_ids: string[];
|
|
700
|
+
start_at: Date;
|
|
701
|
+
end_at: Date;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/** Ticket anterior da jornada (motivo, quem fechou). */
|
|
705
|
+
export interface ICsqPreviousTicketFact {
|
|
706
|
+
ticket_id: string;
|
|
707
|
+
closed_at?: Date;
|
|
708
|
+
close_code?: string;
|
|
709
|
+
close_name?: string;
|
|
710
|
+
closed_by_name?: string;
|
|
711
|
+
messages_count?: number;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/** NF ou link de pagamento ligado a um agendamento. */
|
|
715
|
+
export interface ICsqBillingFact {
|
|
716
|
+
id: string;
|
|
717
|
+
kind: "invoice" | "payment_link";
|
|
718
|
+
appointment_id?: string;
|
|
719
|
+
status?: string;
|
|
720
|
+
/** Ha envio registrado ao lead (evo_chat.notifications / evo_chat_sent). */
|
|
721
|
+
sent?: boolean;
|
|
722
|
+
sent_at?: Date;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/** Bloco "Fatos do sistema" — tudo lido do Firestore antes da chamada ao juiz
|
|
726
|
+
* (principio §I: contexto pre-hidratado, zero rounds de descoberta). */
|
|
727
|
+
export interface ICsqSystemFacts {
|
|
728
|
+
appointments: ICsqAppointmentFact[];
|
|
729
|
+
overlaps: ICsqOverlapFact[];
|
|
730
|
+
tags: string[];
|
|
731
|
+
qualification: string | null;
|
|
732
|
+
dist_channel: string | null;
|
|
733
|
+
capture_data: Record<string, unknown> | null;
|
|
734
|
+
invoices: ICsqBillingFact[];
|
|
735
|
+
payment_links: ICsqBillingFact[];
|
|
736
|
+
previous_tickets: ICsqPreviousTicketFact[];
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// ───────── Cabecalho do laudo (report-spec §2.1) ─────────
|
|
740
|
+
|
|
741
|
+
/** Lead avaliado. `phone` e mascarado na projecao da fila (adendo §F). */
|
|
742
|
+
export interface ICsqReportLead {
|
|
743
|
+
name: string | null;
|
|
744
|
+
phone: string | null;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/** Paciente vinculado ao lead. */
|
|
748
|
+
export interface ICsqReportPatient {
|
|
749
|
+
id: string;
|
|
750
|
+
name: string | null;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/** Atendente humano que aparece no cabecalho. */
|
|
754
|
+
export interface ICsqReportAttendant {
|
|
755
|
+
user_id: string;
|
|
756
|
+
name: string | null;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
export interface ICsqReportHeader {
|
|
760
|
+
lead: ICsqReportLead;
|
|
761
|
+
patients: ICsqReportPatient[];
|
|
762
|
+
attendants: ICsqReportAttendant[];
|
|
763
|
+
/** 1a mensagem considerada -> fechamento. */
|
|
764
|
+
period: { from: Date | null; to: Date | null };
|
|
765
|
+
tickets_count: number;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/** Ticket que disparou a avaliacao. */
|
|
769
|
+
export interface ICsqEvaluatedFor {
|
|
770
|
+
ticket_id: string;
|
|
771
|
+
closed_at: Date;
|
|
772
|
+
close: {
|
|
773
|
+
code?: string | null;
|
|
774
|
+
name?: string | null;
|
|
775
|
+
type_code?: string | null;
|
|
776
|
+
notes?: string | null;
|
|
777
|
+
};
|
|
778
|
+
closed_by_user_id: string | null;
|
|
779
|
+
closed_by_name: string | null;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/** Por que este fechamento foi considerado elegivel (gate). */
|
|
783
|
+
export interface ICsqEligibility {
|
|
784
|
+
csq_mode: CsqMode;
|
|
785
|
+
eligible_calendar_ids: string[];
|
|
786
|
+
has_human_message: boolean;
|
|
787
|
+
reason?: string;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/** Contagem de alertas do laudo (usada pela fila e pelos indices). */
|
|
791
|
+
export interface ICsqAlertCounts {
|
|
792
|
+
alerts_open: number;
|
|
793
|
+
alerts_critical_open: number;
|
|
794
|
+
alerts_new: number;
|
|
795
|
+
alerts_resolved: number;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// ───────── Ledger de execucao (adendo §A) ─────────
|
|
799
|
+
|
|
800
|
+
/** `tenants/{t}/apps/evo-csq/runs/{eventId}` — idempotencia e retry do
|
|
801
|
+
* pipeline. Run `done` faz o handler dar ACK imediato. */
|
|
802
|
+
export interface ICsqRun extends IFireDoc {
|
|
803
|
+
/** `${ticketId}:${closedAt ISO}` — id do doc e chave de idempotencia. */
|
|
804
|
+
event_id: string;
|
|
805
|
+
status: CsqRunStatus;
|
|
806
|
+
attempts: number;
|
|
807
|
+
started_at: Date;
|
|
808
|
+
finished_at?: Date;
|
|
809
|
+
error?: string;
|
|
810
|
+
phase: CsqPhase;
|
|
811
|
+
evaluation_id?: string;
|
|
812
|
+
/** `true` quando a linha do BigQuery ainda nao foi publicada. */
|
|
813
|
+
bq_pending?: boolean;
|
|
814
|
+
/** TTL do Firestore (+30 dias). */
|
|
815
|
+
expires_at: Date;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// ───────── Laudo corrente por lead ─────────
|
|
819
|
+
|
|
820
|
+
/** `tenants/{t}/apps/evo-csq/lead-reports/{leadId}` — relatorio corrente,
|
|
821
|
+
* sobrescrito a cada fechamento humano elegivel. Nao ha historico de laudos
|
|
822
|
+
* no Firestore: a serie completa vai para o BigQuery e a tendencia fica em
|
|
823
|
+
* `trail[]`. */
|
|
824
|
+
export interface ICsqLeadReport extends IFireDoc {
|
|
825
|
+
model_ver: number;
|
|
826
|
+
lead_id: string;
|
|
827
|
+
contact_id: string;
|
|
828
|
+
thread_id: string;
|
|
829
|
+
department_id: string;
|
|
830
|
+
department_name: string | null;
|
|
831
|
+
channel_id: string | null;
|
|
832
|
+
phase: CsqPhase;
|
|
833
|
+
rubric_version: string;
|
|
834
|
+
/** Modelo do juiz usado (null na F0). */
|
|
835
|
+
model: string | null;
|
|
836
|
+
/** Versao do prompt do juiz (rubrica + instrucoes de slots). */
|
|
837
|
+
prompt_version: string | null;
|
|
838
|
+
evaluated_at: Date;
|
|
839
|
+
/** Ultimo `event_id` processado — corta redelivery do Pub/Sub. */
|
|
840
|
+
last_event_key: string;
|
|
841
|
+
/** Id deterministico da avaliacao (mesmo id no BigQuery). */
|
|
842
|
+
evaluation_id: string;
|
|
843
|
+
/** Incrementado a cada regravacao do mesmo `evaluation_id` (rechecagem). */
|
|
844
|
+
version: number;
|
|
845
|
+
|
|
846
|
+
evaluated_for: ICsqEvaluatedFor;
|
|
847
|
+
header: ICsqReportHeader;
|
|
848
|
+
eligibility: ICsqEligibility;
|
|
849
|
+
indicators: ICsqIndicators;
|
|
850
|
+
system_facts: ICsqSystemFacts;
|
|
851
|
+
|
|
852
|
+
summary: string | null;
|
|
853
|
+
lead_state: ICsqLeadState | null;
|
|
854
|
+
claims: ICsqClaim[];
|
|
855
|
+
cross_checks: ICsqCrossCheck[];
|
|
856
|
+
/** Notas por critério (saída do juiz, com evidência) — persistidas para a UI expandir os pilares. */
|
|
857
|
+
criteria?: ICsqCriterionScore[];
|
|
858
|
+
evaluation: ICsqEvaluation;
|
|
859
|
+
alerts: ICsqAlert[];
|
|
860
|
+
counts: ICsqAlertCounts;
|
|
861
|
+
/** Espelho de `counts.alerts_critical_open > 0` (campo para indice). */
|
|
862
|
+
has_critical_open: boolean;
|
|
863
|
+
|
|
864
|
+
highlights: string[];
|
|
865
|
+
improvements: string[];
|
|
866
|
+
confidence: number;
|
|
867
|
+
|
|
868
|
+
/** Quem escreveu no ticket e na jornada (adendo §E). */
|
|
869
|
+
contributors: ICsqContributor[];
|
|
870
|
+
/** Preenchido so quando um unico humano escreveu no ticket fechado. */
|
|
871
|
+
primary_agent_id: string | null;
|
|
872
|
+
coverage: ICsqCoverage;
|
|
873
|
+
data_quality?: ICsqDataQuality;
|
|
874
|
+
/** Veredito humano (adendo §D). */
|
|
875
|
+
review?: ICsqReview;
|
|
876
|
+
/** Estado da rechecagem agendada da agenda no nivel do laudo (adendo §B). */
|
|
877
|
+
verification?: CsqVerificationState;
|
|
878
|
+
|
|
879
|
+
/** Hash do snapshot de politicas (stack de prompts do tenant). */
|
|
880
|
+
policy_snapshot_hash: string | null;
|
|
881
|
+
/** Hash do snapshot dos dicionarios usados na avaliacao. */
|
|
882
|
+
dictionary_snapshot_hash: string | null;
|
|
883
|
+
|
|
884
|
+
llm_usage: ICsqLlmUsage | null;
|
|
885
|
+
cost_usd_est: number | null;
|
|
886
|
+
trail: ICsqTrailEntry[];
|
|
887
|
+
judge_error?: string;
|
|
888
|
+
}
|