Files
agents_and_robots/dev/issues/0038-element-widgets-dashboard.md
T
egutierrez 52d5632d89 docs: crear issues 0036-0041 — nuevas features del sistema
Issues planificados:
- 0036: Claude Code streaming de progreso en Matrix
- 0037: Agente que crea otros agentes/bots via Matrix
- 0038: Webapps y dashboards embebidos en Element via widgets
- 0039: Recordatorios dinámicos y crons que invocan agentes
- 0040: Soporte para mensajes de voz (audio → STT)
- 0041: Videollamadas con agentes via LiveKit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 21:19:09 +00:00

15 KiB

0038 — Webapps y dashboards embebidos en Element via widgets

Estado: pendiente

Objetivo

Incorporar un servidor HTTP embebido en el launcher que sirva dashboards y mini-apps de los agentes, integrables en rooms de Element como Matrix widgets. Los usuarios podran ver estado en tiempo real, metricas e interfaces interactivas de sus agentes directamente desde sus rooms Matrix, sin salir del cliente.

Contexto

  • El launcher ya arranca multiples agentes en paralelo (cmd/launcher/main.go) y tiene un logger centralizado con JSONL rotado por dia.
  • shell/logger/query.go ya expone ReadLogs() y ReadDayLogs() para consultar logs JSONL por agente y fecha — reutilizable para las API de metricas.
  • internal/config/schema.go define AgentConfig con todas las secciones; falta una seccion WebCfg para el servidor HTTP.
  • Matrix soporta widgets via state events im.vector.modular.widgets (Element Web y Desktop). El agente puede enviar estos state events usando mautrix-go.
  • Actualmente no existe shell/web/ ni ningun endpoint HTTP en el proyecto.
  • El issue 0035 (audit trail + !metrics) agrega metricas del dia actual; este issue va mas alla con visualizacion web persistente y en tiempo real.

Arquitectura

Pure core / impure shell

  • pkg/ — no se modifica. No hay logica pura nueva; la transformacion de datos de logs a metricas se puede hacer con funciones helper dentro de shell/web/handlers.go (son inherentemente I/O-bound: leen archivos).
  • shell/web/ — NEW, 100% impuro: servidor HTTP, handlers API, SSE streaming, widget registration via Matrix.
  • internal/config/schema.go — MOD: agregar WebCfg al schema de configuracion.
  • cmd/launcher/main.go — MOD: arrancar servidor web junto con los agentes.

Fase 1 — Servidor HTTP embebido

shell/web/                  NEW — package del servidor web
shell/web/server.go         NEW — setup del servidor HTTP + routes
shell/web/handlers.go       NEW — handlers de los endpoints API
shell/web/static/           NEW — archivos estaticos del dashboard (embed.FS)

Endpoints:

  • GET /api/agents — lista de agentes en ejecucion con estado (running/stopped/error)
  • GET /api/agents/{id} — detalle del agente (config filtrada, uptime, ultima actividad)
  • GET /api/agents/{id}/metrics — metricas agregadas del dia (reutiliza shell/logger/query.go)
  • GET /api/agents/{id}/logs — SSE stream de logs en tiempo real
  • GET /dashboard — SPA del dashboard (HTML/JS/CSS embebido via embed.FS)
  • GET /dashboard/{id} — vista filtrada por agente (util para widgets)

Fase 2 — Integracion Matrix widget

shell/web/widget.go         NEW — helper para registrar widgets en rooms Matrix
  • Cuando un agente se une a un room, opcionalmente registra un widget via state event im.vector.modular.widgets.
  • Widget URL apunta al dashboard embebido filtrado para ese agente: {base_url}/dashboard/{agent-id}?room={room_id}.
  • Usa mautrix-go client.SendStateEvent() para enviar el state event.
  • Config: matrix.widgets.enabled, matrix.widgets.base_url, matrix.widgets.auto_register.

Fase 3 — Dashboard UI

