From a8bcad3db15d688c619ca4486185bea0134b1f5b Mon Sep 17 00:00:00 2001 From: Ayaan Zaidi Date: Sat, 28 Feb 2026 10:50:47 +0530 Subject: [PATCH] fix(gateway): canonicalize notification wake session --- src/gateway/server-node-events.test.ts | 27 ++++++++++++++++++++++++++ src/gateway/server-node-events.ts | 3 ++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/gateway/server-node-events.test.ts b/src/gateway/server-node-events.test.ts index e6ef1ed0e3c..12fdbcf57b6 100644 --- a/src/gateway/server-node-events.test.ts +++ b/src/gateway/server-node-events.test.ts @@ -355,6 +355,8 @@ describe("notifications changed events", () => { beforeEach(() => { enqueueSystemEventMock.mockClear(); requestHeartbeatNowMock.mockClear(); + loadSessionEntryMock.mockClear(); + loadSessionEntryMock.mockImplementation((sessionKey: string) => buildSessionLookup(sessionKey)); }); it("enqueues notifications.changed posted events", async () => { @@ -418,6 +420,31 @@ describe("notifications changed events", () => { }); }); + it("canonicalizes notifications session key before enqueue and wake", async () => { + loadSessionEntryMock.mockReturnValueOnce({ + ...buildSessionLookup("node-node-n5"), + canonicalKey: "agent:main:node-node-n5", + }); + const ctx = buildCtx(); + await handleNodeEvent(ctx, "node-n5", { + event: "notifications.changed", + payloadJSON: JSON.stringify({ + change: "posted", + key: "notif-5", + }), + }); + + expect(loadSessionEntryMock).toHaveBeenCalledWith("node-node-n5"); + expect(enqueueSystemEventMock).toHaveBeenCalledWith( + "Notification posted (node=node-n5 key=notif-5)", + { sessionKey: "agent:main:node-node-n5", contextKey: "notification:notif-5" }, + ); + expect(requestHeartbeatNowMock).toHaveBeenCalledWith({ + reason: "notifications-event", + sessionKey: "agent:main:node-node-n5", + }); + }); + it("ignores notifications.changed payloads missing required fields", async () => { const ctx = buildCtx(); await handleNodeEvent(ctx, "node-n3", { diff --git a/src/gateway/server-node-events.ts b/src/gateway/server-node-events.ts index 50d1bd425bd..85b233a48c2 100644 --- a/src/gateway/server-node-events.ts +++ b/src/gateway/server-node-events.ts @@ -467,7 +467,8 @@ export const handleNodeEvent = async (ctx: NodeEventContext, nodeId: string, evt if (!key) { return; } - const sessionKey = normalizeNonEmptyString(obj.sessionKey) ?? `node-${nodeId}`; + const sessionKeyRaw = normalizeNonEmptyString(obj.sessionKey) ?? `node-${nodeId}`; + const { canonicalKey: sessionKey } = loadSessionEntry(sessionKeyRaw); const packageName = normalizeNonEmptyString(obj.packageName); const title = compactNotificationEventText(normalizeNonEmptyString(obj.title) ?? ""); const text = compactNotificationEventText(normalizeNonEmptyString(obj.text) ?? "");