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>.