---
title: Notifications & Silence (when Omniscio is allowed to interrupt you)
---

# Notifications & Silence (mute, sounds, post-send nav)

## What it is

### What it is

Notifications in Omniscio are the part of the app that yanks you back in when an agent needs you. They show up as native OS toasts, optional chimes (each of three alert types has its own sound: _needs-you_, _stalled_, _error_), and — on mobile — browser push. You can mute globally for a fixed duration or indefinitely, mute specific projects, tune _when_ OS toasts appear (always, only when Omniscio is backgrounded, or never), pick built-in or custom `.wav`/`.mp3`/`.ogg` sounds up to 5 MB, and set a separate volume slider for app sounds vs OS-level notification behavior. Once you've sent a reply, **post-send navigation** decides where you go next. It ships set to **stay on the session you just replied to** (changed 2026-08-24 — advancing by default surprised people working inside a session), but it is a priority ladder you can point at the next session that needs attention if you triage a queue front-to-back.

## Where to find it

**Settings → Notifications** — one pane holds the whole picture, including the **Recent chimes** log of what played and what was silenced.

## How it behaves

### How to use it

1. **Find the settings.** Open Settings → **Notifications**. Everything lives in one pane.
2. **Control when OS toasts appear.** The **Notification behavior** radio set is three-way: **Always** (even when Omniscio is focused), **Only when backgrounded** (the common sweet spot), or **Never** (rely on sounds + in-app toasts only).
3. **Mute temporarily.** Use the **Silence until** picker — choose a preset like "1 hour" / "tomorrow 9 am" / "indefinite", or pick a custom timestamp. You'll stop getting toasts and sounds until that time. Clear it to resume. You can also mute specific projects in the **Per-project mutes** list.
   - **One-click toggle from the header.** The **bell icon in the header toolbar** flips indefinite silence on/off in a single click (a struck-through bell means muted). If you've unpinned the bell so it lives in the header's **"…" overflow menu**, clicking the **Notifications** row there does the same thing — you'll get a brief "Notifications muted" / "Notifications unmuted" toast since the menu can't show the bell's icon state.
4. **Customize sounds.** Flip **Notification sounds enabled** on to get chimes (plays regardless of OS toast behavior — so "Never" + sounds on still audibly alerts you). Each of the three alert categories has its own picker: built-in (alert-ping, attention, classic-ding, digital-tone, gentle-chime, mission-alert, soft-bell, subtle-pop), your OS system sounds, or a custom file you upload. Slider sets volume 0–100%.
5. **Tune post-send navigation.** Two settings control where you land after sending a reply: **Post-send behavior (project mode)** — which now ships as just _stay on this session_, and can be built back up into a ladder (_next attention session in this project → inbox → waiting room_) — and **Post-send behavior (inbox mode)** — _next attention session → waiting room_. Match these to how you triage: if you work a queue front-to-back, add the next-session steps; if you deep-focus in one session, the shipped _stay_ is already right.
6. **Mobile push is separate.** **Mobile sound enabled** and **Web push enabled** control the phone experience independently — handy if you want quiet on the desktop but a chime on the phone when you step away.
7. **Hide content in mobile notifications (privacy).** **Hide content in mobile notifications** is **ON by default** — the privacy-safe posture, so your phone's lock screen and the push provider that delivers the notification never see the actual message. With it on, an incoming text / email / session alert arrives as a generic "New message" / "New email" / "Session needs you" instead of the sender, subject, body, or session name; tapping it still opens the right conversation on the phone. Turn it off if you'd rather keep the at-a-glance preview (sender, subject, body) on your phone.
8. **Turn the app-icon badge on or off.** Your app icon (Windows taskbar / macOS dock) shows a small count that mirrors your **Inbox** — the same number shown on the in-app Inbox (every inbox item, not just sessions that need you). **App icon attention count** (default on) controls it: turn it off for a plain icon with no badge, and the badge updates immediately (no restart). Focus Mode and Presentation Mode already clamp the badge to 0 while they're on. There's also a one-tap **Feature Discovery Nudge** that offers to flip this right from your inbox — a live on/off switch in the card.

