# Agent Notifications & Hooks

> Explains the shift from tmux's terminal alerts and control notifications to cmux's agent-aware notification queue, unread state, CLI hook definitions, and extensible hook formats for multiple coding agents.

- Repository: tmux/tmux-with-manaflow-ai-cmux
- GitHub: https://github.com/tmux/tmux
- Human wiki: https://grok-wiki.com/public/wiki/tmux-tmux-with-manaflow-ai-cmux-62db34dfaddc
- Complete Markdown: https://grok-wiki.com/public/wiki/tmux-tmux-with-manaflow-ai-cmux-62db34dfaddc/llms-full.txt

## Source Files

- `tmux-tmux:notify.c`
- `tmux-tmux:alerts.c`
- `tmux-tmux:control-notify.c`
- `tmux-tmux:input.c`
- `manaflow-ai-cmux:Sources/TerminalNotificationStore.swift`
- `manaflow-ai-cmux:Sources/TerminalNotificationQueue.swift`
- `manaflow-ai-cmux:Sources/NotificationsPage.swift`
- `manaflow-ai-cmux:CLI/CMUXCLI+AgentHookDefinitions.swift`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [tmux-tmux:notify.c](tmux-tmux/notify.c)
- [tmux-tmux:alerts.c](tmux-tmux/alerts.c)
- [tmux-tmux:control-notify.c](tmux-tmux/control-notify.c)
- [tmux-tmux:input.c](tmux-tmux/input.c)
- [tmux-tmux:window.c](tmux-tmux/window.c)
- [manaflow-ai-cmux:Sources/TerminalNotificationStore.swift](manaflow-ai-cmux/Sources/TerminalNotificationStore.swift)
- [manaflow-ai-cmux:Sources/TerminalNotificationQueue.swift](manaflow-ai-cmux/Sources/TerminalNotificationQueue.swift)
- [manaflow-ai-cmux:Sources/NotificationsPage.swift](manaflow-ai-cmux/Sources/NotificationsPage.swift)
- [manaflow-ai-cmux:CLI/CMUXCLI+AgentHookDefinitions.swift](manaflow-ai-cmux/CLI/CMUXCLI+AgentHookDefinitions.swift)
- [manaflow-ai-cmux:Sources/CmuxConfig.swift](manaflow-ai-cmux/Sources/CmuxConfig.swift)
- [manaflow-ai-cmux:Sources/TerminalNotificationPolicy.swift](manaflow-ai-cmux/Sources/TerminalNotificationPolicy.swift)
- [manaflow-ai-cmux:CLI/cmux.swift](manaflow-ai-cmux/CLI/cmux.swift)
- [manaflow-ai-cmux:Sources/KeyboardShortcutSettingsFileStore+Template.swift](manaflow-ai-cmux/Sources/KeyboardShortcutSettingsFileStore+Template.swift)
</details>

# Agent Notifications & Hooks

This page compares tmux's notification model with cmux's agent-oriented model. tmux treats notification as terminal/session state: bells, activity, silence, control-mode protocol messages, and server hooks. cmux keeps those terminal ideas but adds an application-level queue, unread state, desktop delivery, notification policy hooks, and CLI-generated integrations for multiple coding agents.

The useful architectural shift is from "a terminal event happened" to "an agent or terminal surface produced an actionable item for a workspace." That shift lets cmux preserve state across UI views, apply user or project policy, and remain BYOC/BYOK-friendly: hooks are local commands and config files, not tied to a hosted model provider.

## tmux: Alerts, Control Notifications, And Hooks

tmux has three related but distinct paths:

| Path | What it does | Evidence |
| --- | --- | --- |
| Terminal alerts | BEL, activity, and silence set window flags and schedule an alert check. | `input_c0_dispatch` queues `WINDOW_BELL`; `window_update_activity` queues `WINDOW_ACTIVITY`; `alerts_queue` coalesces work through `alerts_list`. |
| User-facing alert output | Depending on options, attached non-control clients get terminal bell output and/or status messages. | `alerts_set_message` skips control clients, sends `TTYC_BEL`, and sets status messages. |
| Control-mode notifications and hooks | Named notifications are routed to control clients and then to configured tmux hooks. | `notify_callback` calls `control_notify_*` functions and then `notify_insert_hook`. |

Sources: [tmux-tmux:input.c:1294-1300](tmux-tmux/input.c), [tmux-tmux:window.c:287-292](tmux-tmux/window.c), [tmux-tmux:alerts.c:157-180](tmux-tmux/alerts.c), [tmux-tmux:alerts.c:292-324](tmux-tmux/alerts.c), [tmux-tmux:notify.c:122-158](tmux-tmux/notify.c)

