**WLMP (WillyLabs Messaging Protocol)** es el protocolo de aplicación bidireccional y asíncrono diseñado para orquestar la comunicación entre el frontend web y **Dobbi**, el asistente de IA de la suite WillyLabs.

Inspirado en la elegancia del *Jupyter Messaging Protocol (JMP)* y operando sobre **WebSockets**, WLMP desacopla la generación de contenido del renderizado visual, permitiendo a la IA dictar flujos de texto en tiempo real (streaming), alterar dinámicamente la disposición de la interfaz (Server-Driven UI), emitir estados de ejecución y solicitar datos estructurados al usuario.

---

## 1. Arquitectura Base y Transporte

WLMP opera exclusivamente sobre **WebSockets** (wss://) para garantizar latencia ultrabaja y comunicación *full-duplex* (bidireccional simultánea).

Todo mensaje transmitido en WLMP es un objeto **JSON estandarizado** (el "Sobre" o *Envelope*). El sistema se basa en un paradigma de **Petición-Respuesta Asíncrona**, donde una solicitud del cliente puede desencadenar una cascada de múltiples mensajes de respuesta por parte de Dobbi a lo largo del tiempo.

### 1.1. Estructura del Sobre (Envelope)

Cada payload enviado por el socket debe cumplir estrictamente con esta estructura JSON:

```json
{
  "header": {
    "msg_id": "string (UUIDv4)",
    "session": "string (ID único de la sesión WebSocket)",
    "msg_type": "string (Tipo de mensaje. Ej: display_data, stream)",
    "date": "string (ISO 8601 UTC)",
    "version": "1.0"
  },
  "parent_header": {
    "msg_id": "string (UUIDv4 del mensaje que originó esta respuesta. Null si es inicio de hilo)"
  },
  "metadata": {
    "auth_mode": "string (anonymous | client)",
    "user_id": "string | null",
    "tenant_id": "string | null (Ej: willy_labs)"
  },
  "content": {
    "display_target": "string (main_panel | side_panel | modal | floating_widget)"
  }
}

```

#### Descripción de los bloques:

* **`header`**: Identifica el mensaje y su intención (`msg_type`).
* **`parent_header`**: Mantiene la trazabilidad. Permite al cliente saber qué bloque de la UI debe actualizarse relacionando la respuesta con la petición original.
* **`metadata`**: Control de contexto. `auth_mode: "anonymous"` le indica a Dobbi que debe restringir herramientas (solo Q&A público). `auth_mode: "client"` activa herramientas avanzadas (RAG privado, consulta a WillyHUB, historiales).
* **`content`**: El cuerpo de la acción. El atributo `display_target` es crucial para indicar al frontend en qué zona de la pantalla debe impactar el contenido.

---

## 2. Tipos de Mensajes (`msg_type`)

El protocolo define un conjunto cerrado de tipos de mensajes para garantizar que el frontend actúe como un despachador (*dispatcher*) predecible.

### 2.1. Cliente → Servidor (Peticiones del Usuario)

| msg_type | Descripción |
| --- | --- |
| `execute_request` | El usuario envía un *prompt*, comando de voz transcrito o archivo adjunto. |
| `form_reply` | El usuario envía un payload estructurado JSON como respuesta a un *wizard* de Dobbi. |
| `interrupt_request` | Señal de aborto. El usuario cancela la generación o la tarea en curso. |

### 2.2. Servidor → Cliente (Respuestas de Dobbi)

| msg_type | Descripción |
| --- | --- |
| `stream` | Chunk de texto en tiempo real. Usado para el efecto máquina de escribir (texto/markdown). |
| `display_data` | Renderizado de componentes ricos. Contiene un diccionario de tipos MIME (Tablas, gráficos, Mermaid). |
| `ui_command` | Directivas de mutación de layout (abrir paneles, redimensionar columnas, animaciones). |
| `task_update` | Emisión del estado mental y ejecución de herramientas del agente (para barra de progreso o timeline). |
| `form_request` | Dobbi pausa su ejecución y envía un JSON Schema para que el cliente dibuje un formulario. |
| `execute_result` | Mensaje de cierre. Indica que Dobbi ha finalizado el ciclo de procesamiento para un `parent_header` dado. |
| `error` | Notificación formal de fallo en la ejecución o validación. |

---

## 3. Sistema de Visualización y Tipos MIME

En WLMP, Dobbi no envía código HTML (a menos que sea estrictamente necesario). Dobbi envía **datos** usando tipos MIME estándar y personalizados (`application/vnd.willy.*`). El frontend lee el diccionario dentro de `display_data` y selecciona la representación visual más rica que soporta.

**Estructura del `content` para `display_data`:**

```json
{"content": {
  "display_target": "side_panel",
  "data": {
    "text/markdown": "Aquí tienes los resultados tabulados:",
    "application/vnd.willy.table+json": {
      "columns": ["ID", "Métrica", "Valor"],
      "rows": [[1, "CPU", "45%"], [2, "RAM", "80%"]]
    },
    "application/vnd.willy.chart+json": {
      "type": "bar",
      "datasets": [{"label": "Uso", "data": [45, 80]}]
    }
  }
}}

```

### 3.1. Catálogo de Tipos MIME Soportados

* **`text/markdown`**: Texto enriquecido general.
* **`text/html`**: Solo usado para renderizados legacy o iframes estrictos.
* **`application/vnd.willy.mermaid+text`**: Código fuente para que el cliente renderice diagramas de flujo.
* **`application/vnd.willy.table+json`**: Datos tabulares nativos (ideal para inyectar en DataTables o Grid.js).
* **`application/vnd.willy.chart+json`**: Esquemas para gráficos (Chart.js, ECharts, D3).
* **`application/vnd.willy.metrics+json`**: Tarjetas tipo KPI (Ej: "Ventas Totales: $10,000").

---

## 4. Mutación de Layout (Server-Driven UI)

Para que Dobbi ordene alteraciones estructurales en el navegador, se utiliza el tipo `ui_command`. El frontend debe implementar un gestor de estado (ej. Redux, Zustand o señales de React/Vue) que reaccione a estas órdenes.

```json
{
  "header": { "msg_type": "ui_command"},
  "content": {
    "action": "layout_mutation",
    "params": {
      "command": "open_sidebar",
      "config": {
        "width": "50%",
        "overlay": false,
        "transition": "slide_in_right"
      }
    }
  }
}

```

**Acciones Soportadas (`action`):**

* `layout_mutation`: Mover, abrir o cerrar contenedores principales (`main_panel`, `side_panel`).
* `modal_trigger`: Levantar una ventana modal emergente.
* `scroll_to`: Forzar el scroll del cliente hacia un ID específico o final del hilo.
* `clear_target`: Limpiar el contenido de un `display_target` específico.

---

## 5. Máquina de Estados del Agente (Lifecycle)

Dobbi procesa tareas complejas mediante agentes. Para evitar que el usuario se impaciente, Dobbi emite señales continuas de su estado interno a través de `task_update`.
Estados posibles: pending | running | success | error

```json
{
  "header": { "msg_type": "task_update"},
  "content": {
    "task_id": "step_001_db",
    "description": "Consultando base de datos WillyHUB Data360...",
    "state": "running",
    "progress": 30
  }
}

```

*El cliente acumula estos eventos para construir un componente tipo "Stepper" o "Consola de Logs de IA" que muestra el pensamiento de Dobbi en tiempo real.*

---

## 6. Formularios Interactivos (Wizards)

Cuando Dobbi requiere datos que no puede inferir, delega la creación de UI al frontend mediante `form_request`.

**1. Petición del Servidor (`form_request`):**

```json
{
  "header": { "msg_type": "form_request", "msg_id": "req_88"},
  "content": {
    "display_target": "main_panel",
    "form_id": "filter_date",
    "title": "Por favor, especifica el rango de fechas",
    "schema": {
      "type": "object",
      "properties": {
        "start_date": { "type": "string", "format": "date" },
        "end_date": { "type": "string", "format": "date" }
      },
      "required": ["start_date", "end_date"]
    }
  }
}

```

**2. Respuesta del Cliente (`form_reply`):**
El cliente renderiza los inputs, valida y responde asociando el `parent_header`.

```json
{
  "header": { "msg_type": "form_reply", "msg_id": "rep_99" },
  "parent_header": { "msg_id": "req_88" },
  "content": {
    "form_id": "filter_date",
    "data": {
      "start_date": "2026-01-01",
      "end_date": "2026-06-15"
    }
  }
}

```

---

## 7. Implementación de Referencia (Backend Python)

Para facilitar la adopción en la arquitectura backend de WillyLabs (típicamente FastAPI/WebSockets), se expone esta estructura fundacional en Python orientada a objetos.

```python
import json
import uuid
import datetime
from typing import Dict, Any, Optional

class WLMPMessage:
    """Clase constructora para los sobres (envelopes) del protocolo WLMP."""
    
    def __init__(
        self, 
        msg_type: str, 
        content: Dict[str, Any], 
        session_id: str, 
        auth_mode: str = "anonymous",
        user_id: Optional[str] = None,
        parent_id: Optional[str] = None
    ):
        self.header = {
            "msg_id": str(uuid.uuid4()),
            "session": session_id,
            "msg_type": msg_type,
            "date": datetime.datetime.now(datetime.timezone.utc).isoformat(),
            "version": "1.0"
        }
        self.parent_header = {"msg_id": parent_id} if parent_id else {}
        self.metadata = {
            "auth_mode": auth_mode,
            "user_id": user_id,
            "tenant_id": "willy_labs"
        }
        self.content = content

    def to_dict(self) -> Dict[str, Any]:
        return {
            "header": self.header,
            "parent_header": self.parent_header,
            "metadata": self.metadata,
            "content": self.content
        }
        
    def to_json(self) -> str:
        return json.dumps(self.to_dict())


class DobbiOrchestrator:
    """Ejemplo de orquestador para gestionar la comunicación WebSocket."""
    
    def __init__(self, websocket, session_id: str, user_id: str):
        self.ws = websocket
        self.session_id = session_id
        self.user_id = user_id
        self.auth_mode = "client" if user_id else "anonymous"

    async def emit_task_update(self, parent_id: str, task_id: str, description: str, state: str):
        content = {
            "task_id": task_id,
            "description": description,
            "state": state
        }
        msg = WLMPMessage(
            msg_type="task_update",
            content=content,
            session_id=self.session_id,
            auth_mode=self.auth_mode,
            user_id=self.user_id,
            parent_id=parent_id
        )
        await self.ws.send_text(msg.to_json())

    async def emit_complex_ui(self, parent_id: str):
        """Flujo de ejemplo documentado: Markdown -> Abrir Panel -> Tabla"""
        
        # 1. Enviar texto a panel principal
        await self.ws.send_text(WLMPMessage(
            msg_type="display_data",
            content={
                "display_target": "main_panel",
                "data": {"text/markdown": "Procesando su solicitud de reporte analítico..."}
            },
            session_id=self.session_id,
            auth_mode=self.auth_mode,
            parent_id=parent_id
        ).to_json())

        # 2. Comandar mutación de interfaz
        await self.ws.send_text(WLMPMessage(
            msg_type="ui_command",
            content={
                "action": "layout_mutation",
                "params": {"command": "open_sidebar", "config": {"width": "40%"}}
            },
            session_id=self.session_id,
            auth_mode=self.auth_mode,
            parent_id=parent_id
        ).to_json())

        # 3. Inyectar datos en el panel lateral
        await self.ws.send_text(WLMPMessage(
            msg_type="display_data",
            content={
                "display_target": "side_panel",
                "data": {
                    "application/vnd.willy.table+json": {
                        "columns": ["Servicio", "Estado"],
                        "rows": [["WillySynaps", "OK"], ["WillyHUB", "OK"]]
                    }
                }
            },
            session_id=self.session_id,
            auth_mode=self.auth_mode,
            parent_id=parent_id
        ).to_json())
        
        # 4. Finalizar petición
        await self.ws.send_text(WLMPMessage(
            msg_type="execute_result",
            content={"status": "completed"},
            session_id=self.session_id,
            auth_mode=self.auth_mode,
            parent_id=parent_id
        ).to_json())

```

Con esta documentación, la arquitectura queda blindada. El protocolo es lo suficientemente rígido para mantener el orden, pero lo bastante extensible mediante el diccionario `data` de representaciones MIME para absorber cualquier evolución futura del ecosistema sin romper versiones anteriores.