Know when your agents need attention
Grove's dashboard is pull only. You have to be looking at it, and push notifications close that gap.
Get told when it matters
- Your phone buzzes when an agent finishes its turn, asks you something, or its workspace breaks.
- Tap one and you land on that workspace.
- Grove runs no second status engine. The broker watches the same activity stream the Activity Dashboard already computes for moments worth a buzz.
- You choose which triggers reach each channel, and everything is off until you turn it on.
What fires a notification
Each trigger has its own switch.
| State | Meaning | Default |
|---|---|---|
waiting |
The turn finished. The agent may need you. | On |
blocked |
The agent is waiting on a permission prompt or a question. | On |
error |
The run or init failed. | On |
idle |
The agent went quiet. | Off |
| Event | Meaning | Default |
|---|---|---|
error |
The workspace hit an error. | On |
orphaned_detected |
Its worktree is gone. | On |
offline_detected |
Its tmux session vanished. | On |
created, killed, paused, resumed, respawned |
Routine lifecycle verbs. | Off |
onlists the agent states that alert, andon_lifecyclelists the workspace events. Routine verbs are off by default.on_questionadds the prompt and its options to the alert, so you can answer from the tray.- A session already waiting when the daemon starts is recorded without an alert, and a 30 second window stops one state from buzzing twice.
- Enable it in any layer of the config cascade.
Severity: how loudly it lands
Every notification carries low, normal, high, or urgent severity.
- A finished turn is
normal. - A question,
blockedstate, or workspaceerrorishigh. - An idle agent or routine lifecycle event is
low. - Gotify maps severity through
prioritiesfrom 0 to 10. - Webhook maps severity through
prioritieson ntfy's scale from 1 to 5.
Channels
Delivery is pluggable across two channels today, and secrets never live in config. Each channel holds the name of the environment variable that carries its token, so a committed .grove/config.json stays safe to publish.
Gotify
Point Grove at a running Gotify server for OS level push with no extra app. The body is markdown with the agent, the reason, the task and every question option on its own line, and Android opens the workspace on tap.
Webhook
A JSON POST to any URL, speaking ntfy's publish format directly, so any relay can consume it. The JSON carries both renderings plus severity, trigger and any open questions with their ids.
Limitations
- The daemon sends them, so a TUI only setup without the daemon receives no alerts. A down or slow channel is skipped, never retried.
- Gotify needs an application token rather than a client token. Grove only sends messages.
- A Gotify push cannot be retracted. A resolved question can remain in your tray.
- Question alerts need a harness that surfaces the question. Claude Code and Codex questions through an MCP tool work. Codex interactive approval prompts produce no alert.
Enabling it
Add notifications to any configuration layer.
~/.config/grove/config.json
{
"notifications": {
"enabled": true,
"deep_link_base_url": "https://grove.example.com",
"on": ["waiting", "blocked", "error"],
"on_question": true,
"on_lifecycle": ["error", "offline_detected", "orphaned_detected"],
"gotify": {
"enabled": true,
"server_url": "https://gotify.example.com",
"token_env": "GROVE_GOTIFY_TOKEN",
"markdown": true,
"priorities": { "low": 2, "normal": 5, "high": 8, "urgent": 9 }
},
"webhook": {
"enabled": true,
"url": "https://ntfy.sh",
"topic": "grove-alerts",
"token_env": "GROVE_NTFY_TOKEN",
"priorities": { "low": 2, "normal": 3, "high": 4, "urgent": 5 }
}
}
}
Export the tokens in the daemon's environment.
export GROVE_GOTIFY_TOKEN="your-gotify-app-token"
export GROVE_NTFY_TOKEN="your-ntfy-token" # only if your topic needs auth
deep_link_base_urlis where your web dashboard is reachable. Its default ishttp://localhost:3000, which a phone resolves to itself, so set an address your phone can reach, or""for no link.- The Configuration Reference lists every field and default.