diff --git a/apps/android/app/src/main/java/ai/openclaw/android/node/DeviceNotificationListenerService.kt b/apps/android/app/src/main/java/ai/openclaw/android/node/DeviceNotificationListenerService.kt index ecc5cee433d..0663bb8158d 100644 --- a/apps/android/app/src/main/java/ai/openclaw/android/node/DeviceNotificationListenerService.kt +++ b/apps/android/app/src/main/java/ai/openclaw/android/node/DeviceNotificationListenerService.kt @@ -137,6 +137,9 @@ class DeviceNotificationListenerService : NotificationListenerService() { super.onNotificationPosted(sbn) val entry = sbn?.toEntry() ?: return DeviceNotificationStore.upsert(entry) + if (entry.packageName == packageName) { + return + } emitNotificationsChanged( buildJsonObject { put("change", JsonPrimitive("posted")) @@ -162,6 +165,9 @@ class DeviceNotificationListenerService : NotificationListenerService() { return } DeviceNotificationStore.remove(key) + if (removed.packageName == packageName) { + return + } emitNotificationsChanged( buildJsonObject { put("change", JsonPrimitive("removed")) diff --git a/src/gateway/server-node-events.test.ts b/src/gateway/server-node-events.test.ts index 9af43490e4e..e6ef1ed0e3c 100644 --- a/src/gateway/server-node-events.test.ts +++ b/src/gateway/server-node-events.test.ts @@ -374,7 +374,10 @@ describe("notifications changed events", () => { "Notification posted (node=node-n1 key=notif-1 package=com.example.chat): Message - Ping from Alex", { sessionKey: "node-node-n1", contextKey: "notification:notif-1" }, ); - expect(requestHeartbeatNowMock).toHaveBeenCalledWith({ reason: "notifications-event" }); + expect(requestHeartbeatNowMock).toHaveBeenCalledWith({ + reason: "notifications-event", + sessionKey: "node-node-n1", + }); }); it("enqueues notifications.changed removed events", async () => { @@ -392,7 +395,27 @@ describe("notifications changed events", () => { "Notification removed (node=node-n2 key=notif-2 package=com.example.mail)", { sessionKey: "node-node-n2", contextKey: "notification:notif-2" }, ); - expect(requestHeartbeatNowMock).toHaveBeenCalledWith({ reason: "notifications-event" }); + expect(requestHeartbeatNowMock).toHaveBeenCalledWith({ + reason: "notifications-event", + sessionKey: "node-node-n2", + }); + }); + + it("wakes heartbeat on payload sessionKey when provided", async () => { + const ctx = buildCtx(); + await handleNodeEvent(ctx, "node-n4", { + event: "notifications.changed", + payloadJSON: JSON.stringify({ + change: "posted", + key: "notif-4", + sessionKey: "agent:main:main", + }), + }); + + expect(requestHeartbeatNowMock).toHaveBeenCalledWith({ + reason: "notifications-event", + sessionKey: "agent:main:main", + }); }); it("ignores notifications.changed payloads missing required fields", async () => { diff --git a/src/gateway/server-node-events.ts b/src/gateway/server-node-events.ts index 53b34132671..50d1bd425bd 100644 --- a/src/gateway/server-node-events.ts +++ b/src/gateway/server-node-events.ts @@ -485,7 +485,7 @@ export const handleNodeEvent = async (ctx: NodeEventContext, nodeId: string, evt } enqueueSystemEvent(summary, { sessionKey, contextKey: `notification:${key}` }); - requestHeartbeatNow({ reason: "notifications-event" }); + requestHeartbeatNow({ reason: "notifications-event", sessionKey }); return; } case "chat.subscribe": {