- AppShell: corregir condición de carrera REST/WS que perdía tokens de agent_stream_chunk
- init_state atómico + eliminación de doble fuente REST/WS para actualización en tiempo real
- conversation_ended e idempotencia de eventos en máquina de estados por conversación
- Seguridad: migrar JWT de query param a In-Band Auth (primer mensaje {action:auth}) con timeout 5s y cierre 1008
- Multi-stream buffer: reemplazar buffer plano por TTL LRU (200 entradas, 60s TTL) para evitar pisado de tokens entre agentes
- agent_stream_completed ya no borra buffer incondicionalmente — delega purge a la política LRU
- Timer: corregir display de 00:00 en estado PENDING con visualización inmediata + cleanup en stop()
- Tests: 8 tests multi-stream, tests In-Band Auth, tests idempotencia y máquina de estados, tests Timer
- Resultado: 86/86 tests pasan | TypeScript 0 errores
448 lines
20 KiB
Markdown
448 lines
20 KiB
Markdown
# Handoff Document — Backend Python (Claro Cases)
|
|
|
|
> **Destinatario**: Agente de IA del equipo backend Python.
|
|
> **Objetivo**: Implementar el servidor REST + WebSocket que alimenta el dashboard HITL de Claro Cases.
|
|
> **Versión del contrato**: 1.0 — Julio 2026
|
|
|
|
---
|
|
|
|
## 1. Resumen Arquitectónico del Frontend
|
|
|
|
### Propósito del Proyecto
|
|
Dashboard *Human-in-the-Loop* (HITL) que permite a asesores humanos:
|
|
1. **Gestionar peticiones HITL** recibidas de un agente virtual durante conversaciones con clientes.
|
|
2. **Monitorear en tiempo real** todas las conversaciones activas, con capacidad de inyectar notas internas.
|
|
|
|
### Stack del Frontend
|
|
| Componente | Tecnología |
|
|
|-----------|-----------|
|
|
| Framework | React 19 + TypeScript |
|
|
| Build tool | Vite 6 |
|
|
| Estado global | Zustand |
|
|
| Ruteo | React Router (`/cases`, `/monitor`) |
|
|
| Estilos | Tailwind CSS v4 (CSS-first con `@theme`) |
|
|
| Validación | Zod |
|
|
| Comunicación | REST (canal autoritativo) + WebSocket (difusión/streaming) |
|
|
| Mock development | MSW (Mock Service Worker) — solo en modo `dev` |
|
|
|
|
### Arquitectura de Comunicación
|
|
```
|
|
┌──────────────────────────────────────┐
|
|
│ Frontend React │
|
|
│ │
|
|
│ /cases ───▶ REST POST /cases/:id/ │──▶ Backend
|
|
│ resolve (ESCRITURA) │ Python
|
|
│ │
|
|
│ /monitor ◀─── WebSocket /ws/ │◀──
|
|
│ dashboard (DIFUSIÓN) │
|
|
└──────────────────────────────────────┘
|
|
```
|
|
|
|
**Regla de oro**: REST es el **único canal autoritativo de escritura**. WebSocket es exclusivamente para difusión de eventos y streaming en tiempo real desde el servidor hacia el cliente. El cliente solo envía por WebSocket el evento `internal_note` (notas internas del asesor).
|
|
|
|
---
|
|
|
|
## 2. Contratos de la API REST
|
|
|
|
**Base URL**: `http://<host>:<port>/api/v1`
|
|
|
|
### 2.1 Listar Casos (con filtros y paginación)
|
|
|
|
```
|
|
GET /api/v1/cases?status=<status>&applicative=<app>&search=<query>&offset=<n>&limit=<n>
|
|
```
|
|
|
|
**Query Parameters** (todos opcionales):
|
|
|
|
| Parámetro | Tipo | Descripción | Ejemplo |
|
|
|-----------|------|-------------|---------|
|
|
| `status` | string | Filtrar por estado | `PENDING`, `IN_PROGRESS`, `RESOLVED`, `FAILED` |
|
|
| `applicative` | string | Filtrar por aplicativo | `AC+`, `ASCARD`, `DiMe`, `Formatos SGCS`, `Mi asistencia 360`, `Paradigma`, `RR`, `Phone Protect` |
|
|
| `search` | string | Búsqueda textual (título, ID externo, cédula, tipo solicitud) | `Pérez` |
|
|
| `offset` | integer | Offset de paginación (default 0) | `0` |
|
|
| `limit` | integer | Límite de items (default 20) | `20` |
|
|
|
|
**Respuesta** (`200 OK`):
|
|
```json
|
|
{
|
|
"items": [
|
|
{
|
|
"id": 1,
|
|
"title": "Validación de Proporcionales - Móvil",
|
|
"description": "Validar si el cliente tiene cobros proporcionales...",
|
|
"status": "PENDING",
|
|
"externalId": "EXT-001",
|
|
"cedula": "1020304050",
|
|
"tipoSolicitud": "Validar_Proporcionales_Movil",
|
|
"applicative": "AC+",
|
|
"uiPattern": "CONFIRMATION_WITH_VALUE",
|
|
"payload": {
|
|
"nombre": "Juan Pérez",
|
|
"telefono": "3101234567",
|
|
"linea": "3008001234"
|
|
},
|
|
"handlingTime": 0,
|
|
"createdAt": "2026-07-23T15:00:00.000Z"
|
|
}
|
|
],
|
|
"total": 53
|
|
}
|
|
```
|
|
|
|
**Campos del objeto `CaseRequest`**:
|
|
|
|
| Campo | Tipo | Requerido | Descripción |
|
|
|-------|------|:--------:|-------------|
|
|
| `id` | number | ✅ | ID único del caso |
|
|
| `title` | string | ✅ | Título descriptivo |
|
|
| `description` | string | ✅ | Descripción detallada |
|
|
| `status` | string | ✅ | `PENDING`, `IN_PROGRESS`, `RESOLVED`, `FAILED` |
|
|
| `externalId` | string | ❌ | ID externo de referencia (ej. número de ticket) |
|
|
| `cedula` | string | ❌ | Documento de identidad del cliente |
|
|
| `tipoSolicitud` | string | ✅ | **Debe coincidir con un `toolName` del CSV** (ver sección 5) |
|
|
| `applicative` | string | ✅ | Aplicativo origen: `AC+`, `ASCARD`, `DiMe`, `RR`, etc. |
|
|
| `uiPattern` | string | ✅ | Patrón de UI: `SIMPLE_CONFIRMATION`, `CONFIRMATION_WITH_VALUE`, `MULTI_FIELD_FORM`, `DATE_SIMPLE`, `FREE_TEXT`, `READ_ONLY` |
|
|
| `payload` | object | ✅ | Datos adicionales del caso (estructura libre) |
|
|
| `handlingTime` | number | ✅ | Tiempo de gestión en segundos (0 si no iniciado) |
|
|
| `createdAt` | string | ✅ | Timestamp ISO-8601 UTC |
|
|
|
|
### 2.2 Obtener Caso Individual
|
|
|
|
```
|
|
GET /api/v1/cases/:id
|
|
```
|
|
|
|
**Respuesta** (`200 OK`): Objeto `CaseRequest` (misma estructura que arriba).
|
|
**Error** (`404`): Si el caso no existe.
|
|
|
|
### 2.3 Resolver Caso (CANAL AUTORITATIVO)
|
|
|
|
```
|
|
POST /api/v1/cases/:id/resolve
|
|
Content-Type: application/json
|
|
```
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"action": "approved",
|
|
"payload": {
|
|
"confirmacion": true,
|
|
"valor": 15000
|
|
},
|
|
"note": "Cliente verificó con documento de identidad"
|
|
}
|
|
```
|
|
|
|
| Campo | Tipo | Requerido | Descripción |
|
|
|-------|------|:--------:|-------------|
|
|
| `action` | string | ✅ | `"approved"` o `"rejected"` |
|
|
| `payload` | object | ✅ | Datos de resolución (estructura depende del `uiPattern` del caso) |
|
|
| `note` | string | ❌ | Nota opcional del asesor |
|
|
|
|
**⚠️ El backend DEBE**:
|
|
1. Derivar `advisorId` del token de autenticación de la sesión HTTP (Bearer token o cookie). **El cliente NO envía `advisorId`.**
|
|
2. Actualizar el `status` del caso a `RESOLVED`.
|
|
3. Actualizar `handlingTime` con la diferencia entre `startedAt` y `resolvedAt` (timestamps propios del backend).
|
|
4. Fusionar el `payload` de resolución con el `payload` existente del caso.
|
|
5. Tras resolver, **emitir el evento `hitl_resolved` por WebSocket** a todos los clientes conectados (broadcast).
|
|
|
|
**Respuesta** (`200 OK`): Objeto `CaseRequest` actualizado.
|
|
|
|
### 2.4 Obtener Conversaciones Activas
|
|
|
|
```
|
|
GET /api/v1/conversations/active
|
|
```
|
|
|
|
**Respuesta** (`200 OK`):
|
|
```json
|
|
[
|
|
{
|
|
"id": "conv-1",
|
|
"clientId": "CLI-001",
|
|
"agentId": "AGENT-01",
|
|
"status": "active",
|
|
"messages": [
|
|
{
|
|
"id": "m1",
|
|
"conversationId": "conv-1",
|
|
"role": "user",
|
|
"content": "Hola, necesito ayuda con mi factura",
|
|
"timestamp": "2026-07-23T15:00:00.000Z",
|
|
"isStreaming": false,
|
|
"metadata": {}
|
|
}
|
|
],
|
|
"createdAt": "2026-07-23T15:00:00.000Z"
|
|
}
|
|
]
|
|
```
|
|
|
|
### 2.5 Obtener Conversación Individual
|
|
|
|
```
|
|
GET /api/v1/conversations/:id
|
|
```
|
|
|
|
**Respuesta** (`200 OK`): Objeto `Conversation` con todos sus mensajes.
|
|
|
|
---
|
|
|
|
## 3. Protocolo y Eventos WebSocket
|
|
|
|
**URL**: `ws://<host>:<port>/ws/dashboard`
|
|
|
|
### 3.1 Envelope Estándar
|
|
|
|
Todo mensaje WebSocket (en ambas direcciones) **debe** usar el siguiente envelope JSON:
|
|
|
|
```json
|
|
{
|
|
"type": "string",
|
|
"eventId": "550e8400-e29b-41d4-a716-446655440000",
|
|
"occurredAt": "2026-07-23T15:00:00.000Z",
|
|
"payload": { }
|
|
}
|
|
```
|
|
|
|
| Campo | Tipo | Descripción |
|
|
|-------|------|-------------|
|
|
| `type` | string | Tipo de evento (ver tablas abajo) |
|
|
| `eventId` | string (UUID v4) | ID único del evento para deduplicación |
|
|
| `occurredAt` | string (ISO-8601 UTC) | Timestamp del lado emisor |
|
|
| `payload` | object | Carga específica del evento |
|
|
|
|
### 3.2 Ciclo de Vida de Conexión y Reconexión
|
|
|
|
1. **Handshake inicial**: El frontend se conecta a `ws://<host>/ws/dashboard`.
|
|
2. **`init_state`**: Al establecer la conexión, el backend **DEBE** enviar inmediatamente un evento `init_state` con el estado completo actual (conversaciones activas + casos pendientes).
|
|
3. **Reconexión**: El frontend implementa backoff exponencial (1s → 2s → 4s → 8s → 16s → máx 30s, factor 2x). Al reconectar, el backend envía nuevamente `init_state` y el frontend **reemplaza** su estado local completo.
|
|
4. **Heartbeat**: Se recomienda que el backend envíe pings periódicos (cada 30s) para detectar desconexiones.
|
|
|
|
### 3.3 Eventos Servidor → Cliente
|
|
|
|
| `type` | Payload | Cuándo se emite |
|
|
|--------|---------|----------------|
|
|
| `init_state` | `{ conversations: Conversation[], activeCases: CaseRequest[] }` | Al conectar o reconectar |
|
|
| `conversation_started` | `{ conversation: Conversation }` | Nueva conversación iniciada |
|
|
| `conversation_ended` | `{ conversationId: string, endedAt: string }` | Conversación finalizada |
|
|
| `user_message` | `{ conversationId: string, message: Message }` | Mensaje completo del usuario |
|
|
| `agent_stream_started` | `{ conversationId: string, messageId: string }` | El agente comienza a generar respuesta |
|
|
| `agent_stream_chunk` | `{ conversationId: string, messageId: string, token: string, index: number }` | Token individual (índice garantiza orden) |
|
|
| `agent_stream_completed` | `{ conversationId: string, messageId: string, fullContent: string }` | Streaming finalizado; `fullContent` es el texto completo |
|
|
| `agent_status_update` | `{ agentId: string, status: "ONLINE" \| "BUSY" \| "OFFLINE" }` | Cambio de estado del agente |
|
|
| `hitl_request` | `{ case: CaseRequest, conversationId: string }` | Se requiere intervención humana |
|
|
| `hitl_resolved` | `{ caseId: number, resolution: object }` | Caso resuelto (broadcast a todos los asesores) |
|
|
| `error` | `{ code: string, message: string, details?: object }` | Error del servidor notificable |
|
|
|
|
#### Ejemplo: Streaming de mensaje del agente
|
|
|
|
```
|
|
Servidor → Cliente:
|
|
|
|
1. { "type": "agent_stream_started", "payload": { "conversationId": "conv-1", "messageId": "m10" } }
|
|
2. { "type": "agent_stream_chunk", "payload": { "conversationId": "conv-1", "messageId": "m10", "token": "Cl", "index": 0 } }
|
|
3. { "type": "agent_stream_chunk", "payload": { "conversationId": "conv-1", "messageId": "m10", "token": "aro", "index": 1 } }
|
|
4. { "type": "agent_stream_chunk", "payload": { "conversationId": "conv-1", "messageId": "m10", "token": ", ", "index": 2 } }
|
|
5. { "type": "agent_stream_chunk", "payload": { "conversationId": "conv-1", "messageId": "m10", "token": "con", "index": 3 } }
|
|
...
|
|
N. { "type": "agent_stream_completed", "payload": { "conversationId": "conv-1", "messageId": "m10", "fullContent": "Claro, con gusto le ayudo..." } }
|
|
```
|
|
|
|
**⚠️ Importante**: Los tokens deben enviarse con `index` secuencial (0, 1, 2, ...) para que el frontend pueda reconstruir el orden incluso si los chunks llegan desordenados por la red.
|
|
|
|
#### Ejemplo: Solicitud HITL
|
|
|
|
```json
|
|
{
|
|
"type": "hitl_request",
|
|
"eventId": "a1b2c3d4-...",
|
|
"occurredAt": "2026-07-23T15:01:00.000Z",
|
|
"payload": {
|
|
"case": {
|
|
"id": 42,
|
|
"title": "Validación de Identidad - Cliente",
|
|
"description": "Validar la identidad del cliente...",
|
|
"status": "PENDING",
|
|
"externalId": "EXT-042",
|
|
"cedula": "1020304050",
|
|
"tipoSolicitud": "Validar_Identidad_Movil",
|
|
"applicative": "AC+",
|
|
"uiPattern": "SIMPLE_CONFIRMATION",
|
|
"payload": { "nombre": "Juan Pérez", "telefono": "3101234567" },
|
|
"handlingTime": 0,
|
|
"createdAt": "2026-07-23T15:01:00.000Z"
|
|
},
|
|
"conversationId": "conv-1"
|
|
}
|
|
}
|
|
```
|
|
|
|
Al recibir `hitl_request`, el frontend automáticamente:
|
|
- Inserta el caso en la lista del dashboard
|
|
- Reproduce una alerta sonora (Web Audio API)
|
|
- Muestra una notificación de escritorio HTML5
|
|
- Hace parpadear el título de la pestaña si el navegador no está enfocado
|
|
|
|
### 3.4 Eventos Cliente → Servidor
|
|
|
|
| `type` | Payload | Cuándo se envía |
|
|
|--------|---------|----------------|
|
|
| `internal_note` | `{ conversationId: string, content: string }` | Asesor inyecta nota interna desde el monitor |
|
|
|
|
**Ejemplo**:
|
|
```json
|
|
{
|
|
"type": "internal_note",
|
|
"eventId": "f9e8d7c6-...",
|
|
"occurredAt": "2026-07-23T15:02:00.000Z",
|
|
"payload": {
|
|
"conversationId": "conv-1",
|
|
"content": "Cliente tiene historial de reclamos similares. Verificar antes de aprobar."
|
|
}
|
|
}
|
|
```
|
|
|
|
**⚠️ El backend DEBE**:
|
|
- Derivar `advisorId` del contexto de la conexión WebSocket autenticada.
|
|
- **Ignorar cualquier campo `advisorId`** que pudiera venir en el payload (el cliente no lo envía, pero por seguridad).
|
|
- Insertar la nota como un mensaje con `role: "internal"` en la conversación indicada.
|
|
- Re-difundir el mensaje como `conversation_update` (o evento equivalente) a los demás clientes conectados.
|
|
|
|
---
|
|
|
|
## 4. Autenticación e Identidad del Asesor
|
|
|
|
### Regla de Negocio
|
|
> **La identidad del asesor (`advisorId`) NUNCA es enviada por el frontend.**
|
|
> El backend es el único responsable de derivarla desde el contexto autenticado de la conexión.
|
|
|
|
### Implementación esperada en el backend
|
|
|
|
| Canal | Mecanismo de autenticación | Cómo derivar `advisorId` |
|
|
|-------|---------------------------|-------------------------|
|
|
| **REST** | Bearer token en header `Authorization: Bearer <jwt>` o cookie de sesión | Extraer `advisorId` del payload del JWT o consultar la sesión |
|
|
| **WebSocket** | Token enviado como query param al conectar: `ws://host/ws/dashboard?token=<jwt>` | Validar JWT durante el handshake; almacenar `advisorId` en el contexto de la conexión |
|
|
|
|
### Endpoints REST que requieren autenticación
|
|
- `GET /api/v1/cases` — cualquier asesor autenticado
|
|
- `GET /api/v1/cases/:id` — cualquier asesor autenticado
|
|
- `POST /api/v1/cases/:id/resolve` — **requiere autenticación**; el backend registra qué asesor resolvió el caso
|
|
- `GET /api/v1/conversations/active` — cualquier asesor autenticado
|
|
|
|
---
|
|
|
|
## 5. Taxonomía de Casos y Patrones de UI
|
|
|
|
El frontend clasifica los casos en **6 patrones de UI** que determinan qué formulario se renderiza al asesor:
|
|
|
|
| `uiPattern` | Descripción | Ejemplo de respuesta esperada |
|
|
|-------------|-------------|------------------------------|
|
|
| `SIMPLE_CONFIRMATION` | Confirmación binaria Sí/No | `{ "confirmacion": true }` |
|
|
| `CONFIRMATION_WITH_VALUE` | Confirmación + valor monetario | `{ "confirmacion": true, "valor": 15000 }` |
|
|
| `MULTI_FIELD_FORM` | Formulario con múltiples campos | `{ "numero_cuotas": 12, "valor_cuota": 85000, "dia_corte": 15, "dia_limite_pago": 25 }` |
|
|
| `DATE_SIMPLE` | Fecha única | `{ "fecha": "15-07-2026" }` |
|
|
| `FREE_TEXT` | Texto libre | `{ "respuesta": "Cargo corresponde a roaming internacional" }` |
|
|
| `READ_ONLY` | Solo informativo (sin campos) | `{}` |
|
|
|
|
### Lista completa de tipos de caso (`tipoSolicitud`)
|
|
|
|
Los 53 `tipoSolicitud` válidos están definidos en el CSV `Consulta de aplicativos - Claro - Facturación.csv` y mapeados en `src/data/caseTypeDefinitions.ts`. El backend **DEBE** enviar un `tipoSolicitud` que coincida exactamente con uno de estos `toolName`:
|
|
|
|
**AC+ (13 casos)**: `Validar_Proporcionales_Movil`, `Tickler_AC+_CreerEnElCliente`, `Validar_Suspensiones_Movil`, `Validar_Identidad_Movil`, `Validar_Moras_Movil`, `Validar_Fecha_Corte_Movil`, `Validar_Fecha_Limite_Movil`, `Activar_Roaming`, `Desactivar_Roaming`, `Validar_Finalizacion_Campaña_Movil`, `Validar_Cambio_Plan_Movil`, `Consulta_Ultima_Factura_Movil`
|
|
|
|
**ASCARD (9 casos)**: `Plan_De_Pagos_EF`, `Tasa_De_Interes_EF`, `Pago_Minimo_EF`, `Plan_Total_EF`, `Refinanciacion_EF`, `Paz_Salvo_EF`, `Unificar_Factura_EF`, `Desbloqueo_EF`, `IMEI_EF`
|
|
|
|
**DiMe (8 casos)**: `Validar_Creer_Cliente`, `Activa_Creer_Cliente`, `Activa_Creer_Cliente_Hogar`
|
|
|
|
**Formatos SGCS (2 casos)**: `Cambio_Ciclos_Movil`
|
|
|
|
**Mi asistencia 360 (2 casos)**: `Escalar_Pagos_No_Abonados`
|
|
|
|
**Paradigma (2 casos)**: `Validar_Aumento_Tarifario`
|
|
|
|
**RR (12 casos)**: `Validar_Seguros_Hogar`, `Validar_Aumento_Tarifario_Hogar`, `Validar_Campaña_Hogar`, `Cambio_Plan_Hogar`, `Validar_Clausula_Hogar`, `Cobros_Adicionales_Hogar`, `Validar_Identidad_Hogar`, `Validar_Moras_Hogar`, `Validar_Fecha_Corte_Hogar`, `Validar_Fecha_Limite_Hogar`, `Validar_Proporcionales_Hogar`, `Validar_Suspensiones_Hogar`, `Validar_Venta_Tecnología`, `Validar_OTT_1`, `Validar_OTT_2`
|
|
|
|
> **Nota**: Algunos `toolName` se repiten con diferente `specialist`/`objective`. Para la UI, solo importa el `toolName`. El mapeo completo (con `uiPattern`, `formFields`, y `validationSchema` Zod) está en `src/data/caseTypeDefinitions.ts`.
|
|
|
|
### Campos del `payload` de resolución (por `uiPattern`)
|
|
|
|
Cuando el frontend envía `POST /cases/:id/resolve`, el `payload` tiene esta estructura según el `uiPattern`:
|
|
|
|
| `uiPattern` | Estructura del `payload` |
|
|
|-------------|-------------------------|
|
|
| `SIMPLE_CONFIRMATION` | `{ "confirmacion": boolean }` |
|
|
| `CONFIRMATION_WITH_VALUE` | `{ "confirmacion": boolean, "valor": number }` |
|
|
| `MULTI_FIELD_FORM` | Estructura variable según el `tipoSolicitud` (ver `caseTypeDefinitions.ts` para cada caso) |
|
|
| `DATE_SIMPLE` | `{ "fecha": "dd-mm-aaaa" }` |
|
|
| `FREE_TEXT` | `{ "respuesta": string }` |
|
|
| `READ_ONLY` | `{}` |
|
|
|
|
---
|
|
|
|
## 6. Notas Técnicas para el Backend
|
|
|
|
### 6.1 Timestamps
|
|
- Todos los timestamps deben estar en **ISO-8601 UTC** (ej. `"2026-07-23T15:00:00.000Z"`).
|
|
- El campo `occurredAt` del envelope WebSocket usa el mismo formato.
|
|
|
|
### 6.2 Manejo de `handlingTime`
|
|
- El backend es la **fuente de verdad** para `handlingTime`.
|
|
- Cuando el asesor comienza a gestionar un caso, el backend registra `startedAt`.
|
|
- Al recibir `POST /cases/:id/resolve`, el backend calcula `handlingTime = resolvedAt - startedAt` (en segundos).
|
|
- El frontend muestra un cronómetro en UI como referencia visual, pero no es autoritativo.
|
|
|
|
### 6.3 Broadcast de `hitl_resolved`
|
|
- Al resolver un caso vía REST, el backend **DEBE** emitir `hitl_resolved` por WebSocket a **todos** los clientes conectados (no solo al que resolvió).
|
|
- Esto permite que otros asesores vean que el caso ya fue atendido.
|
|
|
|
### 6.4 Persistencia
|
|
- El backend debe persistir todos los casos y conversaciones en base de datos.
|
|
- El `payload` de los casos se almacena como JSON.
|
|
- Las notas internas (`internal_note`) se persisten como mensajes en la conversación con `role: "internal"`.
|
|
|
|
### 6.5 MSW (Desarrollo Frontend Independiente)
|
|
- El frontend incluye una capa MSW que simula todos los endpoints REST y datos mock.
|
|
- El backend puede desarrollarse en paralelo sin depender del frontend, ya que los contratos están completamente especificados aquí.
|
|
- Variable de entorno del frontend: `VITE_ENABLE_MSW=true` activa los mocks; `false` o ausente usa el backend real.
|
|
|
|
---
|
|
|
|
## 7. Checklist de Implementación para Backend
|
|
|
|
- [ ] Endpoint `GET /api/v1/cases` con filtros `status`, `applicative`, `search`, `offset`, `limit`
|
|
- [ ] Endpoint `GET /api/v1/cases/:id`
|
|
- [ ] Endpoint `POST /api/v1/cases/:id/resolve` (canal autoritativo)
|
|
- [ ] Endpoint `GET /api/v1/conversations/active`
|
|
- [ ] Endpoint `GET /api/v1/conversations/:id`
|
|
- [ ] Servidor WebSocket en `/ws/dashboard`
|
|
- [ ] Envelope JSON estándar `{ type, eventId, occurredAt, payload }`
|
|
- [ ] Evento `init_state` al conectar/reconectar
|
|
- [ ] Eventos de streaming: `agent_stream_started`, `agent_stream_chunk` (con `index`), `agent_stream_completed`
|
|
- [ ] Evento `hitl_request` al requerir intervención humana
|
|
- [ ] Evento `hitl_resolved` en broadcast tras resolución REST
|
|
- [ ] Evento `internal_note` recibido del cliente → persistir como mensaje `role: internal` → re-difundir
|
|
- [ ] Eventos `conversation_started`, `conversation_ended`, `user_message`, `agent_status_update`
|
|
- [ ] Autenticación REST vía Bearer token / cookie de sesión
|
|
- [ ] Autenticación WebSocket vía query param `?token=<jwt>`
|
|
- [ ] Derivar `advisorId` del contexto autenticado (NUNCA del payload del cliente)
|
|
- [ ] Calcular `handlingTime` como `resolvedAt - startedAt` (segundos)
|
|
- [ ] Timestamps en ISO-8601 UTC
|
|
- [ ] `tipoSolicitud` en casos coincide con los `toolName` del CSV
|
|
- [ ] `uiPattern` en casos coincide con uno de los 6 valores del enum
|
|
|
|
---
|
|
|
|
## 8. Referencia Rápida de Archivos del Frontend
|
|
|
|
| Archivo | Contenido relevante para backend |
|
|
|---------|--------------------------------|
|
|
| `src/types/index.ts` | Interfaces `CaseRequest`, `Conversation`, `Message` |
|
|
| `src/types/wsProtocol.ts` | Schemas Zod de todos los eventos WS + envelope |
|
|
| `src/data/caseTypeDefinitions.ts` | 53 tipos de caso con `uiPattern`, `formFields`, `validationSchema` |
|
|
| `src/services/api.ts` | Cliente REST (endpoints y formatos esperados) |
|
|
| `src/services/wsClient.ts` | Cliente WebSocket (reconexión, envelope) |
|
|
| `src/mocks/handlers.ts` | Datos mock de ejemplo (10 casos, 3 conversaciones) |
|
|
| `SPECIFICATION.md` | Especificación completa del proyecto |
|