Some checks failed
Build and Deploy Docs / build-and-deploy (push) Has been cancelled
- Created logs/ structure (sessions, operations, incidents) - Added session-start/log/end scripts - Installed Git hooks for auto-logging commits/pushes - Added shell integration for zsh - Created CHANGELOG.md - Documented today's session (2026-01-10)
382 lines
12 KiB
Markdown
382 lines
12 KiB
Markdown
# ✅ 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<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 подій
|
||
|
||
**Було:**
|
||
```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 - повідомлення залишаться!
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|