### In-app toast preferences (the unified toast system)

Separate from the OS-toast controls above, the **In-app notifications** card in
Settings → Notifications controls the small pop-ups Omniscio shows _inside_ the app. They
are grouped so you tune them by kind, not one at a time:

- **Always on** (Errors, Warnings) — shown a small "Always on" label, no switch. The
  toast system's **severity gate** renders every error/warning regardless of any
  preference, so "turn off notifications" can never silence "Your message failed to
  send."
- **Confirmations** (settings saved, project/session/recipe/deploy actions, screenshot
  captured) — receipts for actions whose result you can already see. The **"Show
  confirmation toasts"** master switch at the top flips this whole group on/off in one
  click, or toggle just one — e.g. **Screenshot captured** silences the "Screenshot saved
  and copied to clipboard" pop-up while capture failures still show. To keep first-run
  quiet, the lowest-value receipts — **Settings confirmations** and **Project changes** —
  default **off** (the change is already visible in the panel / sidebar); flip them on if
  you want the confirmation.
- **Deletions** and **Notices** (copies, account activity, budget/usage, status, voice,
  and other one-off results) — per-category toggles. Two deliberate default choices here:
  the highest-frequency, lowest-value receipt — **Clipboard copies** — defaults **off** to
  cut out-of-box noise, while **Status alerts** (a session **errored / stalled**) default
  **on**, so the in-app toast you'd most expect for "my session broke" isn't silent. Its
  sibling **Attention alerts** (a session **needs your input**) stays **off** by default —
  it fires far more often, so it's opt-in.

Below the toggles, the **Error/warning display time** dropdown controls how long
error and warning toasts stay on screen before auto-dismissing. Preset options are
5, 8, 10, 15, and 30 seconds; the default is 8 seconds (matching the previous
hard-coded duration). Success and info toasts are unaffected — they always use the
fixed 5-second (desktop) / 2-second (mobile) duration. If a caller passes an
explicit duration when creating a toast, it overrides this setting.

Below that, the **Recent notifications** inspector is a live, newest-first log of
what just popped up (in-memory, resets on restart). Each row shows the category, how long
ago it fired, and the message, with a one-click **mute** that turns that category off on
the spot — always-on rows show "Always on" instead. It is read-only; it never fires a
toast itself.

Under the hood every in-app toast flows through one gated entry point, `notify()`, so the
severity gate and per-category preferences can't be bypassed. Full invariants:
[/.claude/memory/contracts/unified-toast-system-contract.md](/.claude/memory/contracts/unified-toast-system-contract.md).

**Desktop pop-ups have one funnel too.** Every OS notification goes through the notification
service, so **Silence all**, "Never" and Focus Mode apply to all of them — including an
automation's own **Notify me** action and its **Summarize → notification** destination, which
until 2026-09-28 popped up even while everything was silenced (the run log now says when your
quiet settings held one back). Because nothing else in the app shows that notice, when only the
desktop pop-up is off — "Never", or Omniscio is in front under "Only when backgrounded" — it
appears as a message inside the app instead, and it plays the app's notification sound under your
sound settings. The only pop-ups outside the funnel are three receipts for
something you just did yourself: opening the app a second time, a Quick Launch that failed to
start, and a screenshot you just took. A build check fails if any other code builds a raw pop-up.

**On mobile, in-app toasts never cover the chat box.** The moment you focus a text box and
the on-screen keyboard comes up, the toast stack moves to the **top** of the screen — so a
notification never covers what you're typing into or hides behind the keyboard. And whenever
the chat box is on screen with the keyboard down, notifications float just **above** it
instead of landing on top of it — dropping to the usual bottom spot only when there's no chat
box in view. Desktop is unaffected. Behind the scenes:
[/.claude/memory/contracts/keyboard-viewport-contract.md](/.claude/memory/contracts/keyboard-viewport-contract.md).

