Skip to content

agents

Coding agent notifications: which CLI tells you what

Claude Code, Codex CLI and Gemini CLI each signal done and blocked differently. Four layers can page you, and not all of them survive tmux.

Nadia Okonkwo10 min read

Every mainstream coding agent can tell you it finished. Fewer of them can tell you they are stuck, and the ones that can do not all deliver it the same way. Claude Code and Gemini CLI both expose the blocked state to a program you write; Codex CLI exposes it only as a terminal escape sequence, which is the one delivery route that tmux swallows by default. That difference — not the completion signal everybody advertises — is what decides whether a walk-away run is safe.

This page is the map: which agent signals what, the four layers that can carry the signal, and where each layer stops. Every configuration file named here has its own page, linked in place.

Which coding agents can notify you, and about what?

Two questions matter for each tool, and they are not the same question. Can it run a program of my choosing? decides whether you can escalate — to a sound, a push, a webhook, anything a shell can reach. Can it tell the terminal directly? decides whether you get anything at all with zero scripting, and whether that anything survives a multiplexer.

AgentFires a program on 'done'Fires a program on 'blocked'Tells the terminal on 'blocked'
Claude CodeStop hookNotification hookvia a hook you write
Codex CLInotify (agent-turn-complete)no — open requesttui.notifications approval-requested
Gemini CLIAfterAgent hookNotification hookenableNotifications, 'Action required'

Claude Code splits the two states into two hook events by design: per Anthropic's hooks reference, Stop fires "when Claude finishes responding" and Notification "when Claude Code sends a notification" — the permission prompts and idle timeouts. Both take an arbitrary shell command, so both can escalate as far as you are willing to script. The settings scopes and matcher semantics are the whole story on that side.

Gemini CLI reaches the same place by a different road. Its hook table includes AfterAgent, which fires "once per turn after the model generates its final response", and a Notification event that fires "when the CLI emits a system alert (for example, Tool Permissions)" — the approval case. Separately, general.enableNotifications turns on a built-in system notification that distinguishes "Session complete" from "Action required". It is off by default and, per Google's own docs, it falls back to a plain terminal bell on terminals that cannot do better.

Codex CLI is the outlier, and it is worth understanding why before you design around it.

Why does Codex CLI go quiet when it needs approval?

Codex CLI has two separate notification mechanisms that people routinely mistake for one.

notify is a top-level array in ~/.codex/config.toml naming a program. Codex runs it and hands it a JSON blob. As OpenAI's configuration reference states, the only event it currently emits is agent-turn-complete — a finished turn, nothing else.

tui.notifications is a different thing entirely. It does not run a program; it emits an escape sequence at your terminal and lets the terminal decide what a notification looks like. That one does accept approval-requested alongside agent-turn-complete, with notification_method choosing between an OSC 9 sequence and a plain bell.

So the blocked signal exists — it just cannot be handed to a script. There is an open request to expose the approval event through top-level notify as well. Until that lands, anything you want to happen when Codex is waiting has to be triggered by the terminal receiving that sequence, or by watching the pane yourself. The Codex-specific config and a pane-watching wrapper cover both.

The escape-sequence route is the one tmux breaks

An agent that signals "blocked" by writing an escape sequence is signalling to whatever is attached to its terminal. Inside tmux, that is tmux — and tmux does not forward arbitrary OSC sequences to the outer terminal. This is why Codex users report notifications that work standalone and vanish under tmux. It is not a Codex bug; see terminal notifications and the passthrough problem.

What is the difference between done, waiting and stalled?

Three states, and most setups only ever wire the first one.

Done is a completed turn. Every agent above emits it. It is also the least urgent of the three — a finished run is not going anywhere, and nothing is being wasted while you notice it in your own time.

Waiting is a blocking read on your input: a permission prompt, an approval, a clarifying question. Nothing progresses until you answer. This is the state worth paging for, and it is the one with the patchiest support, because it is genuinely harder to emit — the process is sitting on a blocking read, which from the outside looks identical to one that is still thinking.

Stalled has no event anywhere. No agent emits "I have been retrying the same failing test for eleven minutes". You find out by looking. Any tool that claims to detect it is inferring it from output silence, which is what tmux's own monitor-silence does and does honestly — covered in tmux notifications.

If you only wire one signal, wire waiting. A blocked agent produces no completion event to tell you it is blocked, so a done-only setup is silent exactly when silence is most expensive.

What are the four layers that can page you?

A notification is not one thing. It is a signal handed down a chain, and each link can drop it. Knowing which link you are relying on is the difference between a setup that works and one that works on your laptop.

The agent's own hook

The agent runs a command, or emits an escape sequence, when something happens. This is the only layer that knows what happened — everything below it is guessing from output. It is also the only layer that can put the answer in the message rather than making you go and look.

