「通知提醒」是 a4agent 自 0.6.0 起的页签:把「任务跑完了但人不在电脑前」「AI 卡在等你作答或审批」这两件事补上。它提供两条互相独立、可各自关闭的通道——手机推送(ntfy)与桌面横幅(系统通知),并通过对各端的会话 hook 让你在手机上直接回答 AI 的提问、审批权限请求。本文章介绍两条通道的工作方式、统一的通知样式、会话 hook 覆盖的端与挂载方式、配置存放位置与常见问题。
1. 功能概述
| 通道 | 走法 | 适合场景 |
|---|---|---|
| 手机推送 | ntfy → 手机 App,绑定话题与事件开关 | 离开工位,实时掌握任务与会话结果 |
| 桌面横幅 | WinRT 原生 toast,点击唤回窗口 | 已在电脑前但窗口最小化 |
成功/失败/超时] A --> C[会话 hook
提问/权限/完成] A --> D[OpenCode 服务事件] B --> E[统一样式拼装
话题/类型/应用] C --> E D --> E E --> F[手机推送
ntfy] E --> G[桌面横幅
WinRT toast] style A fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style B fill:#d6eaf8,stroke:#1a6b8a,stroke-width:2px style C fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style D fill:#d6eaf8,stroke:#1a6b8a,stroke-width:2px style E fill:#ffecd6,stroke:#e67e22,stroke-width:2px style F fill:#d5f5e3,stroke:#27ae60,stroke-width:2px style G fill:#d5f5e3,stroke:#27ae60,stroke-width:2px
两条通道都不依赖「有一个生效的配置方案」:任务终态来自任务下发,会话事件来自你在各端正常使用的会话。通知提醒默认关闭推送,不开启不影响任何既有行为。
2. 手机推送(ntfy)
2.1 订阅与话题
话题名随机生成(a4ag- 前缀,与 a4phone 的 a4p- 区分,避免双装用户推送混杂),页面提供二维码与订阅地址,用手机 ntfy App(iOS App Store / Android GitHub Releases)扫码或输入话题即可订阅。
话题名即凭据:知晓话题即可向你的手机推送。因此话题可一键重置换名,重置后需重新订阅。数据敏感时建议在页面把服务器改为自建 ntfy 并填写访问令牌(令牌明文存于数据目录,目录本身已收 ACL 权限)。
2.2 推送时机开关
| 事件 | 默认 | 说明 |
|---|---|---|
| 成功 | 开 | 任务与会话正常结束 |
| 失败 | 开 | 附带真实报错原因 |
| 超时 | 开 | 到超时点被强杀 |
| 已取消 | 关 | 那是你自己按的,无需再被告知一次 |
2.3 统一样式(0.6.2 起)
桌面横幅与手机推送此前是两套排版,0.6.2 起合并为同一份三行制结构,任何一端的通知都长一个样:
重构登录页的表单校验 ← 话题名称(哪个会话 / 任务)
已完成 ← 通知类型(已完成 / 失败 / 超时 / 权限申请 / 问题作答)
OpenCode ← 应用名称(哪个引擎)
- 下发任务的应用名称带任务编号(如
任务 #7 · Claude Code); - 六端会话的话题名称取自会话第一条用户消息(取不到退回项目目录名),OpenCode 用会话标题;
- 手机推送在三行之后附「AI 最后输出」(最多 1000 字)——此前只有六端 hook 带,现在 OpenCode 会话与下发任务的通知同样带上;桌面横幅保持三行短版(1000 字会把系统 toast 撑爆);
- 全部通知为纯文字(0.6.2 起移除了状态 Emoji),页面内的功能图标不受影响。
3. 桌面横幅
- 用 WinRT 原生 toast(ctypes 直调 COM,零第三方依赖)实现,来源区显示小 logo + a4agent;0.6.2 换用新身份
eogee.a4agent.v2修复了此前来源区只显示原始身份串且无图标的问题(系统通知平台会缓存坏身份,写对注册表也不回读),旧身份键自动清理。 - 点击横幅唤回窗口;无论窗口是否可见都会弹——0.6.2 修复了 OpenCode 会话终局在窗口开着时既不弹横幅也无声音的问题。
- 桌面横幅有独立总开关,默认开(任务下发的桌面提醒是既有行为,不因新增开关而消失;该开关管的是 hook 链路的提问 / 权限请求 / 完成弹窗)。
- 桌面弹窗由常驻进程代发:hook / 插件触发时把请求原子写入数据目录的
notify-queue/(安装版为%APPDATA%\a4agent\notify-queue\),由常驻进程逐条弹出后删除。原因是部分宿主(如 ZCode)会在 hook 命令退出时杀掉整棵进程树,hook 内直接弹来不及显示。
4. 会话交互:在手机上作答与审批
4.1 覆盖的端
| 端 | 接入方式 | 是否写宿主文件 |
|---|---|---|
| Claude Code | ~/.claude/settings.json 的 hooks(Stop / PreToolUse / PermissionRequest) |
写(合并式,卸载时只摘自己的条目) |
| Qoder | ~/.qoder/settings.json,同构 Claude |
写 |
| WorkBuddy | ~/.workbuddy/settings.json,同构 Claude |
写 |
| Codex | ~/.codex/config.toml,用一对标记行包裹 |
写 |
| ZCode | ~/.zcode/cli/config.json 的 hooks.events(需 hooks.enabled=true) |
写 |
| dsh | 挂载内置 Cordis 插件(进程内加载),dsh 无外部 hook 协议 | 不写宿主配置,只改 profile 的 patch 层 |
| OpenCode | 无 hook 协议,「注册」= 开启 a4agent 侧的服务事件监听(SSE) | 不写任何文件 |
4.2 三类交互
| 交互 | 触发 | 手机侧 |
|---|---|---|
| 提问作答 | AI 调用 AskUserQuestion |
收到问题,点选或文字作答后回写会话继续执行 |
| 权限审批 | 待批准的读写操作 | 选「批准 / 拒绝 / 始终批准」 |
| 计划审批 | 进入计划模式 | 按计划审批上限等待,超时按放行处理,不卡死流程 |
两种等待模式:「在家」优先不阻塞终端(作答等待 60s、计划审批 300s);「外出」放开到分钟级,适用于移动场景。
Codex 的专门适配:它没有注入答案的能力,因此改为阻断该次工具调用、把答案写进 permissionDecisionReason 让模型直接采用,语义上等价于「用户已作答」。
4.3 挂载与卸载
各引擎的 hook 挂载 / 卸载都在页面上完成,幂等(已注册时不重复写)。卸载只摘 a4agent 自己写入的条目,判定用「命令含 a4agent 且含 hook」,不会误删用户自己的 a4phone(a4p)或其它 hook 命令;其余配置原样保留。
点注册] --> B{目标端类型} B -->|Claude/Qoder/WorkBuddy| C[写 settings.json
hooks 段] B -->|Codex| D[写 config.toml
标记块内] B -->|ZCode| E[写 hooks.events
并启用 hooks] B -->|dsh| F[挂载内置
Cordis 插件] B -->|OpenCode| G[仅开启
服务事件监听] C --> H[卸载时只摘
a4agent 的条目] D --> H E --> H style A fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style B fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style C fill:#d6eaf8,stroke:#1a6b8a,stroke-width:2px style D fill:#d6eaf8,stroke:#1a6b8a,stroke-width:2px style E fill:#d6eaf8,stroke:#1a6b8a,stroke-width:2px style F fill:#ffecd6,stroke:#e67e22,stroke-width:2px style G fill:#fdebd0,stroke:#b7950b,stroke-width:2px style H fill:#d5f5e3,stroke:#27ae60,stroke-width:2px
5. 配置存放
全部配置落在运行时数据目录的 phone_config.json(安装版为 %APPDATA%\a4agent\phone_config.json,开发模式在 backend/database/),字段如下:
| 字段 | 说明 |
|---|---|
enabled |
手机推送总开关(默认关) |
server |
ntfy 服务器,默认 https://ntfy.sh,可改自建 |
topic |
话题名(a4ag- 前缀,可一键重置) |
token |
访问令牌,仅自建 ntfy / 开启鉴权时需要 |
events |
成功 / 失败 / 超时 / 已取消 四个推送开关 |
hook.mode |
home(终端优先)或 out(外出,等更久) |
hook.timeout / hook.plan_timeout |
作答与计划审批的等待上限(秒) |
hook.opencode |
OpenCode 服务事件监听的注册位 |
desktop |
桌面横幅总开关(默认开) |
其余相关文件:notify-queue/(桌面弹窗请求队列)、last_session.json(最近一次会话记录,供手机作答回写定位)、opencode.json(OpenCode 地址与 DPAPI 加密的密码)。完整清单见数据目录与日志说明。
6. 常见问题
问:不开启通知提醒,任务和会话会有影响吗?
答:不会。手机推送默认关闭、会话 hook 需你在页面主动注册后才生效;开启前的会话行为与旧版完全一致。
问:话题名泄露了怎么办?
答:话题名即凭据,泄露后在页面一键重置话题名并重新订阅即可。数据敏感时建议改用自己的 ntfy 服务器并填写访问令牌。
问:为什么有些通知没有「AI 最后输出」?
答:桌面横幅只保留三行短版(1000 字会把系统 toast 撑爆),「AI 最后输出」只追加在手机推送里,最多 1000 字。
问:OpenCode 注册会不会改我的 OpenCode 配置?
答:不会。OpenCode 没有 hook 协议,注册只是开启 a4agent 侧对其服务的事件监听(SSE 订阅),不写它的任何文件;批复权限时走它的 permission API 回给服务。
问:卸载 hook 会把我自己配的 hook 删掉吗?
答:不会。卸载只摘「命令含 a4agent 且含 hook」的条目,你自己或其它工具(如 a4phone)写入的 hook 原样保留。
问:任务跑完时窗口是关着的,我怎么知道?
答:窗口隐藏(关窗常驻)时任务到终态会弹原生系统通知并闪烁任务栏图标,点击通知唤回窗口;窗口开着时则在页面内提示。机制与任务下发文档一致。
举手提问