tmux's alert checks are window-centric. `alerts_check_bell`, `alerts_check_activity`, and `alerts_check_silence` inspect window flags, apply per-session action options, call `notify_winlink` with names such as `alert-bell`, and then decide whether to surface a bell/message to clients. The state is compact and terminal-native, but it is not an inbox with per-workspace unread semantics.

Sources: [tmux-tmux:alerts.c:182-215](tmux-tmux/alerts.c), [tmux-tmux:alerts.c:220-251](tmux-tmux/alerts.c), [tmux-tmux:alerts.c:257-289](tmux-tmux/alerts.c), [tmux-tmux:notify.c:278-285](tmux-tmux/notify.c)

tmux control notifications are protocol lines for clients running in control mode. The control path filters to `CLIENT_CONTROL` clients with initialized control state, then writes records such as `%pane-mode-changed`, `%window-pane-changed`, `%session-changed`, and `%paste-buffer-changed`.

Sources: [tmux-tmux:control-notify.c:26-40](tmux-tmux/control-notify.c), [tmux-tmux:control-notify.c:75-89](tmux-tmux/control-notify.c), [tmux-tmux:control-notify.c:148-169](tmux-tmux/control-notify.c), [tmux-tmux:control-notify.c:236-259](tmux-tmux/control-notify.c)

## cmux: Notification As Workspace State

cmux promotes notifications into a first-class app model. `TerminalNotification` stores identifiers for workspace tab, optional surface/panel, title/subtitle/body, creation time, read state, pane flash state, and optional click action. `TerminalNotificationStore` publishes the notification list and derives indexes for unread count, unread counts by tab, unread surfaces, and latest notification lookup.

Sources: [manaflow-ai-cmux:Sources/TerminalNotificationStore.swift:694-739](manaflow-ai-cmux/Sources/TerminalNotificationStore.swift), [manaflow-ai-cmux:Sources/TerminalNotificationStore.swift:742-787](manaflow-ai-cmux/Sources/TerminalNotificationStore.swift), [manaflow-ai-cmux:Sources/TerminalNotificationStore.swift:873-895](manaflow-ai-cmux/Sources/TerminalNotificationStore.swift)

The store records new notifications by removing older notifications for the same tab/surface, inserting the new one at the front, refreshing unread presentation, and then delivering side effects. Read state is explicit: cmux can mark one notification, one tab, one tab/surface, or all notifications as read, and it removes delivered or pending system notification requests when clearing read state.

Sources: [manaflow-ai-cmux:Sources/TerminalNotificationStore.swift:1368-1423](manaflow-ai-cmux/Sources/TerminalNotificationStore.swift), [manaflow-ai-cmux:Sources/TerminalNotificationStore.swift:1425-1472](manaflow-ai-cmux/Sources/TerminalNotificationStore.swift), [manaflow-ai-cmux:Sources/TerminalNotificationStore.swift:1558-1610](manaflow-ai-cmux/Sources/TerminalNotificationStore.swift), [manaflow-ai-cmux:Sources/TerminalNotificationStore.swift:1665-1687](manaflow-ai-cmux/Sources/TerminalNotificationStore.swift)

The UI is built around review, not only interruption. `NotificationsPage` reads the store, shows empty and workspace-unread states, renders rows with unread dots, opens the originating notification target, supports "Jump to Latest Unread," and clears individual or all notifications.

Sources: [manaflow-ai-cmux:Sources/NotificationsPage.swift:4-54](manaflow-ai-cmux/Sources/NotificationsPage.swift), [manaflow-ai-cmux:Sources/NotificationsPage.swift:69-88](manaflow-ai-cmux/Sources/NotificationsPage.swift), [manaflow-ai-cmux:Sources/NotificationsPage.swift:115-156](manaflow-ai-cmux/Sources/NotificationsPage.swift), [manaflow-ai-cmux:Sources/NotificationsPage.swift:185-253](manaflow-ai-cmux/Sources/NotificationsPage.swift)

```text
tmux
  pane/window event
      -> window alert flags
      -> control protocol line or terminal bell/status
      -> optional tmux hook command

cmux
  agent/terminal event
      -> queued workspace/surface notification mutation
      -> policy hook envelope
      -> recorded unread notification
      -> UI state + desktop/sound/custom command effects
```

## Queueing And Coalescing

