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>
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.goya exponeReadLogs()yReadDayLogs()para consultar logs JSONL por agente y fecha — reutilizable para las API de metricas.internal/config/schema.godefineAgentConfigcon todas las secciones; falta una seccionWebCfgpara 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 deshell/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: agregarWebCfgal 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 (reutilizashell/logger/query.go)GET /api/agents/{id}/logs— SSE stream de logs en tiempo realGET /dashboard— SPA del dashboard (HTML/JS/CSS embebido viaembed.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 (
EventSourceen 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
WebCfgainternal/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"aAgentConfig(o a un nuevoLauncherConfigsi se decide no atar a cada agente). -
1.2 Crear
shell/web/server.go:- Struct
Serverconhttp.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.ServeMuxde la stdlib (Go 1.22+ soporta{id}patterns). - Middleware basico: logging, CORS (necesario para iframe de widgets), auth opcional.
- Struct
-
1.3 Crear
shell/web/handlers.go— handlerGET /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.
- Devuelve JSON array con:
-
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.
- Reutilizar
-
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.yamlo reutilizar env vars). - Si
web.enabled, crearweb.Servery arrancarlo en el mismoWaitGroup. - Pasar la lista de agentes al servidor para que los pueda consultar.
- Leer config web (puede ser una seccion nueva en un
-
1.8 Tests: handlers con
httptest:- Test de
/api/agentscon lista de agentes mock. - Test de
/api/agents/{id}con agente existente y no existente. - Test de
/api/agents/{id}/metricscon logs JSONL de ejemplo en tmpdir. - Test del middleware de auth (token valido, invalido, deshabilitado).
- Test de
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.
- Campos requeridos:
-
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.
- Funcion
-
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.goodevagents/handler.go, despues de join a room, siwidgets.enabledybase_urlconfigurado, llamar aRegisterWidget. - Manejar error gracefully (log warning, no romper el agente).
- En
-
2.5 Tests:
- Test del formato del state event generado (campos requeridos presentes).
- Test de
RegisterWidgetcon 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/agentsy renderizar lista de agentes con indicadores de estado. - Colores por status: verde (running), rojo (error), gris (stopped).
- Click en agente → navega a vista detalle.
- Fetch
-
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.
- Fetch
-
3.4 Log viewer en vivo:
- Conectar a
/api/agents/{id}/logsviaEventSource. - Mostrar logs en panel scrollable con auto-scroll.
- Colores por nivel: DEBUG (gris), INFO (blanco), WARN (amarillo), ERROR (rojo).
- Conectar a
-
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.htmlpara SPA routing.
- Servir con
-
3.7 Tests del dashboard:
- Test de que
embed.FScontiene los archivos esperados. - Test de que
/dashboardsirve HTML valido. - Test de que las rutas SPA redirigen a
index.html.
- Test de que
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.mdsobre 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
- Habilitar en config:
web.enabled: true,web.port: 8080 - Arrancar launcher:
./dev-scripts/server/start.sh - Navegar a
http://localhost:8080/dashboard - Ver lista de agentes con estado, click en uno para ver metricas y logs en vivo
Widget en Element
- Configurar adicionalmente:
matrix: widgets: enabled: true base_url: "https://bots.example.com" auto_register: true - Agente se une a un room → auto-registra widget
- En Element Web aparece un panel con el dashboard filtrado para ese agente
- 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
-
net/httpsin frameworks: consistente con el estilo del proyecto (stdlib, sin dependencias externas para HTTP). Go 1.22+ tiene routing con path params nativo enhttp.ServeMux. -
embed.FSpara 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. -
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.
-
Config
WebCfga 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. -
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. -
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.
-
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.ServeMuxcon 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. |