You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When an agent needs your attention, exactly one banner fires carrying the agent's actual message. Click it → the originating tab and pane is focused. You never miss the signal. You never get noise.
That's it. That's the bar. This issue tracks getting from "current state" to that bar across all the small bugs and missing pieces that prevent it from being true today.
One sink. Every notification flows through NotificationManager.notifyAgentAttention. No parallel code paths that can drift.
Suppress when redundant. Active focus → no banner. Mute → no banner. Already-shown-in-the-grace-window → no banner.
Surface when invisible. When the user isn't watching, fire — reliably, with the agent's actual message, with a click that gets them back to the right place.
Audible only via the OS. No app-side chime; the OS controls notification sound (and respects Focus / DND). (Achieved by the sound-drop commit.)
Rationale for ordering Phase B before Phase C: all three are cheap, well-scoped, testable without running the full app. They tighten the OSC path so when we replace the click handler in Phase C, the rest of the system is already correct. Doing Phase C first would mean re-validating these three after the bigger rewrite.
The big one. Substantial Rust work (objc2 + delegate retention + authorization flow). Needs iterative testing in the running app. Once this lands, click-from-NC-after-dismissal works for the first time ever.
Phase D — Catch up on missed events
After Phase C, the click path is reliable. Now help the user see what they missed.
These two are loosely coupled — could be parallel. #554 has higher per-effort impact (the tray solves a real "what did I miss" problem); #553 is more polish.
Needs a call on whether we want this at all (some users hate completion notifications), and if yes whether opt-in. See the issue for the full design discussion (PID-based vs OSC 133 shell integration).
What "done" looks like
Acceptance for the full vision (achieved when all of B + C + D ship):
✅ Claude OSC 9;2 on background tab → exactly one banner with the agent's message
✅ No banner for any non-agent event (server, error, completion of generic commands unless Phase E ships)
⏳ Click banner (live or in NC after dismissal) → focuses the correct tab + correct pane
⏳ Multi-pane tab: only the focused pane suppresses
✅ Muted tab → silent both surfaces
⏳ Permission-denied → surfaced to user with path to fix
⏳ Banner storms throttled per-tab
⏳ Missed banners recoverable via in-app tray
⏳ Settings discoverable without editing JSON
Sequencing summary
Phase A (done)
│
▼
Phase B ── #549 ── #550 ── #551 (cheap parallel-friendly fixes)
│
▼
Phase C ── #548 (the hard one — iterative testing needed)
│
▼
Phase D ── #554 ── #553 (quality-of-life)
│
▼
Phase E ── #552 (only if we decide we want it)
Notes
All phases preserve the one-producer, one-sink rule. New features add behavior on top of notifyAgentAttention, never new parallel paths.
Each sub-issue is self-contained and can be picked up independently. The phase grouping is a recommendation, not a hard dependency (except Phase C → Phase D, where the tray UX is much better if click is reliable).
macOS-only scope throughout — matches the rest of Clawterm's current targeting.
Goal
When an agent needs your attention, exactly one banner fires carrying the agent's actual message. Click it → the originating tab and pane is focused. You never miss the signal. You never get noise.
That's it. That's the bar. This issue tracks getting from "current state" to that bar across all the small bugs and missing pieces that prevent it from being true today.
North-star principles
NotificationManager.notifyAgentAttention. No parallel code paths that can drift.What's solid today
After #547 and the follow-up sound drop:
notifyAgentAttention)Local: http://localhost:5173or any matcher eventWhat's still wrong, ranked by impact
UNUserNotificationCenterImplementation plan — phased so each phase ships independently
✅ Phase A — Foundations (done)
08ae5a0)Phase B — Tighten the OSC path (the cheap wins)
Three small fixes, each <100 lines, each independently mergeable:
PanethroughonOscNotification; check focused pane, not just active tab.tabId → lastSentAt, drop duplicates within window.Rationale for ordering Phase B before Phase C: all three are cheap, well-scoped, testable without running the full app. They tighten the OSC path so when we replace the click handler in Phase C, the rest of the system is already correct. Doing Phase C first would mean re-validating these three after the bigger rewrite.
Phase C — Reliable click from Notification Center
UNUserNotificationCenterRust plugin. Owns the delegate; emitsnotification-clickedTauri events withtab_idpayload.The big one. Substantial Rust work (objc2 + delegate retention + authorization flow). Needs iterative testing in the running app. Once this lands, click-from-NC-after-dismissal works for the first time ever.
Phase D — Catch up on missed events
After Phase C, the click path is reliable. Now help the user see what they missed.
These two are loosely coupled — could be parallel. #554 has higher per-effort impact (the tray solves a real "what did I miss" problem); #553 is more polish.
Phase E — Optional, requires product decision
Needs a call on whether we want this at all (some users hate completion notifications), and if yes whether opt-in. See the issue for the full design discussion (PID-based vs OSC 133 shell integration).
What "done" looks like
Acceptance for the full vision (achieved when all of B + C + D ship):
Sequencing summary
Notes
notifyAgentAttention, never new parallel paths.