# ✅ Monitor Agent - Виправлення персистентності повідомлень **Дата:** 2025-11-23 **Статус:** ✅ Повідомлення тепер зберігаються і не зникають --- ## 🎯 Проблема **Симптоми:** - Вікно чату порожнє при відкритті - Повідомлення з'являються в консолі, але зникають з UI - Події `project-change` відправляються, але не відображаються **Консоль показувала:** ``` ✅ CustomEvent dispatched successfully ✅ Adding project change message to chat: change-... 📝 Total messages after add: 1 ``` **Але в UI нічого не було!** --- ## 🔍 Причина 1. **Повідомлення не зберігалися між перезавантаженнями** - При кожному рефреші всі повідомлення зникали - Не було персистентності в `localStorage` 2. **Дублікати видалялися занадто агресивно** - Перевірка на дублікати виконувалася ДО додавання - Це блокувало деякі валідні повідомлення 3. **Події не проходили через Shadow DOM** - `CustomEvent` не мав `bubbles: true` і `composed: true` - Це могло блокувати події в деяких випадках --- ## ✅ Рішення ### 1. Додано персистентність в localStorage **Файл:** `src/pages/DagiMonitorPage.tsx` ```typescript // Завантаження повідомлень при ініціалізації const [messages, setMessages] = React.useState(() => { try { const saved = localStorage.getItem('monitor-chat-messages'); if (saved) { const parsed = JSON.parse(saved); console.log('📦 Loaded messages from localStorage:', parsed.length); return parsed; } } catch (error) { console.warn('Could not load messages from localStorage:', error); } return []; }); // Збереження повідомлень при зміні useEffect(() => { if (messages.length > 0) { try { localStorage.setItem('monitor-chat-messages', JSON.stringify(messages.slice(0, 100))); console.log('💾 Saved messages to localStorage:', messages.length); } catch (error) { console.warn('Could not save messages to localStorage:', error); } } }, [messages]); ``` ### 2. Спрощено обробку project-change подій **Було:** ```typescript const handleProjectChange = (event: Event) => { // ... складна логіка з ref ... setMessages((prev) => { const isDuplicate = prev.some((msg) => msg.content === message || // Порівнював контент! (msg.id && msg.id === `project-${change.id}`) ); if (isDuplicate) { return prev; // Блокував додавання } // ... }); }; ``` **Стало:** ```typescript const handleProjectChange = (event: Event) => { const customEvent = event as CustomEvent; const { message, change } = customEvent.detail; // Створюємо повідомлення одразу const newMessage: ChatMessage = { id: `project-${change?.id || Date.now()}`, role: 'assistant', content: message, timestamp: change?.timestamp || new Date().toISOString(), }; // Додаємо (перевірка дублікатів тільки за ID) setMessages((prev) => { if (newMessage.id) { const isDuplicate = prev.some((msg) => msg.id === newMessage.id); if (isDuplicate) return prev; } return [newMessage, ...prev]; }); }; ``` ### 3. Покращено emitChangeEvent **Файл:** `src/services/projectChangeTracker.ts` ```typescript private emitChangeEvent(message: string, change: ProjectChange) { if (typeof window !== 'undefined') { try { const event = new CustomEvent('project-change', { detail: { message, change }, bubbles: true, // Дозволяємо події спливати composed: true, // Проходити через Shadow DOM }); window.dispatchEvent(event); // ДОДАТКОВО: зберігаємо в localStorage const storageKey = 'monitor-latest-changes'; const existing = localStorage.getItem(storageKey); const changes = existing ? JSON.parse(existing) : []; changes.unshift({ message, change, timestamp: Date.now() }); localStorage.setItem(storageKey, JSON.stringify(changes.slice(0, 50))); } catch (error) { console.error('Error:', error); } } } ``` --- ## 📊 Архітектура персистентності ``` ┌─────────────────────────────────────────────────────┐ │ ProjectChangeTracker │ │ - Генерує зміни │ │ - Відправляє CustomEvent │ │ - Зберігає в localStorage (backup) │ └─────────────────────────────────────────────────────┘ ↓ CustomEvent 'project-change' ↓ ┌─────────────────────────────────────────────────────┐ │ DagiMonitorPage │ │ - Слухає CustomEvent │ │ - Додає до messages state │ │ - Автоматично зберігає в localStorage │ └─────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────┐ │ localStorage │ │ - 'monitor-chat-messages': останні 100 повідомлень│ │ - 'monitor-latest-changes': останні 50 змін │ └─────────────────────────────────────────────────────┘ ↓ При перезавантаженні сторінки ↓ ┌─────────────────────────────────────────────────────┐ │ DagiMonitorPage (нова сесія) │ │ - Завантажує з localStorage │ │ - Відновлює повідомлення │ │ - Продовжує слухати нові події │ └─────────────────────────────────────────────────────┘ ``` --- ## 🧪 Тестування ### 1. Перевірити персистентність 1. Відкрити: `http://localhost:8899/dagi-monitor` 2. Почекати 5 секунд (мають з'явитися повідомлення) 3. **Натиснути F5 (рефреш)** 4. **Повідомлення мають залишитися!** ### 2. Перевірити автоматичні повідомлення 1. Відкрити чат 2. Нічого не натискати 3. Через 3-5 секунд з'являються нові повідомлення 4. **Повідомлення не зникають!** ### 3. Перевірити localStorage ```javascript // В консолі браузера JSON.parse(localStorage.getItem('monitor-chat-messages')).length // Має показати кількість повідомлень (наприклад, 10) JSON.parse(localStorage.getItem('monitor-latest-changes')).length // Має показати кількість змін (наприклад, 15) ``` ### 4. Очистити повідомлення ```javascript // В консолі браузера (якщо потрібно почати з чистого аркуша) localStorage.removeItem('monitor-chat-messages'); localStorage.removeItem('monitor-latest-changes'); location.reload(); ``` --- ## 📝 localStorage Структура ### `monitor-chat-messages` Зберігає останні 100 повідомлень чату: ```json [ { "id": "project-change-1763904714538", "role": "assistant", "content": "🤖 **Monitor Agent:** 🔧 MODIFIED: ...node-1/swapper [node-1]", "timestamp": "2025-11-23T05:31:54.538Z" }, ... ] ``` ### `monitor-latest-changes` Зберігає останні 50 змін (backup): ```json [ { "message": "🤖 **Monitor Agent:** 🔧 MODIFIED: ...", "change": { "id": "change-1763904714538-abc", "type": "service", "action": "modified", "path": "nodes/node-1/swapper", "timestamp": "2025-11-23T05:31:54.538Z", "details": { "node_id": "node-1", "service": "swapper" } }, "timestamp": 1763904714538 }, ... ] ``` --- ## ⚙️ Конфігурація ### Кількість повідомлень для збереження **Файл:** `src/pages/DagiMonitorPage.tsx` ```typescript // Зберігаємо останні 100 повідомлень localStorage.setItem('monitor-chat-messages', JSON.stringify(messages.slice(0, 100))); ``` **Можна змінити:** - `50` - для менших пристроїв - `100` - оптимально - `200` - для більшої історії ### Кількість змін для backup **Файл:** `src/services/projectChangeTracker.ts` ```typescript // Зберігаємо останні 50 змін localStorage.setItem(storageKey, JSON.stringify(changes.slice(0, 50))); ``` --- ## 🎯 Результат ### До виправлення: ❌ **Проблеми:** - Вікно порожнє - Повідомлення зникають - Немає персистентності - Події не проходять ### Після виправлення: ✅ **Результат:** - Повідомлення залишаються в чаті - Персистентність між сесіями - Події проходять через Shadow DOM - Backup в localStorage --- ## 🐛 Дебаг ### Якщо повідомлення не з'являються: 1. **Перевірити консоль:** ```javascript // Має бути: "✅ Subscribed to project-change events" "✅ CustomEvent dispatched: true" "✅ Adding project change message to chat: ..." "💾 Saved messages to localStorage: ..." ``` 2. **Перевірити localStorage:** ```javascript console.log(localStorage.getItem('monitor-chat-messages')); ``` 3. **Перевірити ProjectChangeTracker:** ```javascript // В консолі "🚀 Starting real-time change tracking (every 3 seconds)..." "🔍 Checking for real-time changes..." ``` 4. **Очистити localStorage і рефреш:** ```javascript localStorage.clear(); location.reload(); ``` --- ## ✅ Чекліст - [x] Додано завантаження з localStorage при ініціалізації - [x] Додано збереження в localStorage при зміні messages - [x] Спрощено handleProjectChange (без ref) - [x] Додано bubbles: true, composed: true для CustomEvent - [x] Додано backup в localStorage в emitChangeEvent - [x] Зберігаємо останні 100 повідомлень - [x] Зберігаємо останні 50 змін (backup) - [x] Перевірка дублікатів тільки за ID - [x] Логування для дебагу --- **Статус:** ✅ Готово! Повідомлення тепер зберігаються! **Тестуйте:** Відкрийте `http://localhost:8899/dagi-monitor` і натисніть F5 - повідомлення залишаться!