shell/web/static/index.html     NEW — SPA entry point
shell/web/static/app.js         NEW — logica JS del dashboard
shell/web/static/style.css      NEW — estilos
  • SPA con vanilla JS (o Preact si crece), embebido en el binario Go via embed.FS.
  • Vistas:
    • Lista de agentes: estado (running/stopped/error), tipo (agent/robot), uptime.
    • Detalle de agente: resumen de config, mensajes recientes, uso de tools.
    • Log viewer en vivo: via SSE (EventSource en JS), muestra logs en tiempo real.
    • Graficas de metricas: mensajes/hora, tool calls, errores, latencia LLM.

Archivos afectados

Archivo Cambio Descripcion
shell/web/ NEW Package completo del servidor web
shell/web/server.go NEW Setup HTTP server, router, middleware
shell/web/handlers.go NEW Handlers API: agents, metrics, logs SSE
shell/web/widget.go NEW Helper para registrar widgets Matrix en rooms
shell/web/static/ NEW Dashboard SPA (HTML/JS/CSS embebido)
internal/config/schema.go MOD Agregar WebCfg con Enabled, Port, Host, BasePath, Auth
cmd/launcher/main.go MOD Arrancar servidor web junto con los agentes
shell/matrix/client.go MOD Agregar metodo para enviar state events de widget (si no existe)

Tareas

Fase 1 — Servidor HTTP embebido

  • 1.1 Agregar WebCfg a internal/config/schema.go:

    type WebCfg struct {
        Enabled  bool   `yaml:"enabled"`   // habilitar servidor web (default false)
        Host     string `yaml:"host"`      // bind address (default "127.0.0.1")
        Port     int    `yaml:"port"`      // puerto HTTP (default 8080)
        BasePath string `yaml:"base_path"` // prefijo de rutas (default "/")
        Auth     WebAuthCfg `yaml:"auth"`  // autenticacion
    }
    type WebAuthCfg struct {
        Enabled  bool   `yaml:"enabled"`   // requerir autenticacion
        TokenEnv string `yaml:"token_env"` // env var con el token de acceso
    }
    

    Agregar campo Web WebCfg yaml:"web" a AgentConfig (o a un nuevo LauncherConfig si se decide no atar a cada agente).

  • 1.2 Crear shell/web/server.go:

    • Struct Server con http.Server, referencia a la lista de agentes en ejecucion, config, logger.
    • Constructor New(cfg WebCfg, agents []AgentInfo, logDir string, logger *slog.Logger) *Server.
    • Metodo Start(ctx context.Context) error — arranca el servidor HTTP en goroutine, se detiene con ctx.
    • Router usando http.ServeMux de la stdlib (Go 1.22+ soporta {id} patterns).
    • Middleware basico: logging, CORS (necesario para iframe de widgets), auth opcional.
  • 1.3 Crear shell/web/handlers.go — handler GET /api/agents:

    • Devuelve JSON array con: id, name, type, status, uptime, description.
    • La info de agentes se obtiene de un registry que el launcher puebla al arrancar.
  • 1.4 Handler GET /api/agents/{id}:

    • Config del agente (filtrada: sin tokens, passwords, API keys).
    • Uptime, ultima actividad, cantidad de mensajes procesados.
    • Error si el {id} no existe.
  • 1.5 Handler GET /api/agents/{id}/metrics:

    • Reutilizar shell/logger/ReadDayLogs() para obtener logs del dia actual.
    • Calcular: mensajes recibidos, comandos ejecutados, llamadas LLM (count + tokens + latencia media), tool calls (count + errores), errores totales.
    • Devuelve JSON con los agregados.
  • 1.6 Handler GET /api/agents/{id}/logs (SSE):

    • Server-Sent Events stream con los ultimos N logs y nuevos logs en tiempo real.
    • Content-Type: text/event-stream.
    • Tail del archivo JSONL actual con polling o fsnotify.
  • 1.7 Integrar arranque del servidor en cmd/launcher/main.go:

    • Leer config web (puede ser una seccion nueva en un launcher.yaml o reutilizar env vars).
    • Si web.enabled, crear web.Server y arrancarlo en el mismo WaitGroup.
    • Pasar la lista de agentes al servidor para que los pueda consultar.
  • 1.8 Tests: handlers con httptest:

    • Test de /api/agents con lista de agentes mock.
    • Test de /api/agents/{id} con agente existente y no existente.
    • Test de /api/agents/{id}/metrics con logs JSONL de ejemplo en tmpdir.
    • Test del middleware de auth (token valido, invalido, deshabilitado).

