背景
今天希望让 Codex 在两类场景里播放声音提醒,而不是只发文字消息:
- 任务完成、准备回复最终结果时
- 需要用户手动授权、登录、确认或执行外部操作时
最开始直接尝试过 afplay 和 say。afplay /System/Library/Sounds/Glass.aiff 返回 AudioQueueStart failed (-66680),说明当前沙箱环境无法直接启动音频队列。后来在沙箱外授权执行 say 后,声音可以正常播放,因此最终选择使用 macOS 自带的 say 命令作为提醒方式。
全局授权
Codex 的全局规则文件是:
/Users/ira/.codex/rules/default.rules
其中已经包含:
prefix_rule(pattern=["say"], decision="allow")
这表示之后 Codex 可以直接执行下面的提醒命令,不用每次都重新请求命令执行权限:
say "任务完成了"
say "需要你授权"
为什么不用 Skill
一开始考虑过创建一个 skill,但后来确认 skill 不适合这个需求。skill 只有在任务语义触发时才会被加载,而“任务完成时提醒我”和“需要授权时提醒我”是跨所有会话的全局行为,不是某一类具体任务。
如果新会话没有触发这个 skill,就无法保证声音提醒规则生效。更合适的方式是使用 Codex 的全局 hooks。
参考思路
参考文章:Self-updating agent skills。
这篇文章的思路是:把需要跨会话稳定执行的行为放到 agent 或 host 生命周期机制里,而不是依赖模型在上下文里“记得”。结合当前 Codex CLI 的功能检查,确认当前版本中 hooks stable true,因此采用 ~/.codex/hooks.json。
声音提醒脚本
创建脚本:
/Users/ira/.codex/hooks/sound-notify/notify.sh
内容:
#!/usr/bin/env bash
set -euo pipefail
message="${1:-Codex needs your attention}"
/usr/bin/say "$message"
并添加可执行权限:
chmod +x /Users/ira/.codex/hooks/sound-notify/notify.sh
Hooks 配置
创建或更新:
/Users/ira/.codex/hooks.json
内容:
{
"hooks": {
"PermissionRequest": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "/Users/ira/.codex/hooks/sound-notify/notify.sh \"Codex needs your approval\"",
"timeout": 10
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "/Users/ira/.codex/hooks/sound-notify/notify.sh \"Codex task complete\"",
"timeout": 10
}
]
}
]
}
}
PermissionRequest 用于需要用户授权时播放 Codex needs your approval;Stop 用于 Codex 停止本轮工作、准备返回结果时播放 Codex task complete。
验证
验证 JSON 格式:
jq . /Users/ira/.codex/hooks.json
验证脚本可以独立播放声音:
/Users/ira/.codex/hooks/sound-notify/notify.sh "Codex sound hook test"
额外验证:
say "配置完成了"
声音可以正常播放。
注意事项
- 新会话通常会读取
~/.codex/hooks.json。 - 如果旧会话没有立即生效,重开会话或重启 Codex app 后再试。
Stop事件可能会在每轮回复结束时触发,不只是在长任务完全结束时触发。PermissionRequest是否在所有授权场景都触发,取决于当前 Codex 版本的 hook 事件实现。
当前结论
使用全局 hooks 比 skill 更适合这个需求。目前配置目标是:所有会话任务完成时自动发声提醒,所有会话需要用户授权时自动发声提醒,并使用已验证可播放的 macOS say 命令。