Перейти до основного вмісту

Сповіщення

AgentCore зберігає in-app сповіщення для кожного користувача та віддає нові події через персональний WebSocket-канал. Сповіщення потрібні для речей, що вимагають уваги поза поточним чатом: схвалення, згадки, згенеровані документи, операційні алерти.

Модель даних

Сповіщення зберігаються в таблиці notifications:

ПолеПризначення
userIdОдержувач. Усі операції списку, читання та видалення ведуться для цього користувача.
typeТип події, що задає продюсер, наприклад approval_pending, mention або document_ready.
titleКороткий заголовок для inbox.
bodyОпційний довший текст.
entityType / entityIdОпціональне посилання на таргет: approval, message, document або generation.
readAtnull, поки користувач не позначить сповіщення прочитаним.
createdAtКлюч сортування для inbox та задач ретеншну.

Індекси (userId, readAt) і (userId, createdAt) роблять дешевими підрахунок непрочитаних, пагінацію та скани ретеншну.

Типи сповіщень

Сервер тримає type як рядок, щоб нових продюсерів можна було додавати без міграції БД. Поточні та заплановані типи:

ТипПродюсерSubject
approval_pendingAgent runner і approval-маршрути, коли потрібен або ескалований HITL-ревʼю.approval
mentionКолаборативні фічі, коли користувача згадують у треді або документі.message, comment або document
document_readyГенерація документів і ingestion знань, коли результат готовий.document або document_generation
systemОпераційні повідомлення, не привʼязані до бізнес-сутності.Опційно

Запис сповіщення — довготривалий. Доставка через WebSocket — це транспорт реального часу; клієнти все одно мають перезапитувати REST-inbox після реконекту.

REST API

Усі маршрути вимагають JWT-автентифікації і працюють тільки для поточного користувача.

ОпераціяЕндпоінтПримітки
Список inboxGET /api/v1/notifications?limit=20&offset=0Повертає items, pagination і unreadCount.
Фільтр непрочитанихGET /api/v1/notifications?unreadOnly=trueВикористовує readAt IS NULL.
Позначити прочитанимPATCH /api/v1/notifications/:id/readІдемпотентно; вже прочитані рядки повертаються без змін.
Позначити все прочитанимPOST /api/v1/notifications/read-allОновлює всі непрочитані рядки поточного користувача.
Видалити однеDELETE /api/v1/notifications/:idВидаляє тільки рядки, які належать поточному користувачу.

Route layer завжди перевіряє id та userId, тож користувачі не можуть прочитати чи видалити чуже сповіщення вгадавши ID.

WebSocket-бродкастинг

Live-доставка йде через /ws/notifications.

Клієнти автентифікуються одним із двох способів:

  1. підключитися з ?token=<jwt>; або
  2. підключитися і відправити { "action": "auth", "token": "<jwt>" } першим повідомленням.

Якщо автентифікація не завершується за пʼять секунд — сокет закривається з кодом 4001.

Після автентифікації сокет підписується на in-process EventEmitter-ключ за userId. Продюсери створюють рядок у БД і викликають emitNotification(). WebSocket віддає:

{
"type": "notification.created",
"payload": {
"id": "clx...",
"userId": "clu...",
"type": "approval_pending",
"title": "Approval required",
"body": "A generated reply needs review",
"entityType": "approval",
"entityId": "clp...",
"readAt": null,
"createdAt": "2026-04-16T10:15:00.000Z"
}
}

Оскільки emitter локальний для процесу, для горизонтального масштабування треба додати shared pub/sub layer, перш ніж кілька API-реплік зможуть бродкастити у сокети на різних подах. Redis pub/sub — природний варіант, бо Redis уже потрібен для BullMQ.

Стан прочитання

Стан непрочитаного визначається полем readAt.

  • Сповіщення непрочитане, коли readAtnull.
  • Позначення прочитаним виставляє readAt у поточний серверний час.
  • read-all оновлює всі непрочитані сповіщення поточного користувача.
  • Видалення рядка прибирає його з inbox і з майбутніх підрахунків непрочитаних.

Клієнти можуть оптимістично оновлювати локальний стан читання, але мають ресинхронізуватись із GET /notifications після реконекту або невдалих write-запитів.

Політика ретеншну

У request-path немає автоматичного очищення. Рекомендована продакшн-політика — запланований cleanup-джоб:

  • тримати непрочитані доти, доки їх не прочитають або явно не видалять;
  • тримати прочитані 90 днів;
  • тримати approval_pending і document_ready довше, якщо compliance вимагає видимості повʼязаного схвалення чи документа;
  • видаляти невеликими батчами з сортуванням по createdAt, щоб уникнути довгих table locks.

Cleanup-джоб ніколи не має видаляти повʼязану бізнес-сутність. Видаляється тільки inbox-покажчик.

Преференції tenant

Налаштування сповіщень моделюються на рівні tenant/product як direct або digest-доставка:

ПреференціяПоведінка
directНегайно показати WebSocket-подію і зберегти рядок в inbox.
digestЗберегти рядок в inbox, приглушити негайне UI-переривання, включити у запланований summary.
mutedЗберегти рядки, важливі для аудиту, але не показувати у проактивному UI.

Поки преференції не зберігаються серверно, клієнти мають тримати локальні налаштування на type сповіщення й усе одно брати канонічний unread-count із сервера.

Чекліст продюсера

При додаванні нового продюсера:

  1. створіть Notification-рядок у тому ж логічному flow, що й вихідна подія;
  2. задайте entityType і entityId, коли користувачу треба кудись перейти;
  3. викличте emitNotification(notification) після комміта рядка;
  4. додайте або розширте тести поведінки REST-inbox і WebSocket-бродкасту;
  5. задокументуйте, чи за замовчуванням подія — direct чи digest.