### Pipeline lane silence

Sessions spawned by a recipe's `pipeline-expansion` step (one per lane × per pipeline stage) are flagged `is_pipeline_lane=true` and never fire a notification — chime, OS toast, or mobile push. This is unconditional: it ignores **Silence until**, ignores per-project mutes, ignores the `showRecipeLaneSessions` visibility toggle (which controls whether the lanes appear in the sidebar at all), and is independent of the `alert_armed` per-session dedup. The orchestrator session itself and any mainline steps you authored (consolidate, approve, repair) still notify normally — you hear the run finish or an approval ask, you just don't hear each of the 132 audit lanes ping individually.

### Per-session alert dedup

> **Errored / stalled sessions no longer chime (updated 2026-08-11, owner rule).** **Error** and **Stalled** were reclassified as non-attention, so they now raise **no chime, no OS notification, and no inbox row** — they go quiet in the sidebar's Interrupted group. Only **Needs You** chimes now (a genuine give-up still surfaces + chimes as `needs_you` "Recovery failed"). The dedup rules below still apply to the Needs You chime; the mentions of Stalled / Error describe the pre-2026-08-11 behavior.

When a session fires a **Needs You** / **Stalled** / **Error** alert, it immediately disarms itself — that _same session_ won't fire another alert (chime or OS toast) until it is **re-armed**. Other sessions are unaffected: if Session A is disarmed and Session B hits Needs You, B fires normally.

The goal is **one chime per arrival into an attention state** — you hear it each time a session genuinely _becomes_ Needs You / Stalled / Error, but a session already sitting in your inbox doesn't keep pinging.

**What re-arms a session** — two triggers, either one is enough:

1. **A genuine transition back into an attention state.** When a session leaves attention (e.g. you reply and it returns to _running_, or auto-continue picks it up) and later transitions _into_ Needs You / Stalled / Error again, that re-entry re-arms it — so the next arrival chimes. This is what makes the chime fire "every time something finishes," not just the first time after a reply.
2. **A human-authored reply** via `IPC.SESSION_SEND_RESPONSE` — the composer, question-widget answers, plan approvals, snippet sends, and mobile WebSocket sends.

**What does NOT re-arm**: churn _within_ attention (e.g. Needs You → Error on the same session, or a repeated Needs You with no intervening non-attention status) — the session was already alerted, so mid-stream status changes don't re-chime. Passive actions don't re-arm either: opening / viewing the session, clicking the OS toast, or a Send Later delivery. (Clicking Continue or auto-continue doesn't re-arm by itself, but the session's later transition back into attention will.)

**What is NOT gated**: mobile push (the phone alerts always come through, intentional design), the visible amber sidebar dot (the session still shows in attention lists), and badge counts.

**Transient process blips stay quiet**: if a session briefly loses its underlying process and Omniscio automatically restarts it (common under heavy load), it flips red for a moment but does **not** chime — the error sound is saved for a failure that actually sticks. The same holds when a session is **rehydrating after Omniscio itself restarts** (an app crash + auto-restart): while it's being brought back it can flicker into an error state, but that self-healing blip is silenced too, so you never hear a chime you can't find in your inbox. You still see it in the sidebar; you just won't get a false alarm for something that fixes itself on its own.

