diff --git a/frontend/public/sw.js b/frontend/public/sw.js index cd54d9c..246a13b 100644 --- a/frontend/public/sw.js +++ b/frontend/public/sw.js @@ -2,12 +2,22 @@ self.addEventListener('push', event => { if (!event.data) return; const data = event.data.json(); + const notifUrl = data.url || '/'; + event.waitUntil( - self.registration.showNotification(data.title || 'Fabled Assistant', { - body: data.body || '', - icon: '/favicon.ico', - badge: '/favicon.ico', - data: { url: data.url || '/' }, + clients.matchAll({ type: 'window', includeUncontrolled: true }).then(clientList => { + // Suppress notification if the user already has the relevant page focused + for (const client of clientList) { + if (client.visibilityState === 'visible' && client.url.includes(notifUrl)) { + return; + } + } + return self.registration.showNotification(data.title || 'Fabled Assistant', { + body: data.body || '', + icon: '/favicon.ico', + badge: '/favicon.ico', + data: { url: notifUrl }, + }); }) ); }); diff --git a/src/fabledassistant/services/generation_task.py b/src/fabledassistant/services/generation_task.py index 7a16c29..5d22ee6 100644 --- a/src/fabledassistant/services/generation_task.py +++ b/src/fabledassistant/services/generation_task.py @@ -358,9 +358,18 @@ async def run_generation( try: from fabledassistant.services.push import send_push_notification, vapid_enabled if vapid_enabled(): - preview = buf.content_so_far[:120].rstrip() - if len(buf.content_so_far) > 120: - preview += "…" + text = buf.content_so_far.strip() + if text: + preview = text[:120].rstrip() + if len(text) > 120: + preview += "…" + else: + # Tool-only response — summarise what was done + tool_names = [tc.get("function") for tc in all_tool_calls if tc.get("function")] + if tool_names: + preview = f"Completed: {', '.join(tool_names[:3])}" + else: + preview = "Action completed" asyncio.create_task(send_push_notification( user_id, title="Response ready", @@ -368,7 +377,7 @@ async def run_generation( url=f"/chat/{conv_id}", )) except Exception: - logger.debug("Failed to schedule push notification", exc_info=True) + logger.warning("Failed to schedule push notification", exc_info=True) # Title generation is non-critical — fire-and-forget so done fires immediately non_system = [m for m in messages if m["role"] != "system"] diff --git a/src/fabledassistant/services/push.py b/src/fabledassistant/services/push.py index c67f3bb..87256ad 100644 --- a/src/fabledassistant/services/push.py +++ b/src/fabledassistant/services/push.py @@ -190,7 +190,7 @@ async def send_push_notification( ) return response.status_code, sub.endpoint except Exception as e: - logger.warning("Push send failed for sub %d: %s", sub.id, e) + logger.error("Push send failed for sub %d: %s", sub.id, e, exc_info=True) return 0, sub.endpoint loop = asyncio.get_event_loop() @@ -199,9 +199,12 @@ async def send_push_notification( for sub, result in zip(subscriptions, results): if isinstance(result, Exception): + logger.error("Push gather exception for user %d: %s", user_id, result, exc_info=result) continue status_code, endpoint = result - if status_code == 410: + if status_code in (200, 201): + logger.info("Push notification sent to sub %d (status %d)", sub.id, status_code) + elif status_code == 410: asyncio.create_task(_remove_expired_subscription(user_id, endpoint)) - elif status_code and status_code not in (200, 201): - logger.warning("Push returned status %d for user %d", status_code, user_id) + elif status_code: + logger.error("Push returned unexpected status %d for user %d sub %d", status_code, user_id, sub.id)