Skip to content

feat(notifications): OS notification when a background agent session finishes#155

Open
liam-russell wants to merge 2 commits into
mainfrom
feat/agent-session-notifications
Open

feat(notifications): OS notification when a background agent session finishes#155
liam-russell wants to merge 2 commits into
mainfrom
feat/agent-session-notifications

Conversation

@liam-russell

Copy link
Copy Markdown
Contributor

Type

  • feat — new feature or user-visible capability

Summary

  • SproutGit now shows a native OS notification when a background worktree's agent session finishes or goes idle, so you don't have to keep checking terminals you're not actively watching.
  • Clicking the notification brings the app to the front and jumps straight to that worktree's terminal.
  • Added a "Notifications" section in Settings to turn this off if you don't want it.

Why

Closes #92. Users running agents across several worktrees at once had no signal when a background session finished — issue #92 called this out as a differentiator, building on the session-status plumbing added for the agent sessions dashboard (#96).

Screenshots / recordings

New "Notifications" section in Settings (right column, below "Default Shell"):

┌─ 🔔 Notifications ──────────────────────────────────────┐
│  Agent session finished              [x] Enabled        │
│  Show an OS notification when a background               │
│  worktree's agent session finishes or goes idle.          │
└────────────────────────────────────────────────────────┘

This environment is a shared multi-agent host without a working Electron
debug/CDP target I could safely attach to for this change (the project's
.claude/launch.json at the repo root is currently owned by another
concurrent agent's dev server), so I wasn't able to capture an actual
screenshot. The component reuses the exact checkbox/label/section markup
already used by McpSection/ShellSection in Settings, verified by
reading the rendered JSX and cross-checking Tailwind classes against the
existing sections.

Manual verification steps for a reviewer:

  1. pnpm dev
  2. Open Settings → the new "Notifications" section appears in the right column, below "Default Shell".
  3. Toggle it off/on — the label switches between "Enabled"/"Disabled" and persists across an app restart.
  4. Launch an agent in one worktree, switch to a different worktree/tab (or unfocus the app window), and let the agent finish or go quiet for ~20s — an OS notification should appear; clicking it should focus the app and switch to that worktree's terminal tab.

Breaking changes

None. New IPC channels (notification:show, event:agentSessionStatus, event:notificationClicked) are additive; TerminalManagerWithMeta's constructor gained two new optional parameters.

Test plan

  • Added unit tests for the new idle-detection logic:
    • packages/terminal/src/__tests__/idle-tracker.test.ts — the IdleTracker class (touch/remove/checkIdle transitions, re-arming after new activity).
    • packages/terminal/src/__tests__/select-idle-agent-sessions.test.ts — filtering idle sessions down to agent-launched ones only.
  • E2E was skipped per project convention — OS-level notifications aren't practical to assert on in the WebdriverIO/Electron E2E suite.
  • pnpm lint && pnpm typecheck pass cleanly (verified via the pre-commit hook and standalone runs).
  • pnpm test: the new/changed packages (@sproutgit/terminal, @sproutgit/types) pass consistently, including a full clean run. Some pre-existing tests in packages/git and app (e.g. remote.test.ts, tool-test.test.ts, worktree-lifecycle.test.ts) intermittently failed locally with EAGAIN/EPIPE fork errors from heavy concurrent load on this shared multi-agent host — not from this diff (none of those files were touched, the failing subset changed between runs, and they pass individually when the host isn't under load). Recommend relying on CI for the definitive signal there.

…finishes

Detects agent session exit and output-idle (via a new IdleTracker in
TerminalManager) and, when the affected worktree isn't in view, shows a
native OS notification that jumps back to the session on click. Adds a
Settings toggle to opt out.

Closes #92
Copilot AI review requested due to automatic review settings July 6, 2026 11:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds native OS notifications for background agent sessions finishing or going idle, including click-to-focus/jump behavior and a Settings toggle so users can disable the feature.

Changes:

  • Introduces an idle-detection mechanism in the terminal manager and emits agent-session status events (idle/exited).
  • Adds renderer-side notification policy + OS notification IPC handler + click event to jump back to the session.
  • Adds a Settings “Notifications” section and unit tests for the idle tracking/filtering logic.

Reviewed changes

Copilot reviewed 18 out of 19 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pnpm-lock.yaml Locks the new Vitest dependency resolution.
packages/types/src/notifications.ts Adds shared IPC payload types for session status + notifications.
packages/types/src/ipc.ts Adds IPC channel constants and the notification:show invoke contract.
packages/types/src/index.ts Re-exports the new notifications types.
packages/terminal/src/terminal-manager.ts Adds idle tracking hooks, session metadata type, and idle polling/reporting.
packages/terminal/src/index.ts Exports idle tracker utilities/types from the terminal package.
packages/terminal/src/idle-tracker.ts Implements IdleTracker (last-activity + one-shot idle reporting).
packages/terminal/src/tests/select-idle-agent-sessions.test.ts Tests filtering idle sessions down to agent-launched ones.
packages/terminal/src/tests/idle-tracker.test.ts Tests IdleTracker idle transition and re-arming behavior.
packages/terminal/package.json Adds Vitest scripts and devDependency for the terminal package tests.
app/src/renderer/settings/NotificationsSection.tsx Adds Settings UI to enable/disable agent session notifications.
app/src/renderer/routes/workspace.tsx Wires the notifications hook and reuses the jump-to-session handler.
app/src/renderer/routes/settings.tsx Renders the new Notifications settings section.
app/src/renderer/hooks/useAgentSessionNotifications.ts Implements renderer-side policy + request to show OS notifications + click handling.
app/src/renderer/agent-session-notification-settings.ts Persists the enable/disable setting via existing settings IPC.
app/src/preload/index.ts Exposes new notification/status APIs and events to the renderer.
app/src/main/ipc/terminal.ts Emits agent session status events on exit/idle from main to renderer.
app/src/main/ipc/notifications.ts Implements notification:show using Electron Notification and click-to-jump event.
app/src/main/index.ts Registers the new notification IPC handlers during app startup.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread packages/terminal/src/terminal-manager.ts Outdated
Comment on lines +14 to +19
useEffect(() => {
void loadAgentSessionNotificationsEnabled().then(value => {
setEnabled(value);
setLoaded(true);
});
}, []);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5d8e483 — the load now always flips loaded to true via .finally() and surfaces a toast on failure instead of leaving the checkbox stuck disabled.

Comment on lines +21 to +29
async function toggle() {
const next = !enabled;
setEnabled(next);
try {
await saveAgentSessionNotificationsEnabled(next);
} catch (err) {
onToast(String(err), 'error');
}
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5d8e483 — reverts enabled back to the previous value if the save fails, so the UI doesn't desync from the persisted setting.

worktreePath: event.cwd,
terminalId: event.id,
});
})();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5d8e483 — the async IIFE now has a .catch that logs the error instead of letting it become an unhandled rejection.

- Match handleSessionData's override signature to the base class for clarity
- Surface load/save errors in NotificationsSection instead of silently
  leaving the checkbox disabled or the UI desynced from the stored setting
- Log (not swallow) failures inside the agent-session-status handler
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.

OS notification when a background agent session finishes

2 participants