**A scheduled (cron) job that hits a usage limit stays quiet too**: when a cron job fails specifically because it ran into a Claude usage or weekly-rate limit — a benign capacity condition that clears on its own once limits reset — Omniscio treats it as a capacity park, not an alarm. It plays **no error chime** (and Omniscio won't auto-spawn a fix-it session that would just hit the same limit), but the failure still shows its card in your inbox with the real reason, so you can see it without being jolted by a sound for something you can't act on. An ordinary cron failure (a real bug) still chimes as before.

**Persistence**: the armed/disarmed state is stored on the session row (`alert_armed` column, schema v96) so it survives app restart.

**Full contract**: the arm/disarm writers, the seven-gate fire cascade (pipeline-lane → snooze → recovery-in-flight → arm → overlay → Focus Mode → fire), and the test-locked invariants live in [/.claude/memory/contracts/notification-chime-contract.md](/.claude/memory/contracts/notification-chime-contract.md) — read it before changing any chime behavior. The **recovery-in-flight** gate is what silences the transient blips above: a session being auto-recovered (an app-restart resume episode, a rate-limit auto-switch, an auto-resumed suspend, or a lost-contact revive) yields the chime while it self-heals, mirroring the fact that the inbox already hides it (invariant **I10**).

### Global per-minute sound cap

Separate from the per-session dedup above, a **global hard cap** limits how many notification sounds of **any** variety play per rolling 60 seconds — across all sessions and all three alert types combined (currently **1 per minute**). Where the per-session dedup stops _one_ session from re-pinging, this stops a _storm_ of many sessions from becoming a barrage: if 30 sessions hit Needs You at once, you hear at most one chime that minute, not thirty.

It's enforced in the background process — the single function every notification sound flows through — so it survives a window reload (the on-screen cooldown doesn't), and it caps **audio only**: every session still turns its sidebar dot amber/red and still appears in your attention lists, only the sound is throttled. Scheduled **alarms**, **read-aloud (TTS)**, dictation beeps, and the **sound-preview** buttons in Settings use separate audio paths and are never capped.

Locked as invariant **I9** in [/.claude/memory/contracts/notification-chime-contract.md](/.claude/memory/contracts/notification-chime-contract.md).

### On-screen quiet (when you're already looking)

Two smaller, on-screen rules sit in front of the actual sound — separate from the per-minute cap, and they only affect the _desktop_ chime:

- **You're actively working the Inbox (15 seconds).** If you've just _clicked, typed, scrolled, or tapped_ in the Inbox, a new chime stays quiet for 15 seconds — you're already looking, so the ping would be redundant. This counts only real interactions: **moving the mouse or bringing the window to the front does _not_ count.** (A fix in Aug 2026 — before it, a cursor drifting over the Inbox could swallow every needs-you chime while the history still claimed "Played.")
- **Back-to-back cooldown (10 seconds).** Two chimes within 10 seconds collapse to one. Under the 1-per-minute cap this rarely comes up; it's a last-resort debounce that lives in the window (so it resets if you reload).

Both now tell the truth in **Recent chimes** below: when the desktop drops a chime for either reason, the window reports back what it actually did, so the log records it as **Silenced** (with the reason) instead of a false "Played."

### Recent chimes (chime history)

Heard a chime but not sure what it was? **Settings → Notifications → Recent chimes** is a persistent log of the last chime decisions — every sound that _played_, plus every one that was _silenced_ and why (a self-healing restart blip, a snoozed session, the per-minute cap, or because you were already active in the Inbox). Each row shows the session, the kind (Needs You / Stalled / Error), whether the sound actually played, and how long ago. Unlike the in-app **Recent notifications** toast inspector higher up the pane (in-memory, resets on restart), this **survives restarts** — so a chime you heard minutes ago is always accountable. Turn it off with **Save notification chime history** (on by default); when off it records nothing and never touches the sounds themselves. It is bounded (newest entries kept, older ones pruned) and read-only. Locked as invariant **I11** in [/.claude/memory/contracts/notification-chime-contract.md](/.claude/memory/contracts/notification-chime-contract.md).

## For agents

### How it works