cmux separates cross-thread event intake from main-actor UI mutation with `TerminalMutationBus`. It queues notification delivery and clear operations, assigns sequence numbers, coalesces pending notifications by generation and target key, and drains batches on the main actor. Clear operations can remove pending delivery entries before they hit the store.

Sources: [manaflow-ai-cmux:Sources/TerminalNotificationQueue.swift:35-62](manaflow-ai-cmux/Sources/TerminalNotificationQueue.swift), [manaflow-ai-cmux:Sources/TerminalNotificationQueue.swift:122-166](manaflow-ai-cmux/Sources/TerminalNotificationQueue.swift), [manaflow-ai-cmux:Sources/TerminalNotificationQueue.swift:168-195](manaflow-ai-cmux/Sources/TerminalNotificationQueue.swift), [manaflow-ai-cmux:Sources/TerminalNotificationQueue.swift:217-233](manaflow-ai-cmux/Sources/TerminalNotificationQueue.swift)

Delivery is guarded against stale targets. Before queued delivery reaches `addNotification`, cmux checks whether the target tab or surface still exists. There is also a synchronous delivery path that discards pending notifications for the same target before immediately adding a notification.

Sources: [manaflow-ai-cmux:Sources/TerminalNotificationQueue.swift:326-354](manaflow-ai-cmux/Sources/TerminalNotificationQueue.swift), [manaflow-ai-cmux:Sources/TerminalNotificationQueue.swift:357-378](manaflow-ai-cmux/Sources/TerminalNotificationQueue.swift), [manaflow-ai-cmux:Sources/TerminalNotificationQueue.swift:381-419](manaflow-ai-cmux/Sources/TerminalNotificationQueue.swift)

## Notification Policy Hooks

cmux lets notification behavior be shaped by configured local commands. The config schema supports `notifications.hooks`, `hooksMode`, per-hook `id`, `command`, `timeoutSeconds`, and `enabled`. Global hooks are loaded first, local hooks are appended by default, and local config can replace inherited hooks with `hooksMode: "replace"`.

Sources: [manaflow-ai-cmux:Sources/CmuxConfig.swift:146-217](manaflow-ai-cmux/Sources/CmuxConfig.swift), [manaflow-ai-cmux:Sources/CmuxConfig.swift:2005-2019](manaflow-ai-cmux/Sources/CmuxConfig.swift), [manaflow-ai-cmux:Sources/CmuxConfig.swift:2282-2307](manaflow-ai-cmux/Sources/CmuxConfig.swift), [manaflow-ai-cmux:Sources/KeyboardShortcutSettingsFileStore+Template.swift:91-102](manaflow-ai-cmux/Sources/KeyboardShortcutSettingsFileStore+Template.swift)

A notification policy hook receives a JSON envelope with notification payload, context, and effect flags. Defaults are enabled for recording, unread marking, workspace reorder, desktop delivery, sound, custom command, and pane flash. Hooks can return JSON patches to modify the envelope or stop later hooks.

```swift
// manaflow-ai-cmux:Sources/TerminalNotificationPolicy.swift
struct TerminalNotificationPolicyEffects: Codable, Sendable, Equatable {
    var record: Bool = true
    var markUnread: Bool = true
    var reorderWorkspace: Bool = true
    var desktop: Bool = true
    var sound: Bool = true
    var command: Bool = true
    var paneFlash: Bool = true
}
```

Sources: [manaflow-ai-cmux:Sources/TerminalNotificationPolicy.swift:5-28](manaflow-ai-cmux/Sources/TerminalNotificationPolicy.swift), [manaflow-ai-cmux:Sources/TerminalNotificationPolicy.swift:180-186](manaflow-ai-cmux/Sources/TerminalNotificationPolicy.swift), [manaflow-ai-cmux:Sources/TerminalNotificationPolicy.swift:255-279](manaflow-ai-cmux/Sources/TerminalNotificationPolicy.swift), [manaflow-ai-cmux:Sources/TerminalNotificationPolicy.swift:913-929](manaflow-ai-cmux/Sources/TerminalNotificationPolicy.swift)

Hooks are executed as local `/bin/sh -c` commands in the resolved hook working directory. cmux passes notification fields both as environment variables and as JSON on stdin, enforces a timeout, limits stdout size, treats non-zero exit status as failure, and decodes non-empty stdout as a JSON patch.

Sources: [manaflow-ai-cmux:Sources/TerminalNotificationPolicy.swift:459-551](manaflow-ai-cmux/Sources/TerminalNotificationPolicy.swift), [manaflow-ai-cmux:Sources/TerminalNotificationPolicy.swift:553-562](manaflow-ai-cmux/Sources/TerminalNotificationPolicy.swift), [manaflow-ai-cmux:Sources/TerminalNotificationPolicy.swift:644-689](manaflow-ai-cmux/Sources/TerminalNotificationPolicy.swift), [manaflow-ai-cmux:Sources/TerminalNotificationPolicy.swift:776-817](manaflow-ai-cmux/Sources/TerminalNotificationPolicy.swift)

## Agent Hook Definitions

cmux's CLI hook layer is explicitly multi-agent. `AgentHookDef` records the agent name, config location, environment overrides, disable environment variable, hook marker, binary name, supported events, feed-hook events, and hook format. This makes adding an agent a data-definition problem when the target agent fits an existing format.

```swift
// manaflow-ai-cmux:CLI/CMUXCLI+AgentHookDefinitions.swift
enum HookFormat {
    case flat
    case nested(timeoutMs: Int)
    case antigravityJSON(timeoutSeconds: Int)
    case rovoDevYAML
    case hermesAgentYAML
}
```

Sources: [manaflow-ai-cmux:CLI/CMUXCLI+AgentHookDefinitions.swift:6-47](manaflow-ai-cmux/CLI/CMUXCLI+AgentHookDefinitions.swift), [manaflow-ai-cmux:CLI/CMUXCLI+AgentHookDefinitions.swift:49-73](manaflow-ai-cmux/CLI/CMUXCLI+AgentHookDefinitions.swift), [manaflow-ai-cmux:CLI/CMUXCLI+AgentHookDefinitions.swift:104-118](manaflow-ai-cmux/CLI/CMUXCLI+AgentHookDefinitions.swift)

The current definitions cover Codex, Grok, OpenCode, Pi, Amp, Cursor, Gemini, Antigravity, Rovo Dev, Hermes Agent, Copilot, CodeBuddy, Factory, and Qoder. Different agents map their native lifecycle names to cmux subcommands such as `session-start`, `prompt-submit`, `stop`, `notification`, and `session-end`.

Sources: [manaflow-ai-cmux:CLI/CMUXCLI+AgentHookDefinitions.swift:122-152](manaflow-ai-cmux/CLI/CMUXCLI+AgentHookDefinitions.swift), [manaflow-ai-cmux:CLI/CMUXCLI+AgentHookDefinitions.swift:153-187](manaflow-ai-cmux/CLI/CMUXCLI+AgentHookDefinitions.swift), [manaflow-ai-cmux:CLI/CMUXCLI+AgentHookDefinitions.swift:188-245](manaflow-ai-cmux/CLI/CMUXCLI+AgentHookDefinitions.swift), [manaflow-ai-cmux:CLI/CMUXCLI+AgentHookDefinitions.swift:246-297](manaflow-ai-cmux/CLI/CMUXCLI+AgentHookDefinitions.swift)

The generated shell command stays portable: it finds a bundled CLI path or `cmux` on `PATH`, respects `CMUX_SOCKET_PATH`, requires `CMUX_SURFACE_ID` for normal dispatch, and returns `{}` when disabled or unavailable. Grok and Antigravity use pinned dispatch with extra marker and trace support.

Sources: [manaflow-ai-cmux:CLI/CMUXCLI+AgentHookDefinitions.swift:304-320](manaflow-ai-cmux/CLI/CMUXCLI+AgentHookDefinitions.swift), [manaflow-ai-cmux:CLI/CMUXCLI+AgentHookDefinitions.swift:323-370](manaflow-ai-cmux/CLI/CMUXCLI+AgentHookDefinitions.swift), [manaflow-ai-cmux:CLI/CMUXCLI+AgentHookDefinitions.swift:384-433](manaflow-ai-cmux/CLI/CMUXCLI+AgentHookDefinitions.swift)

## Format Adapters Preserve User Config

For JSON-based agents, cmux builds hook dictionaries differently for flat, nested, and Antigravity schemas. Feed bridge hooks get a longer timeout so interactive approvals or plan/question waits do not hit the agent's shorter default timeout.

Sources: [manaflow-ai-cmux:CLI/cmux.swift:23148-23208](manaflow-ai-cmux/CLI/cmux.swift), [manaflow-ai-cmux:CLI/cmux.swift:23210-23239](manaflow-ai-cmux/CLI/cmux.swift)

Installation is careful about ownership. cmux removes only cmux-owned entries from existing hook config, preserves unknown or user-owned data, and then inserts new cmux entries. Specialized adapters handle OpenCode, Pi, Amp, Rovo Dev, Hermes Agent, and Antigravity before the generic JSON installer runs.

Sources: [manaflow-ai-cmux:CLI/cmux.swift:24115-24140](manaflow-ai-cmux/CLI/cmux.swift), [manaflow-ai-cmux:CLI/cmux.swift:24176-24249](manaflow-ai-cmux/CLI/cmux.swift), [manaflow-ai-cmux:CLI/cmux.swift:24252-24282](manaflow-ai-cmux/CLI/cmux.swift), [manaflow-ai-cmux:CLI/cmux.swift:28898-28925](manaflow-ai-cmux/CLI/cmux.swift)

## Portable Design Takeaways

| Design question | tmux answer | cmux answer | Portable idea |
| --- | --- | --- | --- |
| What is the notification target? | Window, winlink, session, control client. | Workspace tab, surface/panel, notification record. | Keep low-level events, but normalize them into product objects. |
| Is unread state first-class? | No, alert flags/status are transient terminal state. | Yes, `isRead`, unread indexes, manual/restored/panel-derived workspace indicators. | Treat "needs attention" separately from "should interrupt." |
| Can policy change delivery? | tmux options choose bell/activity/silence behavior and hooks run commands. | Configured hooks can patch record/unread/desktop/sound/command/paneFlash effects. | Use local command hooks for provider-neutral extension. |
| How are agents integrated? | Not agent-specific. | Agent definitions map native hook events to cmux subcommands and config formats. | Model integrations as data plus format adapters. |

Sources: [tmux-tmux:alerts.c:70-89](tmux-tmux/alerts.c), [tmux-tmux:notify.c:178-230](tmux-tmux/notify.c), [manaflow-ai-cmux:Sources/TerminalNotificationStore.swift:749-787](manaflow-ai-cmux/Sources/TerminalNotificationStore.swift), [manaflow-ai-cmux:Sources/TerminalNotificationPolicy.swift:21-28](manaflow-ai-cmux/Sources/TerminalNotificationPolicy.swift), [manaflow-ai-cmux:CLI/CMUXCLI+AgentHookDefinitions.swift:6-47](manaflow-ai-cmux/CLI/CMUXCLI+AgentHookDefinitions.swift)

## BYOC/BYOK Implications

cmux's architecture stays vendor-agnostic because notification policy is a local shell-command contract and agent integration is driven by config files, environment variables, socket paths, and CLI subcommands. No notification path in the inspected code requires a specific model provider or hosted connector. A Grok-Wiki integration should keep that property: skill packs, generated wiki context, and hook definitions should be portable file, repository, or catalog sources, with repository code remaining the source of truth.

Sources: [manaflow-ai-cmux:Sources/CmuxConfig.swift:236-277](manaflow-ai-cmux/Sources/CmuxConfig.swift), [manaflow-ai-cmux:Sources/TerminalNotificationPolicy.swift:524-562](manaflow-ai-cmux/Sources/TerminalNotificationPolicy.swift), [manaflow-ai-cmux:CLI/CMUXCLI+AgentHookDefinitions.swift:315-320](manaflow-ai-cmux/CLI/CMUXCLI+AgentHookDefinitions.swift)

## Generation Context

This page used repository code as source of truth. The selected Compound Engineering guidance was applied as page-shaping metadata from the provided bundled snapshot description; no local `STRATEGY.md`, `AGENTS.md`, or `docs/solutions/**` files were found in the two repository folders during this focused pass.

## Summary

tmux provides a robust terminal multiplexer notification substrate: alert flags, visual/audible terminal feedback, control-mode protocol events, and hook dispatch. cmux builds a product workflow above that kind of substrate: queued workspace notifications, explicit unread state, review UI, desktop/sound/custom command effects, local policy hooks, and multi-agent CLI hook definitions. The portable lesson is to keep raw terminal and agent events simple, then normalize them into an application-owned notification record before applying policy or UI behavior.

Sources: [tmux-tmux:notify.c:122-158](tmux-tmux/notify.c), [manaflow-ai-cmux:Sources/TerminalNotificationQueue.swift:326-354](manaflow-ai-cmux/Sources/TerminalNotificationQueue.swift), [manaflow-ai-cmux:Sources/TerminalNotificationStore.swift:1425-1472](manaflow-ai-cmux/Sources/TerminalNotificationStore.swift), [manaflow-ai-cmux:CLI/CMUXCLI+AgentHookDefinitions.swift:122-297](manaflow-ai-cmux/CLI/CMUXCLI+AgentHookDefinitions.swift)
