Notifications & messages
Status bar, desktop/push notifications, and messages.
Status bar
[status_bar]
enabled = true # show a status bar while attached
position = "bottom" # "bottom" or "top"
The status bar shows session name, status, agent type, branch, git status, unread messages, and fleet summary, updating in real time.
In terminal-owned attach, this position also selects the Graith-owned chrome row. With a visible status bar or read-only indicator, Graith reserves that row outside the child PTY viewport when the terminal has at least two rows; one-row terminals suppress the chrome row so the child keeps the line.
Notifications
[notifications]
enabled = true # desktop notifications (status changes AND `gr notify`)
on_stopped = false # notify when a session stops
command = "" # custom notification command (optional)
# Proactive `gr notify` push notifications:
backend = "macos" # "macos" (required helper app) or "command"; default "macos"
max_per_hour = 12 # rolling-hour cap on low/normal pushes (high bypasses)
quiet_hours_start = "22:00" # suppress low/normal pushes in this window (24h "HH:MM")
quiet_hours_end = "07:00" # window may wrap past midnight; high priority bypasses
When command is set, status-change notifications run it via sh -c instead
of the system notification API and pass GRAITH_SESSION_NAME, GRAITH_STATUS,
and GRAITH_MESSAGE. gr notify push notifications use the command only when
backend = "command"; they pass GRAITH_NOTIFY_TITLE,
GRAITH_NOTIFY_MESSAGE, and GRAITH_NOTIFY_PRIORITY.
Proactive push notifications (gr notify)
The orchestrator (and triggers) can proactively get your attention — a morning briefing, a CI failure, a review needed — rather than leaving it silently in an inbox:
gr notify "Morning briefing ready" --priority low
gr notify "CI failing on main after 3 retries" --priority high
Priority levels: low, normal (default), and high. high plays a sound and
bypasses quiet hours and the rate limit; low/normal are subject to both.
Only the orchestrator session and the user can send notifications — plain agent
sessions are rejected to prevent spam. Identical notifications within the
coalesce window (30s by default) are coalesced. Other backends (ntfy,
Pushover, Slack) are planned.
Native macOS notifications
Both default session-status notifications (such as on_stopped) and the
macos push backend prefer a small bundled helper app (GraithNotifier.app,
bundle identifier com.graith.notifier) that posts via
UNUserNotificationCenter. They therefore appear as “Graith” in System
Settings > Notifications, where you can configure their style, sounds, and
Do-Not-Disturb behavior like any other app. Stable and graith-dev Homebrew
installations install this helper automatically on macOS; Linux packages do not
contain it. Managed macOS daemon services retain the packaged helper beside
their private Graith.app service generation, so the macos backend still uses
Graith’s native helper after the daemon starts from its service copy.
Build the helper with make notifier (macOS only — a no-op on Linux) and place
the resulting macos/build/GraithNotifier.app where graith can find it:
alongside the gr binary, under <prefix>/libexec/graith/ or
<prefix>/share/graith/, in /Applications, or in ~/Applications. Set
GRAITH_NOTIFIER_APP to override the location.
The helper is required for native macOS delivery. If it isn’t installed or can’t launch, the dispatch fails and graith logs/reports the failure; it does not route the notification through another application. If you’ve explicitly turned off notifications for “Graith” in System Settings, graith honours that choice as a suppressed notification.
Triggers can fire a notification when their action completes:
[trigger.action]
type = "session"
notify_on_complete = true
notify_message = "Morning briefing ready" # templated; optional
notify_priority = "low" # low|normal|high; optional
Timing
Low-level notification pacing — override to tune coalescing, backend dispatch,
and PTY injection. The idle timeout and max wait are shared by inbox
notifications and gr type, so both avoid colliding with an attached user’s
typing under one policy. Every key is optional; leave the table out for the
defaults below.
[notifications.timing]
coalesce_window = "30s" # drop an identical push within this window ("0" disables coalescing)
dispatch_timeout = "15s" # per-backend dispatch timeout (helper app / command)
inbox_idle_timeout = "10s" # wait before inbox notifications or `gr type` inject into an attached PTY
inbox_max_wait = "2m" # cap that user-idle wait before injecting anyway
inbox_cooldown = "30s" # minimum interval between unread-inbox nudges to one session ("0" disables)
inbox_detached_delay = "5s" # settle delay before notifying a session with no attached client ("0" is immediate)
coalesce_window, inbox_cooldown, and inbox_detached_delay accept "0" to
disable. dispatch_timeout, inbox_idle_timeout, and inbox_max_wait fall back
to their default when zero or negative (they have no sensible zero). An
unparseable value always falls back to the default.
Messages
[messages]
max_age = "" # prune messages older than this, e.g. "7d", "168h" (empty = keep forever)
max_per_stream = 0 # cap messages per stream (0 = unlimited)
Duration strings support days: 7d, 30d, 1d12h.
Status
[status]
ttl = "5m" # default TTL for status updates
A status set via gr status auto-expires after this TTL if the agent produces new output without updating it. Override per-update with gr status --ttl <duration>.