fix FCM push notifications
This commit is contained in:
1 parent
560d9c19b3
commit
d801a18db5
4 files changed
+177
-6
No files matched your search
+37
-4
@@ -5,13 +5,44 @@ relay. **Decision: FCM primary, ntfy fallback** (`IRIS_PUSH_BACKEND`).
|
||||
|
||||
## 8.1 When push fires
|
||||
|
||||
- A frame targets a `chat_id` whose device is **disconnected** (WS closed) →
|
||||
drop to **outbox** + fire **push**.
|
||||
- A frame targets a `chat_id` whose device is **disconnected** (no live
|
||||
SSE/long-poll subscriber) → drop to **outbox** + fire **push** — with one
|
||||
refinement, *turn-aware push* (below).
|
||||
- Also fire push for high-priority foreground events the user should see even if
|
||||
the app is backgrounded (approvals, clarifies, cron completions) — the app
|
||||
decides whether to also show an in-app banner.
|
||||
- If the device is **connected**, no push (the live frame is enough).
|
||||
|
||||
### 8.1.1 Turn-aware push (one push per turn, final answer as body)
|
||||
|
||||
An agent turn can span minutes and emit several completed status messages
|
||||
("researching X…", "found Y…", "writing findings…", final answer). Pushing each
|
||||
parked message would spam an offline user with the steps in between. So while
|
||||
the agent's turn is **in flight** (hermes holds the typing indicator on from
|
||||
turn start until the handler's `finally` at turn end), normal-priority
|
||||
`message` / `message.stop` / `media.offer` frames that park with no live
|
||||
device are **held back** per chat instead of pushing; the latest one is pushed
|
||||
when the turn ends (`stop_typing`), so the offline user gets **one push with
|
||||
the final answer**. Details:
|
||||
|
||||
- Turn state is tracked per `chat_id` from the typing indicator
|
||||
(`send_typing` → in flight, `stop_typing` → ended; hermes fires
|
||||
`stop_typing` in the handler's `finally`, after the final send, so the
|
||||
flush always sees the final frame).
|
||||
- The held-back frame is still parked in the outbox — sync catch-up is
|
||||
unaffected; only the push is deferred.
|
||||
- **High-priority notifications** (approval/clarify/cron) push immediately,
|
||||
even mid-turn — they need user action.
|
||||
- If the device **reconnects mid-turn** (SSE/long-poll open), the held-back
|
||||
push is dropped: the app syncs the parked frames and must not get a
|
||||
duplicate push at turn end.
|
||||
- If the turn ends while the device is live, nothing is pushed (the frames
|
||||
were delivered live / synced).
|
||||
- Turns without a typing indicator (e.g. typing disabled in config) and
|
||||
non-turn deliveries (cron, standalone sends) push immediately as before.
|
||||
- Best-effort: a gateway crash mid-turn loses the held-back push (the frames
|
||||
remain in the outbox and sync on reconnect).
|
||||
|
||||
## 8.2 `PushBackend` interface (`push.py`)
|
||||
|
||||
```python
|
||||
@@ -26,9 +57,10 @@ class PushBackend(Protocol):
|
||||
Selected at adapter init by `IRIS_PUSH_BACKEND` (`fcm` default, `ntfy`).
|
||||
|
||||
### 8.2.1 `FcmBackend` (primary)
|
||||
|
||||
- **FCM HTTP v1 API** via `httpx` (core dep). Auth = Firebase **service
|
||||
account** (`IRIS_FCM_SERVICE_ACCOUNT` JSON path) → mint a short-lived
|
||||
OAuth2 access token (cached, refreshed before expiry).
|
||||
OAuth2 access token (cached, refreshed before expiry).
|
||||
- Fallback: legacy **server key** (`IRIS_FCM_SERVER_KEY`) if no service
|
||||
account (simpler, but legacy).
|
||||
- Target = the device's **FCM token** (registered via `hello` /
|
||||
@@ -43,6 +75,7 @@ Selected at adapter init by `IRIS_PUSH_BACKEND` (`fcm` default, `ntfy`).
|
||||
devices).
|
||||
|
||||
### 8.2.2 `NtfyBackend` (fallback, self-host friendly)
|
||||
|
||||
- Reuses hermes's existing ntfy publish path (hermes ships an ntfy adapter).
|
||||
- Publish to `NTFY_TOPIC` on `NTFY_SERVER_URL` (default `https://ntfy.sh`) via
|
||||
`httpx` POST, with an `X-Title` / `X-Message` / `X-Tag` / `X-Priority` and a
|
||||
@@ -132,4 +165,4 @@ device push watermark:
|
||||
Residual edge: FCM is at-least-once, so a lost device ack can still produce a
|
||||
duplicate *system-displayed* notification (two `FCM-Notification:*` ids). The
|
||||
designed evolution is the data-only push option (§8.2.1), which moves display
|
||||
into the app and lets it use a stable per-message notification id.
|
||||
into the app and lets it use a stable per-message notification id.
|
||||
Reference in new issue
Block a user