Files
microdao-daarion/MONITOR-MESSAGES-PERSIST-FIX.md

12 KiB
Raw Blame History

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

// Завантаження повідомлень при ініціалізації
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. Перевірити персистентність

  1. Відкрити: http://localhost:8899/dagi-monitor
  2. Почекати 5 секунд (мають з'явитися повідомлення)
  3. Натиснути F5 (рефреш)
  4. Повідомлення мають залишитися!

2. Перевірити автоматичні повідомлення

  1. Відкрити чат
  2. Нічого не натискати
  3. Через 3-5 секунд з'являються нові повідомлення
  4. Повідомлення не зникають!

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

🐛 Дебаг

Якщо повідомлення не з'являються:

  1. Перевірити консоль:

    // Має бути:
    "✅ Subscribed to project-change events"
    "✅ CustomEvent dispatched: true"
    "✅ Adding project change message to chat: ..."
    "💾 Saved messages to localStorage: ..."
    
  2. Перевірити localStorage:

    console.log(localStorage.getItem('monitor-chat-messages'));
    
  3. Перевірити ProjectChangeTracker:

    // В консолі
    "🚀 Starting real-time change tracking (every 3 seconds)..."
    "🔍 Checking for real-time changes..."
    
  4. Очистити 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 - повідомлення залишаться!