Fase 2 — Integracion Matrix widget

  • 2.1 Investigar formato del state event im.vector.modular.widgets:

    • Campos requeridos: type, url, name, id, creatorUserId.
    • Verificar compatibilidad con Element Web 1.x actual.
  • 2.2 Crear shell/web/widget.go:

    • Funcion RegisterWidget(ctx context.Context, client *mautrix.Client, roomID, widgetID, widgetName, baseURL, agentID string) error.
    • Construye el state event content con la URL del dashboard filtrado.
    • Envia via client.SendStateEvent(roomID, "im.vector.modular.widgets", widgetID, content).
    • Funcion UnregisterWidget(...) para limpiar al salir.
  • 2.3 Agregar seccion matrix.widgets.* al config:

    matrix:
      widgets:
        enabled: false         # habilitar registro automatico de widgets
        base_url: ""           # URL publica del servidor web (requerido si enabled)
        auto_register: true    # registrar widget al unirse a room
        widget_name: "Dashboard" # nombre visible del widget
    
  • 2.4 Integrar auto-registro en el runtime:

    • En devagents/runtime.go o devagents/handler.go, despues de join a room, si widgets.enabled y base_url configurado, llamar a RegisterWidget.
    • Manejar error gracefully (log warning, no romper el agente).
  • 2.5 Tests:

    • Test del formato del state event generado (campos requeridos presentes).
    • Test de RegisterWidget con mock de mautrix client.
    • Test de la URL generada (incluye agent ID y room ID como query params).