The notification orchestrator is [/src/main/services/notification-service.ts](/src/main/services/notification-service.ts); sound playback and custom-sound file handling live in [/src/main/services/notification-sound-service.ts](/src/main/services/notification-sound-service.ts). Built-in chimes ship as `.wav` files in [/resources/sounds/](/resources/sounds/). The settings pane is [/src/renderer/src/features/settings/sections/notifications/NotificationSettings.tsx](/src/renderer/src/features/settings/sections/notifications/NotificationSettings.tsx), surfacing `notificationBehavior`, `silenceUntil` (stored as ISO timestamp or the literal `'indefinite'`), `mutedProjectIds`, `toastPreferences` (a 19-category, group-bucketed allow-map for in-app toasts — gated by the `notify()` entry point with errors/warnings always shown), `notificationSoundEnabled`, `notificationSoundNeedsYou` / `Stalled` / `Error`, `notificationSoundVolume`, and `mobileSoundEnabled`. Incoming SMS gets its own **`smsNotificationsEnabled`** toggle (default on): incoming-SMS OS notifications pop a toast _and_ play a chime on every text, so `notifySms` gates on this flag — turn it off to quiet SMS without reaching for the blunt global "Never" (which would also kill the session / needs-you / budget notifications you rely on). The desktop app-icon count badge mirrors the unified inbox count (`useInboxAttentionCount`, reported from the main window via `notification:inbox-badge-report` and painted by `resolveBadgeCount`; the session-only `countAttentionVisible` is only the pre-hydration fallback). **On Windows that badge is our own AMBER overlay** — matching the in-app amber inbox count instead of Windows' default red numeric badge — drawn from a theme-coloured image the renderer sends over `notification:inbox-badge-overlay` and set via `setOverlayIcon`; macOS/Linux keep the native numeric badge (their badge colour is OS-controlled). It has its own **`desktopIconBadgeEnabled`** toggle (default on): the shared `badgeSurfaceSilenced()` gate — which BOTH badge inputs route through — clamps the badge to 0 when it is `=== false` (read `=== false`, never `!value`, so an existing install with no saved key defaults ON and never silently loses its badge), and a `settings-apply` side-effect refreshes the badge live + records a `desktop_icon_badge/toggled` usage event whichever surface flips it (the Notifications toggle or the discovery nudge). A separate **`contentFreeMobilePush`** toggle (**default on**, F010 / F048) governs the phone *payload's* privacy rather than whether it fires: by default the desktop-side `deliver()` (mobile-push-service.ts) sends a generic title/body to Google FCM instead of the sender / subject / body / session name, keeping only the deep-link ids so a tap still routes — so no message content leaves the device in the push the provider sees and retains. Turn it off to get the rich preview (sender / subject / body) back in the push payload. Mobile Web Push is a separate **per-device** control — the "Push notifications" toggle reflects and flips THIS device's real push subscription (subscribe / unsubscribe), not a synced setting, so the opt-in banner never nags an already-subscribed phone and each device re-enables on its own; there is no `webPushEnabled` setting (removed — a per-device fact can't be a synced boolean). See [/.claude/memory/contracts/mobile-push-optin-contract.md](/.claude/memory/contracts/mobile-push-optin-contract.md). Silence is a timestamp comparison, not a scheduled quiet-hours engine — there's no "from 10 pm to 7 am" yet; you set a one-shot until-time and it clears itself. Post-send navigation (`postSendBehavior` for project mode, `inboxPostSendBehavior` for inbox) is handled by the session nav resolver in [/src/renderer/src/stores/session-nav-resolver.ts](/src/renderer/src/stores/session-nav-resolver.ts) — the same resolver that handles arrow-key navigation, so the "next attention session" logic is unified. Full decision-tree + click-handler routing: [feature-notifications.md](/.claude/memory/feature-notifications.md).

## Related

### Related

- [snooze-a-session.md](snooze-a-session.md) — snooze silences a single session rather than the whole app
- [session-stuck-in-needs-you.md](session-stuck-in-needs-you.md) — _why_ you got an alert
- [account-pool.md](account-pool.md) — the exhausted-accounts OS notification respects these global controls; the in-app banner does not
- The dedup also gates **Stalled** and **Error** alerts — see `notifyNeedsYou` / `notifyStalled` / `notifyError` in [/src/main/services/notification-service.ts](/src/main/services/notification-service.ts) for the gate

