Push notification dedupe: one message = one notification — an offline message was notified twice (FCM push, then again when the app synced the outbox and mirrored the replayed frames). Fix: the gateway records the highest outbox cursor delivered per device via push (devices.last_pushed_cursor, advanced only on successful send) and returns it in hello.ack; sync-replayed frames carry their outbox cursor in the envelope; the app skips system notifications for replayed frames at/below the watermark (live frames never suppressed — that is the case where no push fired). Also: 5s per-chat push coalescing so a cron delivery (notification frame + message frame) pushes once, and the FCM handler no longer posts a redundant notification (skips when WS is connected or FCM already displayed the notification payload; data-only messages are the exception). Docs: frames.schema.json, 04-wire-protocol.md, 08-push.md §8.8

This commit is contained in:
ARIA committed 2026-08-21 17:21:54 +02:00
1 parent acd5fb4ad0
commit 9f3f9842c8
11 files changed
+301 -98

No files matched your search

+9
View File
@@ -21,6 +21,10 @@ Every frame:
- `v` — protocol version (currently `1`). Server rejects unknown major versions.
- `id` — request id (client-chosen). Responses/acks echo it. Events have no `id`.
- `chat_id` / `thread_id` — top-level for convenience; may also be in `payload`.
- `cursor` — outbox cursor the frame was parked under. Present **only** on
frames replayed by `sync` (live frames carry none). The app compares it
against `last_pushed_cursor` from `hello.ack` to skip re-notifying frames
that already woke the device via push (`08-push.md` §8.7).
- Unknown `type`s are ignored (forward-compat); unknown `payload` fields ignored.
**Binary media frames** are not JSON. A media transfer is: one JSON header frame
@@ -36,9 +40,14 @@ Pairing succeeded.
"server_caps":{"streaming":true,"reasoning":true,"tools":true,"media":true,
"search":true,"push":"fcm","pickers":true},
"sync_cursor":1042,
"last_pushed_cursor":1040,
"channels":[{"chat_id":"android:default","name":"Default","kind":"default","is_default":true}]
}}
```
`last_pushed_cursor` is the highest outbox cursor already delivered to THIS
device via the push backend (0 = never). The app skips system notifications
for sync-replayed frames with `cursor <= last_pushed_cursor` — they already
woke the device via push (dedupe, `08-push.md` §8.7).
### `message`
A final / standalone message.