Desktop OS notifications and session routing fixes
NousResearch/hermes-agent · cluster 48808 · apps/desktop
Data as of
Nothing open on GitHub. Fix merged in #48999. Check it covers your case.
The short answer
- Known?
- Yes. 5 related items grouped here, 2 issues already closed.
- Fix event
- A fix was merged to main in PR #48999. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
resolved, deciding PRs #48999, #107077. That is separate from the fix event. - Still open
- Nothing open on GitHub.
Work on native OS notification support for the desktop app, including per-type toggles and fixes for correctly resuming or navigating to the corresponding session when a notification is clicked. Includes bug reports about session-not-found errors and window restoration on notification click.
Open issues0
Open PRs0
Fixes on record
-
#48999 fix(desktop): resume stored session id on notification click
files:
apps/desktop/src/app/desktop-controller.tsx,apps/desktop/src/lib/session-ids.test.ts,apps/desktop/src/lib/session-ids.tsShow the diff
-
apps/desktop/src/app/desktop-controller.tsx@@ -14,6 +14,7 @@ import { useSkinCommand } from '@/themes/use-skin-command' import { formatRefValue } from '../components/assistant-ui/directive-text' import { getCronJobs, getSessionMessages, listAllProfileSessions, type SessionInfo, triggerCronJob } from '../hermes' import { type ChatMessage, chatMessageText, preserveLocalAssistantErrors, toChatMessages } from '../lib/chat-messages' +import { storedSessionIdForNotification } from '../lib/session-ids' import { isMessagingSource, LOCAL_SESSION_SOURCE_IDS, @@ -276,16 +277,20 @@ export function DesktopController() { } }, []) - // Notification click: the main process already focused the window; jump to its session. + // Notification click: the main process already focused the window; jump to its + // session. Notifications are tagged with the gateway *runtime* session id, but + // the chat route is keyed by the *stored* id — navigating with the runtime id + // resumes a non-existent stored session ("session not found") and strands the + // user. Translate runtime -> stored before navigating. useEffect(() => { const unsubscribe = window.hermesDesktop?.onFocusSession?.(sessionId => { if (sessionId) { - navigate(sessionRoute(sessionId)) + navigate(sessionRoute(storedSessionIdForNotification(sessionId, runtimeIdByStoredSessionIdRef.current))) } }) return () => unsubscribe?.() - }, [navigate]) + }, [navigate, runtimeIdByStoredSessionIdRef]) // Notification action button (Approve/Reject) — resolve in place, no navigation. useEffect(() => { -
apps/desktop/src/lib/session-ids.test.ts@@ -0,0 +1,44 @@ +import { describe, expect, it } from 'vitest' + +import { storedSessionIdForNotification } from './session-ids' + +describe('storedSessionIdForNotification', () => { + it('translates a runtime id back to its stored id', () => { + // The route is keyed by the stored id, but notifications carry the runtime + // id. Resolving runtime -> stored keeps notification-click navigation from + // resuming a non-existent stored session ("session not found"). + const map = new Map([['stored-abc', 'runtime-123']]) + + expect(storedSessionIdForNotification('runtime-123', map)).toBe('stored-abc') + }) + + it('returns the id unchanged when no mapping is known', () => { + // A notification for a session this window never opened may already carry a + // stored id; let the resume/REST lookup handle it as-is. + const map = new Map([['stored-abc', 'runtime-123']]) + + expect(storedSessionIdForNotification('stored-xyz', map)).toBe('stored-xyz') + }) + + it('returns the id unchanged for an empty map', () => { + expect(storedSessionIdForNotification('runtime-123', new Map())).toBe('runtime-123') + }) + + it('resolves the correct stored id among several sessions', () => { + const map = new Map([ + ['stored-1', 'runtime-1'], + ['stored-2', 'runtime-2'], + ['stored-3', 'runtime-3'] + ]) + + expect(storedSessionIdForNotification('runtime-2', map)).toBe('stored-2') + }) + + it('does not treat a stored id as a runtime id (keys are not matched)', () => { + // The map is stored -> runtime. A value that only appears as a *key* must + // not be rewritten, otherwise an already-stored id could be mangled. + const map = new Map([['stored-1', 'runtime-1']]) + + expect(storedSessionIdForNotification('stored-1', map)).toBe('stored-1') + }) +}) -
apps/desktop/src/lib/session-ids.ts@@ -0,0 +1,26 @@ +// The gateway tags every event — and therefore every native notification — +// with the *runtime* session id (the key under which the session lives in the +// gateway's in-memory `_sessions` map). The chat route, however, is keyed by +// the *stored* session id (`stored_session_id`), which is a different value: +// a brand-new chat gets a runtime id immediately but its stored id is assigned +// when the first turn persists. Navigating to a runtime id therefore tries to +// resume a stored session that does not exist ("session not found") and +// strands the user, who experiences it as the running session being destroyed. +// +// `runtimeIdByStoredSessionId` maps stored -> runtime; this resolves the +// reverse so notification-click navigation lands on the real route. The id is +// returned unchanged when no mapping is known — it may already be a stored id +// (e.g. a notification for a session this window never opened), in which case +// the normal resume/REST lookup handles it. +export function storedSessionIdForNotification( + id: string, + runtimeIdByStoredSessionId: ReadonlyMap<string, string> +): string { + for (const [storedId, runtimeId] of runtimeIdByStoredSessionId) { + if (runtimeId === id) { + return storedId + } + } + + return id +}
-
-
#107077 fix(desktop): make native notifications reveal their session tab
files:
apps/desktop/electron/main.ts,apps/desktop/electron/notification-actions.test.ts,apps/desktop/electron/notification-actions.ts,apps/desktop/electron/notification-ipc.test.ts,apps/desktop/electron/notification-ipc.ts,apps/desktop/electron/notification-registry.test.ts,apps/desktop/electron/notification-registry.ts,apps/desktop/electron/notification-types.ts,apps/desktop/src/app/contrib/hooks/notification-focus.test.tsx,apps/desktop/src/app/contrib/hooks/use-desktop-integrations.test.tsx,apps/desktop/src/app/contrib/hooks/use-desktop-integrations.ts,apps/desktop/src/app/open-session.test.ts,apps/desktop/src/app/open-session.ts,apps/desktop/src/global.d.ts,apps/desktop/src/store/native-notifications.test.ts,apps/desktop/src/store/native-notifications.ts
1 PR closed without merging
From the close comments: other 1
-
#48831 fix(desktop): route notification clicks by stored session id · other →
#48808,#48999
Closed issues · 2
How this cluster was grouped
-
Link evidence (
partly structural links): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text. - Members outside that structural core may belong to a different underlying problem.
-
Pairwise link density
0.34: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the Lab