Skip to content

Add notification ding when agent needs attention#2373

Open
D3OXY wants to merge 4 commits into
pingdotgg:mainfrom
D3OXY:feat/notification-ding
Open

Add notification ding when agent needs attention#2373
D3OXY wants to merge 4 commits into
pingdotgg:mainfrom
D3OXY:feat/notification-ding

Conversation

@D3OXY

@D3OXY D3OXY commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

What Changed

Adds an opt-in audible ding that plays when an agent needs the user's attention.

Important

This is strictly an in-app sound effectnot a desktop notification, not a native OS notification, not a popup or toast. There's no permission flow, no system tray icon, no notification center entry. It just plays a short bell through the page's audio output.

New per-device (ClientSettings) controls in a new Notifications section in Settings → General:

  • Notification sound — master toggle + a Play sound preview button
  • When agent finishes — fires on turn completed / error / interrupted
  • When approval requested — fires when the agent asks to run a command, edit a file, or proposes a plan
  • When question asked — fires when the agent asks a clarifying question
  • Play sound when — focus rule: Always / Window not focused / Window not focused or viewing a different thread

Behaviour:

  • All toggles default to off — fully opt-in.
  • Rising-edge only — no double-dings if another approval lands while one is already pending.
  • Throttled to one ding per 5 seconds across all event types and threads.
  • Archived threads suppressed.
  • Silent-fail + console.warn if browser autoplay is blocked or the audio asset fails to load. The Play sound preview button surfaces errors via toast since it's an explicit user action.

Why

When an agent runs a long task, it's common to switch to another window or another thread while waiting. Without an audible cue, you keep glancing back to check if it's finished or needs an approval. A short ding closes that loop without hijacking attention the way a popup or system notification would.

Keeping it as an in-app sound (rather than the Notifications API) avoids the permission prompt, works identically across browser and Electron, and stays scoped to the app — the user is always in T3 Code when they hear it.

UI Changes

New Notifications section in Settings → General, between General and Providers:

image

The bell sound that plays (a short servant-bell ring):

notification.mp3

Implementation notes

  • Pure derivation in apps/web/src/notificationSound.ts:
    • deriveNotificationTriggers(prevShells, nextShells, events) — rising-edge detection on latestTurn.state, hasPendingApprovals || hasActionableProposedPlan, hasPendingUserInput. Skips archived threads. Requires prev to exist (no spurious dings on initial bootstrap).
    • shouldPlay(triggers, settings, focusContext, nowMs, lastPlayAtMs) — applies sub-toggle filters, focus rule, and 5s throttle.
    • Both fully unit-tested (28 tests in notificationSound.test.ts).
  • Side-effecting notificationSoundManager singleton owns a single HTMLAudioElement for /sounds/notification.mp3 (~54 KB MP3 bundled in apps/web/public/sounds/). Lazy-initialized; SSR-safe guards on document / window access.
  • Wired into apps/web/src/environments/runtime/service.ts at both applyRecoveredEventBatch and applyShellEvent paths so server-pushed shell upserts (the common case for fresh approvals / turn-end) reliably trigger.
  • Current-thread accessor wired via useLocation().pathname in __root.tsx; the manager itself is non-React and exposes a setCurrentThreadAccessor injection point.
  • Schema additions in packages/contracts/src/settings.ts use the existing Schema.withDecodingDefault pattern; ClientSettingsPatch updated in lockstep. Backward-compatible — missing fields decode to defaults.
  • New settings included in useSettingsRestore so "Restore defaults" wipes them.

Verification

  • bun fmt — clean
  • bun lint — 0 errors (pre-existing warnings unrelated)
  • bun typecheck — clean for @t3tools/contracts and @t3tools/web
  • bun run test — 998/998 pass (28 new in notificationSound.test.ts)

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Add notification sound when agent needs attention

  • Adds a notificationSoundManager singleton (notificationSound.ts) that plays an audio chime when thread state transitions occur: agent turn ends, approval requested, or question asked.
  • Introduces deriveNotificationTriggers to diff previous and current thread shell maps and shouldPlay to enforce user settings, focus rules, and a 5-second throttle.
  • Mounts a NotificationSoundBootstrap component in the root app shell (__root.tsx) to wire thread state changes to sound playback for authenticated users.
  • Adds five new client settings fields (notificationSoundEnabled, notificationSoundOnTurnEnd, notificationSoundOnApproval, notificationSoundOnQuestion, notificationSoundFocusRule) with a Settings UI section including per-scenario toggles, a focus rule selector, and a test-sound button.
  • Behavioral Change: notification sounds are skipped on the initial bootstrap run to avoid spurious dings on page load.

Macroscope summarized e962104.

@coderabbitai

coderabbitai Bot commented Apr 27, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 393c40ef-f2c4-491f-b1f6-c793a6420b8c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the size:XL 500-999 changed lines (additions + deletions). label Apr 27, 2026
@macroscopeapp

macroscopeapp Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

New feature introducing notification sounds with audio playback, settings UI, and state monitoring. While well-implemented with tests and disabled by default, this adds substantial new user-facing capability that benefits from human review.

You can customize Macroscope's approvability policy. Learn more.

@D3OXY

D3OXY commented Apr 28, 2026

Copy link
Copy Markdown
Contributor Author

@juliusmarminge Could you take a look at this when you have the time? Thanks! It’s off by default, doesn’t use any OS level notification APIs. Just a Ding. It should work across all the environments.

@github-actions github-actions Bot added the vouch:unvouched PR author is not yet trusted in the VOUCHED list. label May 3, 2026
@D3OXY D3OXY force-pushed the feat/notification-ding branch 2 times, most recently from 19959e3 to 71b56d0 Compare June 23, 2026 19:39
D3OXY added 4 commits June 27, 2026 19:45
Opt-in audible ding (not a desktop/OS notification or popup) that plays
when an agent finishes a turn, requests approval, or asks a question.
Per-device settings with focus-aware playback and a 5s throttle. New
"Notifications" section in Settings with master toggle, three event
sub-toggles, focus rule, and a Play Sound preview button.
`latestTurn.state` is not a reliable end-of-turn signal: the projector
flips it to "completed" on every `thread.turn-diff-completed` event,
which fires mid-turn whenever a checkpoint is captured for a git repo
(see ProviderRuntimeIngestion turn.diff.updated handling). The next
session-set running event then flips it back, producing
running -> completed -> running oscillations through a single turn and
spurious notification dings on the first mid-turn diff capture.

Switch turn-end detection to `session.orchestrationStatus`, which the
provider keeps as "running" continuously through tool calls and only
transitions out at actual turn end. "starting" is treated as still
active so session restarts/resumes mid-turn don't trigger.
@D3OXY D3OXY force-pushed the feat/notification-ding branch from 71b56d0 to e962104 Compare June 27, 2026 14:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL 500-999 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant