When Codex waits for you
Codex CLI stops and waits in two situations:
- An approval. With the default
approval_policy = "on-request", Codex asks before it runs a command outside its sandbox, reaches the network, or does anything else your settings do not allow yet. - The end of a turn. It finished what you asked, or it has a question, and waits for your next message.
Nothing happens until you answer. An agent that stopped on an approval a minute into a long task is still waiting an hour later.
Option 1: Codex's built-in notifications
Codex's terminal interface has notifications built in. They are on by default, so you may only need to check how your terminal shows them. These are the settings, with their defaults:
# ~/.codex/config.toml
[tui]
notifications = true # or a list: ["agent-turn-complete", "approval-requested"]
notification_method = "auto" # auto | osc9 | bel
notification_condition = "unfocused" # unfocused | always
notificationsturns them on or off, or limits them to the event types you list.notification_methodpicks how they are sent. Inautomode Codex uses an OSC 9 escape sequence when the terminal seems to support it, which terminals such as iTerm2 and Ghostty show as a macOS notification, and falls back to the terminal bell otherwise.notification_conditiondecides whether they fire only when the terminal window is not focused (the default) or always.
In Terminal.app you get the bell. Terminal can play a sound, or badge and bounce the Dock icon, when a bell rings in a background tab; look in its profile settings under Advanced. In tmux, set -g monitor-bell on marks the window that rang.
Option 2: a notify script for a macOS banner
The notify setting runs a program of your choice when Codex emits an event. Today the only event is agent-turn-complete, so this covers "Codex is done", not approvals. Codex passes the event as a single JSON argument with fields such as type, cwd, input-messages and last-assistant-message.
Step 1: write the script
Save this as ~/.codex/notify.sh. It posts a notification titled with the project folder and Codex's last message. It uses jq (brew install jq).
#!/bin/sh
# Codex passes the event as one JSON argument.
[ "$(printf '%s' "$1" | jq -r '.type')" = "agent-turn-complete" ] || exit 0
msg=$(printf '%s' "$1" | jq -r '(."last-assistant-message" // "Turn complete")[0:200]')
dir=$(basename "$(printf '%s' "$1" | jq -r '.cwd // "."')")
osascript -e 'on run argv' \
-e 'display notification (item 1 of argv) with title (item 2 of argv) sound name "Glass"' \
-e 'end run' "$msg" "Codex: $dir"
Make it executable with chmod +x ~/.codex/notify.sh. Passing the text to osascript as arguments keeps quotes in Codex's message from breaking the command.
Step 2: point Codex at it
Add this line to ~/.codex/config.toml, using the full path to your home folder:
notify = ["/Users/you/.codex/notify.sh"]
notify is a top-level key, so put it above the first [section] header in the file. A line placed under [tui] belongs to [tui] and does nothing.
Step 3: test it
Start a new Codex session, ask for something small, and switch to another app. When the turn ends, a banner should appear. If it does not, open System Settings → Notifications: notifications from osascript are listed under Script Editor, and a Focus mode can hide them.
Option 3: a hook for approval requests
Codex also has hooks: commands it runs at fixed points, configured in ~/.codex/hooks.json or inline in config.toml. They are on by default. The PermissionRequest hook runs when Codex is about to ask for approval, which is the event notify does not cover.
Save this as ~/.codex/hooks/approval.sh. It reads the request from standard input and posts a banner. It prints nothing, so Codex goes on to show its normal approval prompt.
#!/bin/sh
# Codex sends the approval request as JSON on standard input.
input=$(cat)
what=$(printf '%s' "$input" | jq -r \
'(.tool_input.description // .tool_input.command // .tool_name // "Approval needed") | tostring | .[0:200]')
dir=$(basename "$(printf '%s' "$input" | jq -r '.cwd // "."')")
osascript -e 'on run argv' \
-e 'display notification (item 1 of argv) with title (item 2 of argv) sound name "Glass"' \
-e 'end run' "$what" "Codex needs approval: $dir" >/dev/null
Then register it in ~/.codex/hooks.json:
{
"hooks": {
"PermissionRequest": [
{
"hooks": [
{ "type": "command", "command": "/bin/sh ~/.codex/hooks/approval.sh" }
]
}
]
}
}
Codex runs a new or changed hook only after you review and trust it. Start Codex, type /hooks, and trust the hook. A hook that prints an allow or deny decision would answer the request itself; this one only notifies, so you still decide.
Where notifications fall short
Banners are enough for one or two sessions. With more, the same gaps show up as with any agent:
- A banner says something is waiting, but you still find the right terminal tab and scroll back to read the request.
- Banners pile up in Notification Center with no record of which ones you already handled.
- Nothing shows the overall state: which agents are working, which are done, which are stuck.
- If you also run Claude Code, it needs its own hooks in its own format. Our Claude Code notifications guide covers that side.
Option 4: Cadence's toolbar and Inbox
Cadence is a free macOS app that watches your coding agents and collects what they need from you. It runs Codex in herdr, a terminal runtime that knows when an agent is working, waiting or done, so you need no notify script and no terminal setting.
Set it up
- Download Cadence. Install
codexand sign in once in a normal terminal; Cadence starts it under your own OpenAI sign-in. - Open ⋯ → New agent… on the toolbar and pick Codex. If herdr is missing, the view shows the install command; run it and click I installed it.
- Enter the Folder and an optional First message, then start it. Or double-tap ⌥ and say "start a Codex agent in the api folder".
Since 0.5.6 you can also keep typing codex in your own terminal. Turn on Settings → Agents → Your own terminals, and codex started in Ghostty, iTerm, Terminal or your editor runs through Cadence. The terminal looks and works the same. It adds one line to ~/.zshrc, and you can turn it off any time.
See state at a glance
The toolbar on the edge of your screen has one dot per agent: blue working, amber needs you, green done. Codex sits next to Claude Code and any other CLI you run, each with a short title written from what it is doing.
Answer without switching windows
When Codex asks for approval or prints numbered options, the question lands in the Inbox. Press 1 to 9 to answer, Space to type, or V to speak, and Cadence types your answer into Codex's terminal. Esc takes it back within two seconds. Finished turns arrive as cards with a short recap.
Sound, banners and your phone
In Settings → Agents, Text-to-speech reads completions and approval requests aloud, and Notifications turns macOS banners for agent events on or off. The iPhone app, in a private TestFlight beta, shows the same cards and puts an agent's options on its notifications as buttons.
Common questions
Why do I get no Codex notification in Terminal.app?
Terminal.app does not show OSC 9 notifications, so Codex falls back to the bell, and by default only when the window is not focused. Turn on the bell's visual or Dock options in Terminal's profile settings, or use the notify script above for a real banner.
Does the Codex notify setting fire for approval requests?
No. notify currently runs only for agent-turn-complete. For approvals, use the built-in approval-requested notification in [tui] notifications, or a PermissionRequest hook as shown above.
Why does my Codex hook not run?
Codex skips new or changed hooks until you trust them. Run /hooks in Codex and trust it. Also check that hooks are not turned off with hooks = false under [features], and that the script works when you pipe a sample JSON object into it.
Does Cadence change my Codex config?
No. Cadence starts Codex in a herdr terminal and reads its screen to tell when it is waiting. It does not edit ~/.codex/config.toml or hooks.json, and Codex keeps using its own sign-in.