The multiplexer

tmux watches panes for activity, bells and silence, and can run a command when a window raises an alert. Useful precisely because it needs no cooperation from the program in the pane — it works for tools with no hook system at all, and it is the layer that turns "output stopped" into an event.

The terminal emulator

Warp, iTerm2, Ghostty and Windows Terminal each turn a bell or an OSC sequence into something the operating system will show you. Zero scripting, and the layer most likely to break silently the moment a multiplexer sits between it and the program.

A push service

The only tier that survives you leaving the building. Nothing in the list above delivers to a phone; reaching one means the layer above calling out to something that can — an ntfy topic, a Pushover account, a webhook.

The layers are additive and mostly independent, which is both the good news and the trap. A hook that plays a sound and a terminal that raises a toast will both fire, and you will get two notifications for one event and start muting things. Pick one layer per state and let the others stay quiet — and if the state you are signalling is "done" versus "waiting", give them two different sounds rather than two separate channels.

Which layer should you actually use?

Match the layer to how far away you are, not to how sophisticated it looks.

You are...Right layerWhy the others are wrong
at the keyboardterminal bell or a sounda desktop toast for a 40-second task is noise
elsewhere in the buildingOS notification from a hooka bell you cannot hear is not a signal
running many agents at oncetmux alerts on the paneper-tool hooks means N configs to maintain
out of the housepush servicenothing local reaches a locked phone

The per-tool cost is the one people underestimate. One agent, one machine, one hook file is a ten-minute setup. Three agents across two machines is six config files in three different formats, each of which breaks on its own release schedule. At that point the multiplexer layer is cheaper than the agent layer, because tmux does not care which agent is in the pane — which is the argument tmux notifications makes in full.

Where does every notification stop helping?

At the moment it arrives.

A banner tells you something happened. It does not tell you what the pane says, whether the diff is sane, or whether the y/n sitting there is npm test or rm -rf. If you are at the desk, the answer costs a keystroke. If you are not, a notification you cannot act on is worse than none — it has converted a task you had successfully forgotten about into one you are now anxious about and cannot resolve.

This is the half of the problem the notification layer structurally cannot solve, and it is why the honest advice is to spend less time perfecting the signal and more time making the follow-up cheap. Getting a good message into the banner helps: a hook runs a shell command, so it can read git diff --stat or the tail of a log and paste that in, and "finished: 4 files, tests green" answers the question the banner would otherwise create. Beyond that you need the session itself.

mtmux is the answer to the second half, and it is deliberately not a notification tool — it sends nothing and knows nothing about agent state. It serves the tmux session your agent is already running in to a browser over an end-to-end encrypted tunnel, so the follow-up to a push is opening the pane on the device already in your hand rather than finding a laptop. How that works with agents covers the shape of it; the setup walkthrough is the five-minute version. The broader argument for why polling beats paging on long runs is in stop babysitting coding agents, and if you are getting there over SSH today, tmux over SSH is the setup it replaces.

Which coding agent has the best notification support?

Claude Code, by a clear margin: it splits completion and blocked into two separate hook events (Stop and Notification), and both run an arbitrary shell command, so both can escalate to a sound, a desktop alert or a push. Gemini CLI covers the same two states through AfterAgent and Notification hooks. Codex CLI can only hand a completed turn to a program.

Why do I get no notification when Codex CLI asks for approval?

Because Codex CLI's notify program hook fires only on agent-turn-complete. The approval signal exists, but only through tui.notifications, which emits a terminal escape sequence rather than running a program — and that sequence is filtered by tmux unless you explicitly configure the passthrough.

Can a coding agent tell me when it is stuck rather than finished or blocked?

No. No agent emits a "stalled" event, because nothing in the process knows it is stalled — from the inside it is still working. The closest honest approximation is watching for output silence, which tmux's monitor-silence does at the pane level, independent of which agent is running.

Do agent notifications work inside tmux?

Hook-based ones do, because they run a shell command that never touches the terminal. The ones built on escape sequences frequently do not: tmux does not forward arbitrary OSC sequences to the outer terminal, so a notification that works standalone can silently stop working under tmux.

Can I get coding agent notifications on my phone?

Only by making the local hook call out to a push service such as ntfy or Pushover — none of these agents deliver to a phone on their own. Note that a push tells you something happened but not what the pane says, so most people pair it with a way to actually open the session remotely.

Put this to work — mtmux attaches to the tmux server you already run.

Related reading

agents

Stop babysitting your AI coding agent

A long agent run ends in four distinct ways and only one is success — why exit codes can't tell them apart, and what reading the pane gets you instead.

Nadia Okonkwo8 min read