Files
paseo/docs/notifications.md
T
Mohamed BoudraandKassam Khoja 12e7a448f0 fix(app): open notification agents once in their current workspace (#7612)
* fix(app): open notification agents once in their current workspace

Keep tap ownership across Activity and React remounts, clear native responses, and resolve agent workspaces through existing navigation after startup settles.

Co-authored-by: Kassam Khoja <kassam@shopwrk.com>

* fix(app): hydrate agent targets with the compact sidebar closed

Hold directory demand for the agent route while resolving its workspace; the compact sidebar does not supply demand before it opens.

* fix(app): keep deferred workspace opens in the layout model

Key pending tab opens by workspace so redirects and retained screens cannot lose or consume another workspace's target. Legacy open URLs seed this state at their route boundary.

* docs(app): document notification tap ownership and native replay

* fix(ci): route feature-owned Electron sources to desktop checks

* refactor(app): separate notification API from React integration

* fix(app): consume pending opens before painting the restored tab

A legacy open link showed the previously focused terminal for a frame before switching to the requested terminal. Seed and consume the pending open in layout effects so a ready target switches before paint.

* fix(e2e): focus the visible retained terminal surface

---------

Co-authored-by: Kassam Khoja <kassam@shopwrk.com>
2026-10-22 14:41:38 +07:00

72 lines
3.3 KiB
Markdown

# Notifications
Use `notification-open` for OS notification taps. It owns delivery, parsing,
process-local deduplication, startup readiness, and native acknowledgement.
Keep agent and workspace navigation in their existing navigation modules.
Import the pure API from `@/notification-open` and the app-shell component from
`@/notification-open/react`; senders must not load the React routing runtime.
Agent payloads name a host and agent. Ignore their workspace hint when opening:
the agent can move after a notification is sent. Use `navigateToAgent`, which
uses the current store record or the agent route to resolve the workspace from
the host. Terminal payloads name their workspace and use `navigateToWorkspace`.
See [agent targets and deferred opens](expo-router.md#agent-targets) for route
ownership and workspace recovery. Notification generation, presence, and push
eligibility remain daemon concerns.
## Native delivery
Keep the router instance at module scope. Android's `singleTask` Activity can
finish on Back while its JS context remains alive. A React ref is lost when the
root remounts; process-local handled IDs must survive that remount.
Clear `lastNotificationResponse` after dispatch. Reading a launch response does
not acknowledge it. Without clearing it, a later Activity launch can reopen the
old agent.
With expo-notifications 57, Android also replays its pending response list to
new native listeners. Clearing the last response does not drain that list.
Deduplication covers listener registration within one JS context. A development
JS reload creates another context and can replay a cold-launch response; do not
add persisted handled IDs to compensate. Recheck this limitation when upgrading
expo-notifications.
iOS drains pending responses when its delegate first handles them. Push data
read by Expo arrives under `userInfo.body`; simulator payloads must match:
```json
{
"aps": { "alert": { "title": "Agent finished", "body": "Open the agent" }, "sound": "default" },
"body": {
"serverId": "host",
"workspaceId": "workspace",
"agentId": "agent",
"reason": "finished"
}
}
```
An Android FCM launch intent needs `google.message_id` and a JSON `body` extra.
An arbitrary Activity launch with data extras does not exercise Expo's FCM path.
Foreground pushes remain in Notification Center without a banner or sound.
This preserves notifications sent with stale presence information.
## Startup and verification
Wait for the root navigator, host registry, remembered selection, and startup
redirects before handing off a tap. A compact agent route must demand the host's
directory itself; its sidebar can be closed for the entire cold launch.
Verify warm and cold taps, stale and missing workspace hints, two distinct taps,
and Android Back followed by launcher reopen or a different notification tap. Check the focused tab and every
workspace's saved layout, since a foreign pinned tab survives navigation.
Use the daemon's payload shape on both iOS and Android; a browser event alone
does not test native replay or acknowledgement.
Existing foreign pins belong to the reconciliation fix in
[#6900](https://github.com/getpaseo/paseo/issues/6900). Desktop main-process window
selection is tracked separately in
[#6875](https://github.com/getpaseo/paseo/issues/6875).