Notifications — Liu Terminal

Liu Terminal notifications. Get notified when AI agents finish tasks, with configurable hooks and sound alerts.

liu-notify is an optional standalone process, while the Liu GUI can host the same server in-process. It accepts a v1 24-byte header or the current v2 28-byte header with optional metadata over a local socket, throttles with a token bucket, dedups identical events, and routes a sound (or spoken line) per (agent, event) pair.

Run it

# Build alongside the main app
cmake --build build --target liu-notify

# Start the daemon
./build/liu-notify daemon

# Send a one-shot event
./build/liu-notify send --tool=claude --event=stop

Config lives at ~/.config/liu/notify.conf (or $XDG_CONFIG_HOME/liu/notify.conf) — separate from the main app's config.json so the daemon stays usable without Liu itself running.

When Liu is running, its GUI hosts the notification server in-process; the CLI does not need to start a second daemon. If no server is listening, send exits without delivering an event. Use the standalone daemon only when notifications must work without Liu.

Supported agent hooks

Liu can install and remove lifecycle hooks for the agents below while the app is running. The hook is written into the agent’s own configuration, and Liu removes its managed entry when the hosting instance exits.

AgentHook surfaceSignalsNotes
Claude Code~/.claude/settings.jsonstart · notify · completeUserPromptSubmit drives the working state; Notification and Stop can alert you.
Codex~/.codex/config.tomlcompleteThe Stop hook reports a completed turn. Run /hooks in Codex once to trust it.
Grok~/.grok/hooks/liu-notify.jsonnotify · completeNotification and top-level Stop events are handled; sub-agent completions stay silent.
OpenCode~/.config/opencode/plugins/liu-notify.jscompleteThe managed plugin listens for top-level session.idle events.
Antigravity~/.gemini/config/hooks.jsoncompleteA named Stop hook reports when the top-level execution loop ends.
Command Code~/.commandcode/settings.jsoncompleteIts Claude-compatible settings schema is wired to the top-level Stop event.

Copilot and custom are accepted by the low-level notification protocol and sound matrix, but Liu does not install an automatic lifecycle hook for them yet. Other agents still run normally in Liu tabs; they simply do not receive automatic hook notifications.

Tool × Event matrix

Configure a sound rule for any combination. Unset cells fall through to the catch-all.

startstopnotifyerrorcomplete
claude·····
copilot·····
codex·····
custom·····

Frame format

The v1 header is 24 bytes; the current v2 header is 28 bytes because it appends meta_len. The complete frame is capped at 4096 bytes. Validation is fuzzed by tests/fuzz_notify_validate.c so malformed frames fail closed without taking the server down.

offset  size  field
0       4     magic      ('N','T','F','Y')
4       2     version    (1 or current 2)
6       2     flags      (speak=1, bell=2)
8       1     tool_id    (claude=1, copilot=2, codex=3, custom=255)
9       1     event_id   (start=1, stop=2, notify=3, error=4, complete=5)
10      1     priority   (reserved)
11      1     source     (cli=0, stdin=1, test=2)
12      4     seq        (client nonce — for dedup)
16      4     title_len  (<= 256)
20      4     body_len   (<= 3072)
24      4     meta_len   (v2 only, <= 512)
payload         title, body, then metadata key=value lines
                total frame <= 4096 bytes

The v2 metadata section currently carries cwd=, session= and agent= lines. Hook senders use it to attribute an event to the correct pane; unknown keys are ignored for forward compatibility.

Platform back-ends

Same frame, different speaker. macOS uses AVSpeechSynthesizer and Linux shells out to notify-send. Each back-end lives in its own file (platform_notify_macos.m, platform_notify_linux.c) so swapping or stubbing one is a single-file change.

Next Configuration →

Documentation

Getting started

Guides

Reference

Other languages

Türkçe · 简体中文 · 한국어 · Español · Deutsch · Italiano · Français · 日本語 · العربية