Files
agents_and_robots/dev/issues/completed/011-markdown-rendering.md
T
egutierrez f561f686c4 refactor: migrar tasks/ a dev/issues/ con estructura de desarrollo
Se mueve la documentación de issues/tasks de .claude/tasks/ a dev/issues/
para separar la planificación de desarrollo de la configuración de Claude.
Se añade dev/README.md como índice de la carpeta de desarrollo. Los issues
completados se mueven a dev/issues/completed/. Esto permite que dev/ sea
el punto central de documentación interna del proyecto.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 17:41:16 +00:00

3.8 KiB

Tarea 11 — Renderizar mensajes como Markdown en Matrix

Problema

Todos los mensajes de los agentes (respuestas LLM, comandos, errores) se envían como texto plano via SendText(). Matrix soporta mensajes con format: org.matrix.custom.html + formatted_body para renderizar Markdown (negrita, código, listas, etc.) en clientes como Element.

Existe un SendMarkdown() en shell/matrix/client.go pero tiene dos problemas:

  1. Solo se usa en un único lugar (runtime.go:617 — notificación de tool use).
  2. No convierte Markdown a HTML: pone el markdown crudo en FormattedBody, que Matrix espera como HTML.

Alcance

1. Añadir conversión Markdown → HTML (shell/matrix/client.go)

  • Añadir dependencia github.com/yuin/goldmark (parser Markdown → HTML estándar, muy usado en Go).
  • Corregir SendMarkdown() para que convierta el body de Markdown a HTML antes de ponerlo en FormattedBody.
  • Body queda como texto plano (fallback para clientes que no soportan HTML) — se puede dejar el markdown crudo ahí, que es lo estándar en Matrix.
func (c *Client) SendMarkdown(ctx context.Context, roomID, markdown string) error {
    html := mdToHTML(markdown) // nueva función interna
    content := event.MessageEventContent{
        MsgType:       event.MsgText,
        Body:          markdown,
        Format:        event.FormatHTML,
        FormattedBody: html,
    }
    _, err := c.raw.SendMessageEvent(ctx, id.RoomID(roomID), event.EventMessage, content)
    return err
}

2. Cambiar la interfaz MatrixSender para exponer SendMarkdown

  • shell/effects/runner.go: añadir SendMarkdown(ctx, roomID, text) error a la interfaz MatrixSender.
  • tools/matrix.go: añadir SendMarkdown a la interfaz MatrixToolSender (o como se llame).

3. Cambiar todos los call sites de SendTextSendMarkdown

Puntos a cambiar:

Archivo Línea(s) Contexto
agents/runtime.go:394 Respuesta de tarea orquestada SendText → SendMarkdown
agents/runtime.go:456 Reply LLM (loop) SendText → SendMarkdown
agents/runtime.go:462 Reply LLM (fallback) SendText → SendMarkdown
shell/effects/runner.go:68 Runner.executeOne (ActionKindReply) SendText → SendMarkdown
agents/runtime.go:456 Comando ejecutado (!xxx) SendText → SendMarkdown
agents/runtime.go:462 Comando desconocido SendText → SendMarkdown

4. Mantener SendText para uso interno/futuro

No eliminar SendText, solo dejar de usarlo como canal principal de respuesta. Podría ser útil para mensajes que realmente no necesitan formato (logs internos, debugging).

5. Actualizar interfaz en tests/mocks

Cualquier mock de MatrixSender que exista en tests necesitará el método SendMarkdown.

Tareas ordenadas

  • go get github.com/yuin/goldmark
  • Crear función mdToHTML(md string) string en shell/matrix/ (usa goldmark)
  • Corregir SendMarkdown() para usar mdToHTML
  • Añadir SendMarkdown a la interfaz MatrixSender en shell/effects/runner.go
  • Cambiar runner.executeOne (ActionKindReply) de SendTextSendMarkdown
  • Cambiar runtime.go — respuesta de comandos (!xxx) a SendMarkdown
  • Cambiar runtime.go — respuesta de tarea orquestada a SendMarkdown
  • Actualizar interfaz en tools/matrix.go si aplica
  • Actualizar mocks en tests
  • Test manual: enviar mensaje al bot y verificar que Element renderiza markdown

Notas

  • goldmark es safe por defecto (escapa HTML peligroso) — no hay riesgo XSS.
  • El Body del evento Matrix queda como markdown crudo — esto es correcto según la spec de Matrix (es el fallback plaintext).
  • Los mensajes de error simples ("Comando desconocido: !foo") también pasan por SendMarkdown — no pasa nada, goldmark los deja como <p>texto</p> sin más.