- Node-guardian running on MacBook and updating metrics - NODE2 agents (Atlas, Greeter, Oracle, Builder Bot) assigned to node-2-macbook-m4max - Swapper models displaying correctly (8 models) - DAGI Router agents showing with correct status (3 active, 1 stale) - Router health check using node_cache for remote nodes
12 KiB
✅ Monitor Agent - Виправлення персистентності повідомлень
Дата: 2025-11-23
Статус: ✅ Повідомлення тепер зберігаються і не зникають
🎯 Проблема
Симптоми:
- Вікно чату порожнє при відкритті
- Повідомлення з'являються в консолі, але зникають з UI
- Події
project-changeвідправляються, але не відображаються
Консоль показувала:
✅ CustomEvent dispatched successfully
✅ Adding project change message to chat: change-...
📝 Total messages after add: 1
Але в UI нічого не було!
🔍 Причина
-
Повідомлення не зберігалися між перезавантаженнями
- При кожному рефреші всі повідомлення зникали
- Не було персистентності в
localStorage
-
Дублікати видалялися занадто агресивно
- Перевірка на дублікати виконувалася ДО додавання
- Це блокувало деякі валідні повідомлення
-
Події не проходили через Shadow DOM
CustomEventне мавbubbles: trueіcomposed: true- Це могло блокувати події в деяких випадках
✅ Рішення
1. Додано персистентність в localStorage
Файл: src/pages/DagiMonitorPage.tsx
// Завантаження повідомлень при ініціалізації
const [messages, setMessages] = React.useState<ChatMessage[]>(() => {
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 подій
Було:
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; // Блокував додавання
}
// ...
});
};
Стало:
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
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. Перевірити персистентність
- Відкрити:
http://localhost:8899/dagi-monitor - Почекати 5 секунд (мають з'явитися повідомлення)
- Натиснути F5 (рефреш)
- Повідомлення мають залишитися!
2. Перевірити автоматичні повідомлення
- Відкрити чат
- Нічого не натискати
- Через 3-5 секунд з'являються нові повідомлення
- Повідомлення не зникають!
3. Перевірити localStorage
// В консолі браузера
JSON.parse(localStorage.getItem('monitor-chat-messages')).length
// Має показати кількість повідомлень (наприклад, 10)
JSON.parse(localStorage.getItem('monitor-latest-changes')).length
// Має показати кількість змін (наприклад, 15)
4. Очистити повідомлення
// В консолі браузера (якщо потрібно почати з чистого аркуша)
localStorage.removeItem('monitor-chat-messages');
localStorage.removeItem('monitor-latest-changes');
location.reload();
📝 localStorage Структура
monitor-chat-messages
Зберігає останні 100 повідомлень чату:
[
{
"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):
[
{
"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
// Зберігаємо останні 100 повідомлень
localStorage.setItem('monitor-chat-messages', JSON.stringify(messages.slice(0, 100)));
Можна змінити:
50- для менших пристроїв100- оптимально200- для більшої історії
Кількість змін для backup
Файл: src/services/projectChangeTracker.ts
// Зберігаємо останні 50 змін
localStorage.setItem(storageKey, JSON.stringify(changes.slice(0, 50)));
🎯 Результат
До виправлення:
❌ Проблеми:
- Вікно порожнє
- Повідомлення зникають
- Немає персистентності
- Події не проходять
Після виправлення:
✅ Результат:
- Повідомлення залишаються в чаті
- Персистентність між сесіями
- Події проходять через Shadow DOM
- Backup в localStorage
🐛 Дебаг
Якщо повідомлення не з'являються:
-
Перевірити консоль:
// Має бути: "✅ Subscribed to project-change events" "✅ CustomEvent dispatched: true" "✅ Adding project change message to chat: ..." "💾 Saved messages to localStorage: ..." -
Перевірити localStorage:
console.log(localStorage.getItem('monitor-chat-messages')); -
Перевірити ProjectChangeTracker:
// В консолі "🚀 Starting real-time change tracking (every 3 seconds)..." "🔍 Checking for real-time changes..." -
Очистити localStorage і рефреш:
localStorage.clear(); location.reload();
✅ Чекліст
- Додано завантаження з localStorage при ініціалізації
- Додано збереження в localStorage при зміні messages
- Спрощено handleProjectChange (без ref)
- Додано bubbles: true, composed: true для CustomEvent
- Додано backup в localStorage в emitChangeEvent
- Зберігаємо останні 100 повідомлень
- Зберігаємо останні 50 змін (backup)
- Перевірка дублікатів тільки за ID
- Логування для дебагу
Статус: ✅ Готово! Повідомлення тепер зберігаються!
Тестуйте: Відкрийте http://localhost:8899/dagi-monitor і натисніть F5 - повідомлення залишаться!