Fase 3 — Dashboard UI

  • 3.1 Crear shell/web/static/index.html:

    • HTML minimo con viewport meta, link a CSS, script tag.
    • Routing basico client-side (hash-based: #/, #/agent/{id}).
  • 3.2 Crear shell/web/static/app.js:

    • Fetch /api/agents y renderizar lista de agentes con indicadores de estado.
    • Colores por status: verde (running), rojo (error), gris (stopped).
    • Click en agente → navega a vista detalle.
  • 3.3 Vista detalle de agente:

    • Fetch /api/agents/{id} y /api/agents/{id}/metrics.
    • Mostrar: nombre, tipo, uptime, descripcion, metricas del dia en tabla.
    • Seccion de metricas con numeros grandes y colores.
  • 3.4 Log viewer en vivo:

    • Conectar a /api/agents/{id}/logs via EventSource.
    • Mostrar logs en panel scrollable con auto-scroll.
    • Colores por nivel: DEBUG (gris), INFO (blanco), WARN (amarillo), ERROR (rojo).
  • 3.5 Graficas de metricas (simple):

    • Canvas o SVG basico (sin librerias externas) para mensajes/hora y tool calls.
    • Alternativa: ASCII-art charts si se quiere mantener minimalismo extremo.
  • 3.6 Embed estaticos en Go:

    //go:embed static/*
    var staticFS embed.FS
    
    • Servir con http.FileServer(http.FS(staticFS)) en el router.
    • Fallback a index.html para SPA routing.
  • 3.7 Tests del dashboard:

    • Test de que embed.FS contiene los archivos esperados.
    • Test de que /dashboard sirve HTML valido.
    • Test de que las rutas SPA redirigen a index.html.

Fase 4 — Tests de integracion y cleanup

  • 4.1 Test de integracion end-to-end: arrancar servidor, verificar que todos los endpoints responden correctamente con agentes mock.
  • 4.2 Documentar configuracion en el config.yaml template de agents/_template/.
  • 4.3 Agregar seccion en CLAUDE.md sobre el servidor web y widgets.

Ejemplo de uso

Configuracion basica

# En la config del launcher o en un agent config
web:
  enabled: true
  host: "0.0.0.0"
  port: 8080
  auth:
    enabled: true
    token_env: "WEB_DASHBOARD_TOKEN"

Dashboard standalone

  1. Habilitar en config: web.enabled: true, web.port: 8080
  2. Arrancar launcher: ./dev-scripts/server/start.sh
  3. Navegar a http://localhost:8080/dashboard
  4. Ver lista de agentes con estado, click en uno para ver metricas y logs en vivo

Widget en Element

  1. Configurar adicionalmente:
    matrix:
      widgets:
        enabled: true
        base_url: "https://bots.example.com"
        auto_register: true
    
  2. Agente se une a un room → auto-registra widget
  3. En Element Web aparece un panel con el dashboard filtrado para ese agente
  4. El usuario ve metricas y logs sin salir del room

Acceso directo a API

# Lista de agentes
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/agents

# Metricas de un agente
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/agents/asistente-2/metrics

# Stream de logs en vivo
curl -H "Authorization: Bearer $TOKEN" -N http://localhost:8080/api/agents/asistente-2/logs

Decisiones de diseno

  1. net/http sin frameworks: consistente con el estilo del proyecto (stdlib, sin dependencias externas para HTTP). Go 1.22+ tiene routing con path params nativo en http.ServeMux.

  2. embed.FS para estaticos: deployment de un solo binario. No se necesitan archivos externos ni pasos de build frontend separados. El dashboard es lo suficientemente simple para vanilla JS.

  3. SSE en vez de WebSocket para logs en vivo: SSE es mas simple, funciona a traves de proxies HTTP, reconexion automatica en el browser, y es suficiente para un flujo unidireccional (servidor → cliente). WebSocket seria overkill para este caso.

  4. Config WebCfg a nivel launcher, no por agente: el servidor web es uno solo para todos los agentes (lo sirve el launcher). Evita N puertos por N agentes. La info por agente se filtra en los endpoints.

  5. Widget registration opcional: el dashboard funciona standalone sin Matrix widgets. Los widgets son un bonus para integracion en Element. Si el usuario no configura widgets.base_url, simplemente no se registran widgets.

  6. Auth por token simple: para la primera iteracion, un bearer token en env var es suficiente. Integracion con Matrix OIDC o session cookies se puede agregar despues si es necesario.

  7. Filtrar secrets del API: el endpoint /api/agents/{id} nunca expone tokens, API keys, passwords ni recovery keys. Se filtran los campos *_env, access_token_env, etc. antes de serializar.

Prerequisitos

  • Issue 0035 (audit trail + !metrics): no es bloqueante pero si esta implementado, el endpoint de metricas puede reutilizar la logica de agregacion. Sin el, se implementa directamente leyendo JSONL.
  • shell/logger/query.go — ya existe y funciona.
  • Go 1.22+ — necesario para http.ServeMux con path params (el proyecto usa Go 1.23.5, OK).

Riesgos

Riesgo Mitigacion
Element widget support varia entre clientes (Web vs mobile vs Desktop) Testear con Element Web primero (el cliente principal del proyecto). Mobile puede no soportar widgets custom. Documentar limitaciones.
CORS necesario para iframe de widgets Agregar headers CORS configurables en el middleware del servidor. Restringir origenes al homeserver.
HTTPS obligatorio para widgets en produccion Element requiere HTTPS para widgets. Documentar que en produccion se necesita reverse proxy (nginx/caddy) con TLS. En desarrollo localhost funciona sin HTTPS.
Dashboard crece en complejidad → SPA inmanejable con vanilla JS Empezar simple. Si crece, migrar a Preact (~3KB) que se puede embeber sin build system. No usar React/Vue/frameworks pesados.
Servidor web expuesto → superficie de ataque Auth por defecto deshabilitada → solo escucha en 127.0.0.1. En produccion, auth habilitada + HTTPS + reverse proxy. Nunca exponer secretos en la API.
SSE streaming consume memoria si hay muchos clientes Limitar a N conexiones SSE simultaneas (configurable). Desconectar clientes idle. Buffer limitado de logs en memoria.
embed.FS aumenta tamano del binario Los archivos estaticos son HTML/JS/CSS minimo (estimado <100KB). Impacto negligible vs las dependencias Go existentes.