Skip to content

fix(desktop): route notification clicks to the window showing the agent - #4131

Open
cleiter wants to merge 1 commit into
getpaseo:mainfrom
cleiter:route-notification-click-to-right-window
Open

cleiter wants to merge 1 commit into
getpaseo:mainfrom
cleiter:route-notification-click-to-right-window

Conversation

@cleiter

@cleiter cleiter commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Type of change

  • Bug fix

Reasoning

With two desktop windows open against the same daemon, filtering the sidebar to different projects in each, a notification click could pull a workspace from the wrong window's project into view. Every connected window posts its own OS notification banner for the same daemon event, and a click always focused the window that sent the banner, not the window that actually showed the target agent. Whichever duplicate banner you happened to click decided where the app jumped.

Goals

  • A notification click opens the agent in the window that already shows it: visible in a pane, on that workspace's route, or listed in that window's sidebar. Not the window that happened to post the banner.
  • paseo:// deep links use the same window lookup as notification clicks, so both entry points pick the same window.
  • When no open window shows the target, fall back to today's behavior (focused window, or create one), so nothing regresses when the new per-window reports are unavailable, such as an old renderer build or dev-server drift.
  • When more than one window shows the same target, the most recently focused window wins.

Non-goals

  • Duplicate banners: each window still posts its own OS banner for the same daemon event, so two open windows still show two banners and two sounds for one event. Deduping needs its own key, TTL cache, and tests, and does not change where a click lands once routing is by ownership.
  • The shared sidebar-view storage key: two windows still share one localStorage key for sidebar view state, so the last writer wins and a reloading window can inherit the other window's filters. This is a real, separate bug, not caused by this change.

QA

Tests:

  • packages/desktop/src/window/workspace-route-url.test.ts (new) - parses packaged and dev-server workspace route URLs, decodes percent-encoded ids, and rejects non-workspace or malformed URLs.
  • packages/desktop/src/agent-navigation.test.ts (extended) - the tiered windowsShowingTarget ranking (visible agent, URL, sidebar key), the most-recently-focused tie-break, no match, and that windowLoading/removeWindow clear a window's recorded view.
  • packages/desktop/src/features/notification-click.test.ts (new) - route resolution from notification data, and the fallback to the sending window when no candidate matches.
  • packages/desktop/src/window/desktop-window-owner.test.ts (extended) - preferredWindow wins over the default focus order, and the existing fallback still applies when it returns null.
  • packages/app/src/desktop/window-view-report.test.ts (new) - the report payload's sort and dedupe, and that an inactive sidebar reports no workspace keys.

Ran two desktop windows against the same daemon for several days of normal work, each filtered to a different project. Before this change, clicking a notification could bring an agent from one window's project into the other window's Working list. After this change, notification clicks landed in the correct window throughout normal daily use. Did not separately exercise the deep-link path or the close-window fallback path by hand; those are covered by the unit tests above.

The additions to workspace-screen.tsx and sidebar-model.tsx are additive effects that publish derived view state to a new store. They do not change existing render or filtering logic.

Desktop-only change (Electron). Not applicable: iOS, Android, web browser.

Checklist

  • One focused change
  • npm run typecheck passes
  • npm run lint passes
  • npm run format passes
  • QA evidence
  • Tests added or updated where it made sense

With two windows open against the same daemon, every window posts its
own banner for the same event. A click always focuses the window that
sent the banner. It does not focus the window that shows the target
agent. A workspace from one window's project could then appear in
another window's Working list.

The fix gives the main process a view of each window. Each window
reports its visible agent ids. While the sidebar is active, it also
reports the sidebar's workspace keys. A new lookup in
AgentNavigationInbox ranks windows for a target: first by visible
agent, then by the window's own URL, then by sidebar key. The
window's URL already tracks in-app navigation, so this needs no extra
report. The most recently focused window wins a tie. The notification
click handler and openOrFocusAgent, which handles deep links, both
use this lookup and fall back to today's focused-window order when no
window matches.

Duplicate banners and the shared sidebar-view storage key are
separate, known issues. They stay out of scope here.
@cleiter
cleiter force-pushed the route-notification-click-to-right-window branch from 6c49b63 to 613f178 Compare September 2, 2026 12:48
@greptile-apps

greptile-apps Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds per-window view reporting so notification clicks and agent deep links prefer the most recently focused window already displaying the target, while retaining the existing fallback behavior.

  • Publishes visible agents and sidebar workspace keys from each renderer through a validated Electron IPC boundary.
  • Ranks windows by visible agent, current workspace route, and visible sidebar workspace.
  • Applies the selected window to notification clicks and agent deep links, with lifecycle cleanup and focused-window tie-breaking.
  • Adds focused unit coverage for reporting, URL parsing, ranking, notification routing, and window ownership.

Confidence Score: 5/5

The PR appears safe to merge with no concrete blocking or independently actionable non-blocking issues identified.

The renderer report lifecycle, IPC validation, target ranking, fallback behavior, and notification/deep-link delivery remain aligned, and the investigated edge cases did not expose a reachable changed-code failure.

Important Files Changed

Filename Overview
packages/desktop/src/agent-navigation.ts Adds per-window reports, focus ordering, lifecycle cleanup, and tiered target-window ranking.
packages/desktop/src/main.ts Validates renderer view reports and integrates ranked window selection into notifications and deep links.
packages/desktop/src/features/notifications.ts Routes notification clicks through the ranked candidate source while preserving sender fallback.
packages/app/src/desktop/use-report-window-view.ts Combines renderer view state into deterministic reports and suppresses unchanged IPC updates.
packages/app/src/stores/desktop-window-view-store.ts Introduces non-persisted per-window state for visible agents and sidebar workspaces.
packages/desktop/src/window/workspace-route-url.ts Parses packaged and development workspace URLs for current-route ownership matching.

Sequence Diagram

sequenceDiagram
  participant Renderer as Renderer window
  participant Main as Electron main
  participant Inbox as Navigation inbox
  participant OS as OS notification/deep link
  Renderer->>Main: reportView(visible agents, workspace keys)
  Main->>Inbox: setWindowView(windowId, report)
  OS->>Main: notification click or deep link
  Main->>Inbox: windowsShowingTarget(target, window URLs)
  Inbox-->>Main: ranked matching windows
  Main->>Renderer: focus selected window and deliver target
  Note over Main,Renderer: If no window matches, retain focused/sending-window fallback
Loading

Reviews (1): Last reviewed commit: "fix(desktop): route notification clicks ..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant