Motivation
Hermes runs long tasks (builds, research, multi-step workflows) but has no way to alert the user when finished unless a gateway (Telegram/Discord) is running. A lightweight notification tool using OS built-ins would fill this gap.
Originally proposed in PR #170 by @ygd58 — great concept, but the implementation needs a ground-up rebuild to match the codebase patterns and address security/compatibility concerns identified in review.
Proposed Tools
notify — Desktop notification
- Linux:
notify-send (libnotify)
- macOS:
osascript (display notification)
- Windows: PowerShell toast (Windows 10+)
notify_sound — System alert sound
- Linux:
paplay/pw-play/aplay
- macOS:
afplay
- Windows:
[System.Media.SystemSounds]::Beep.Play()
Requirements
Registration
- Must use the
tools/registry.py pattern: module-level registry.register() with toolset, schema, and handler=lambda args, **kw: ... wrapper
- Add
"notification" toolset to toolsets.py
- Add module to
_discover_tools() in model_tools.py
Security
- No string interpolation into shell commands. User-controlled
title/message must never be interpolated into osascript/PowerShell strings
- Use
subprocess.run() with list arguments where possible
- For osascript: pass script via stdin or use proper AppleScript escaping
- For PowerShell: use
-EncodedCommand with base64, or pass values via environment variables
Remote / SSH support
- Desktop notifications require a GUI session — they won't work over SSH, which is the most common scenario for long-running tasks
check_fn should detect headless/SSH environments (check $DISPLAY, $SSH_CONNECTION, $WAYLAND_DISPLAY)
- Fallback options to consider:
- Terminal bell (
\a) — works over SSH
- Integration with existing
send_message tool (Telegram/Discord) when gateway is active
- tmux/screen notification if running inside one
- The tool should still be useful locally (IDE terminal, desktop terminal)
Platform compatibility
- Linux sound file paths should not be hardcoded to Ubuntu/freedesktop only — probe for available files or use terminal bell as fallback
- macOS sound paths (
/System/Library/Sounds/) are stable
- Windows toast requires Win 10+ — graceful fallback for older versions
Testing
- Unit tests for each platform backend (mock subprocess calls)
- Test
check_fn behavior in headless environments
- Test escaping/sanitization of special characters in title/message
Nice-to-haves
- Works well with the existing cron system (notify when scheduled task completes)
- Configurable via
hermes tools (enable/disable notification toolset)
- Optional urgency levels (low/normal/critical) mapped to platform-specific behavior
Reference
Motivation
Hermes runs long tasks (builds, research, multi-step workflows) but has no way to alert the user when finished unless a gateway (Telegram/Discord) is running. A lightweight notification tool using OS built-ins would fill this gap.
Originally proposed in PR #170 by @ygd58 — great concept, but the implementation needs a ground-up rebuild to match the codebase patterns and address security/compatibility concerns identified in review.
Proposed Tools
notify— Desktop notificationnotify-send(libnotify)osascript(display notification)notify_sound— System alert soundpaplay/pw-play/aplayafplay[System.Media.SystemSounds]::Beep.Play()Requirements
Registration
tools/registry.pypattern: module-levelregistry.register()withtoolset,schema, andhandler=lambda args, **kw: ...wrapper"notification"toolset totoolsets.py_discover_tools()inmodel_tools.pySecurity
title/messagemust never be interpolated into osascript/PowerShell stringssubprocess.run()with list arguments where possible-EncodedCommandwith base64, or pass values via environment variablesRemote / SSH support
check_fnshould detect headless/SSH environments (check$DISPLAY,$SSH_CONNECTION,$WAYLAND_DISPLAY)\a) — works over SSHsend_messagetool (Telegram/Discord) when gateway is activePlatform compatibility
/System/Library/Sounds/) are stableTesting
check_fnbehavior in headless environmentsNice-to-haves
hermes tools(enable/disable notification toolset)Reference
tools/tts_tool.py,tools/terminal_tool.py