# Task 011 — Matrix Thread Support ## Objetivo Permitir que los agentes mantengan conversaciones en threads de Matrix (`m.thread`), de forma que cada interaccion con un usuario pueda vivir en un hilo separado en lugar de la timeline principal del room. Las respuestas del agente deben volver en el hilo y no en la rama principal ## Contexto Matrix soporta threads via `m.relates_to` con `rel_type: "m.thread"`. Un thread siempre referencia un **evento raiz** y opcionalmente incluye `m.in_reply_to` como fallback para clientes sin soporte de threads. ```json { "m.relates_to": { "rel_type": "m.thread", "event_id": "$rootEventId", "is_falling_back": true, "m.in_reply_to": { "event_id": "$lastEventInThread" } } } ``` ## Prerequisito - Task: Reply simple (`m.in_reply_to`) ya implementado. ## Plan de implementacion ### 1. Detectar threads entrantes en el Listener - En `shell/matrix/listener.go`, al parsear el evento, extraer `m.relates_to` - Si `rel_type == "m.thread"`, capturar `event_id` como `ThreadRootID` - Propagar `ThreadRootID` en `MessageContext` ### 2. Extender MessageContext - `pkg/decision/types.go`: anadir `ThreadRootID string` (el evento raiz del thread) - Esto es dato puro, no rompe la arquitectura ### 3. Extender ReplyAction - `pkg/decision/types.go`: anadir `ThreadRootID string` a `ReplyAction` - El runner usara esto para decidir si enviar como thread o como mensaje normal ### 4. SendThreadMarkdown en Client - `shell/matrix/client.go`: nuevo metodo `SendThreadMarkdown(ctx, roomID, threadRootID, inReplyTo, markdown)` - Construye el `m.relates_to` con `rel_type: "m.thread"` + fallback `m.in_reply_to` ### 5. Actualizar effects/Runner - `shell/effects/runner.go`: si `ReplyAction.ThreadRootID != ""`, usar `SendThreadMarkdown` - Actualizar interfaz `MatrixSender` con el nuevo metodo ### 6. Propagacion en runtime.go - Cuando el mensaje entrante ya esta en un thread (`msgCtx.ThreadRootID != ""`), las respuestas del bot deben continuar en ese thread - Cuando el usuario inicia una conversacion nueva, decidir segun config si crear thread o no ### 7. Configuracion por agente - `internal/config/schema.go`: anadir opcion `matrix.threads.enabled: bool` y `matrix.threads.auto_thread: bool` (crear thread automatico por cada conversacion nueva) - Default: `enabled: true`, `auto_thread: false` ### 8. Memory por thread - La window de conversacion deberia poder ser por thread en vez de por room - Si `ThreadRootID != ""`, usar `threadRootID` como key de la window en vez de `roomID` - Esto permite conversaciones paralelas en threads distintos sin mezclarse ### 9. Tests - Unit tests para `SendThreadMarkdown` (verificar estructura JSON) - Test de integracion: listener detecta thread entrante y propaga ThreadRootID - Test: respuesta dentro de thread mantiene el thread root correcto ## Notas - `is_falling_back: true` siempre debe estar cuando se usa thread + in_reply_to fallback - El `event_id` de `m.relates_to` (nivel top) siempre apunta al root del thread, nunca cambia - El `m.in_reply_to` dentro del thread apunta al ultimo mensaje respondido - Clientes sin soporte de threads ven el fallback como un reply normal