A small Codex Stop hook that displays an Android notification through
Termux:API when Codex finishes a response.
The notification title contains the current project name, and its body contains a simplified version of the latest Codex response. Notification failures never affect Codex itself.
- Termux on Android
- The Termux:API app and Termux package
- Python 3
- Codex hooks support
Install the required packages in Termux:
pkg install python termux-apiTermux and the Termux:API app must come from the same distribution source to work together correctly.
- Copy the script into the Codex hooks directory and make it executable:
mkdir -p "$HOME/.codex/hooks"
cp termux_stop_notification.py "$HOME/.codex/hooks/"
chmod +x "$HOME/.codex/hooks/termux_stop_notification.py"- Add the following configuration to
~/.codex/config.toml. Replace<HOME>with your absolute home path. A typical Termux home path is/data/data/com.termux/files/home.
[features]
hooks = true
[[hooks.Stop]]
[[hooks.Stop.hooks]]
type = "command"
command = "/data/data/com.termux/files/usr/bin/python3 <HOME>/.codex/hooks/termux_stop_notification.py"
timeout = 20
statusMessage = "Sending Android completion notification"If a [features] section already exists, add hooks = true to it instead of
creating a duplicate section. On first use, Codex may ask you to trust the hook.
Review it before approving.
- Optionally test the hook directly:
printf '%s' '{"cwd":"/tmp/example","last_assistant_message":"The task is complete."}' \
| python3 termux_stop_notification.pyText-to-speech is disabled by default. When enabled, the hook reads the same
shortened response used in the notification through Android's system TTS
engine. Speech starts in the background so it does not hold up the Codex
Stop hook.
To enable TTS, append --tts to the hook command:
command = "/data/data/com.termux/files/usr/bin/python3 <HOME>/.codex/hooks/termux_stop_notification.py --tts"Remove --tts to turn it off again. Because this changes the hook definition,
Codex may ask you to review and trust the hook again.
You can also control TTS without changing the hook definition by setting
CODEX_TERMUX_TTS before starting Codex:
# Enable TTS for Codex processes started from this shell.
export CODEX_TERMUX_TTS=1
# Disable it again.
export CODEX_TERMUX_TTS=0Accepted enabled values are 1, true, yes, and on, ignoring letter case.
The --tts flag always enables speech regardless of the environment variable.
By default, the hook only removes Markdown and truncates long responses. Add
--codex-summary to ask the locally installed
Codex App Server for a short,
semantic summary before showing and speaking the notification:
command = "/data/data/com.termux/files/usr/bin/python3 <HOME>/.codex/hooks/termux_stop_notification.py --tts --codex-summary"
timeout = 20This uses the existing Codex ChatGPT login and its subscription allowance. It
does not require an OpenAI API key or separate API billing. The default summary
model is gpt-5.6-luna
with reasoning disabled.
The summarizer is isolated for safety and predictable behavior:
- hooks and apps are disabled in the nested ephemeral thread, preventing hook recursion and unnecessary connector startup;
- the nested thread is read-only, never asks for approvals, and is not saved;
- project instructions are avoided by running from the system temporary folder;
- completion text is capped at 4,000 characters and treated as untrusted data;
- short, single-line responses skip Codex and use no subscription allowance;
- App Server errors, unavailable models, rerouting, or an 8-second timeout fall back to the existing local summary.
To enable or disable semantic summaries without editing the hook definition:
export CODEX_TERMUX_CODEX_SUMMARY=1 # Enable
export CODEX_TERMUX_CODEX_SUMMARY=0 # DisableOptional flags are --summary-model MODEL and --summary-timeout SECONDS.
The feature requires a Codex version with codex app-server and subscription
access to the selected model. Because App Server is currently experimental,
the hook always keeps its local fallback.
List the TTS engines installed on your device:
termux-tts-enginesSelect an engine and locale with --tts-engine, --tts-language, and
--tts-region. For example, this configuration uses Google TTS with a Korean
locale, slightly lower pitch, and slightly faster speech:
command = "/data/data/com.termux/files/usr/bin/python3 <HOME>/.codex/hooks/termux_stop_notification.py --tts --tts-engine com.google.android.tts --tts-language ko --tts-region KR --tts-pitch 0.9 --tts-rate 1.1"Available voice options:
| Option | Meaning | Example |
|---|---|---|
--tts-engine |
Engine package reported by termux-tts-engines |
com.google.android.tts |
--tts-language |
Language code | ko |
--tts-region |
Region code | KR |
--tts-variant |
Engine-specific language variant | Varies by engine |
--tts-pitch |
Pitch multiplier; 1.0 is normal |
0.9 |
--tts-rate |
Speech-rate multiplier; 1.0 is normal |
1.1 |
Termux:API does not expose a portable option for selecting a specific named voice, such as a particular male or female voice. Choose that voice in the Android text-to-speech settings for the selected engine. Engines may ignore an unsupported language, region, or variant.
You can compare installed engines directly before changing the hook:
termux-tts-speak -e com.samsung.SMT -l ko -n KR "Samsung voice test"
termux-tts-speak -e com.google.android.tts -l ko -n KR "Google voice test"Installed engine package names vary by device. After changing the hook command, review and trust its updated definition in Codex when prompted.
- Reads the JSON payload supplied through standard input.
- Uses the last component of
cwdin the notification title. - Simplifies Markdown in
last_assistant_messageand limits it to 320 characters. - Optionally creates a brief semantic summary through the logged-in Codex App Server.
- Optionally reads the shortened response through Android system TTS.
- Uses a stable notification ID so each completion replaces the previous notification instead of creating an ever-growing list.
python3 -m unittest discover -s tests -v