How the ReCursor bridge server notifies the mobile app about agent events — no Firebase, fully WebSocket-based.
Agent Event -> Bridge Server -> WebSocket -> Mobile App -> Local Notification (if backgrounded)
All notifications flow through the existing WebSocket connection. No external push services (FCM/APNs) are used.
- Bridge sends a
notificationmessage over the WebSocket. - App displays an in-app banner/toast or updates the notification center badge.
- No OS-level notification needed — the user is already in the app.
- If the WebSocket connection is still alive (kept by OS background mode), the app receives the event and displays a local notification via
flutter_local_notifications. - Local notifications support action buttons (e.g., "Approve" / "View").
- Bridge stores events in a pending event queue.
- On reconnect, bridge replays all unacknowledged events.
- App processes the backlog and shows relevant notifications.
| Type | Trigger | Priority | Action |
|---|---|---|---|
| Task Complete | Agent finishes a task | Normal | Navigate to result |
| Approval Required | Agent needs tool call approval | High | Approve/reject buttons |
| Error | Agent encounters an error | High | Navigate to chat |
| Agent Idle | Agent waiting for input | Low | Navigate to chat |
{
"type": "notification",
"id": "notif-001",
"payload": {
"session_id": "sess-abc123",
"notification_type": "approval_required",
"title": "Approval needed: Edit login.dart",
"body": "Claude Code wants to change the OAuth callback URL.",
"priority": "high",
"data": {
"tool_call_id": "tool-001",
"screen": "approval_detail"
}
}
}{
"type": "notification_ack",
"payload": {
"notification_ids": ["notif-001", "notif-002"]
}
}The bridge removes acknowledged events from the pending queue.
- Bell icon in the app bar with unread count badge.
- Tap to open notification list (grouped by session).
- Each notification is tappable — routes to the relevant screen.
- "Mark all read" action.
- Notifications persist locally in Drift for offline access.
- WebSocket heartbeat ensures connection is alive.
- If heartbeat fails, app shows "disconnected" banner — user knows notifications won't arrive.
- On reconnect, bridge replays full event backlog (bounded by configurable max age, e.g., 24 hours).
- No silent failure — if the connection is down, the user sees it immediately.
| WebSocket-only | Firebase FCM | |
|---|---|---|
| Works when app is killed | No | Yes |
| Requires Google/Apple services | No | Yes |
| Privacy | Full control | Data via Google servers |
| Complexity | Simpler, no external setup | FCM config, APNs certs |
| Reliability when connected | Immediate, guaranteed | Best-effort delivery |
The WebSocket-only approach is chosen because the app's primary value requires an active bridge connection anyway. If the bridge is unreachable, notifications are moot.
// Initialize local notifications
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
const androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
const iosSettings = DarwinInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
);
await flutterLocalNotificationsPlugin.initialize(
const InitializationSettings(android: androidSettings, iOS: iosSettings),
onDidReceiveNotificationResponse: (response) {
// Handle notification tap
_handleNotificationTap(response.payload);
},
);
// Show local notification
Future<void> showLocalNotification(AppNotification notification) async {
const androidDetails = AndroidNotificationDetails(
'recursor_channel',
'ReCursor Notifications',
importance: Importance.high,
priority: Priority.high,
showWhen: true,
);
const iosDetails = DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
);
await flutterLocalNotificationsPlugin.show(
notification.id.hashCode,
notification.title,
notification.body,
const NotificationDetails(android: androidDetails, iOS: iosDetails),
payload: jsonEncode(notification.data),
);
}- Bridge Protocol — WebSocket message specification
- Architecture Overview — System architecture
- Data Flow — Message sequence diagrams
Last updated: 2026-03-17