a4phone 在 AI 助手一轮任务结束时(Claude Code / Codex 的 Stop 事件,DSH 的 turn/end 事件)自动触发通知:电脑弹出系统通知,同时把任务结果与 AI 最后输出的一段话推送到手机,让你离开电脑也能看到任务的实际产出。本文章介绍该功能的触发机制、通知内容构成与去重规则。

1. 功能概述

维度 说明
触发事件 Claude Code / Codex:Stop;DSH:turn/end(reason.kind 为 completed)
电脑端 系统弹窗通知(Windows 气泡 / macOS 通知 / Linux notify-send)
手机端 ntfy.sh 推送,含目录、会话 ID 与 AI 最后输出
是否可关闭 配置 topic 为空时只弹电脑通知,不推手机
适用版本 全部版本,DSH 支持自 1.1.6 起

1.1 事件流程

graph LR A[AI 助手
完成任务] --> B[Stop / turn/end
事件触发] B --> C[读取会话记录
transcript] C --> D[抽取 AI
最后输出] D --> E[截断到
1000 字符] E --> F[电脑系统通知] E --> G[ntfy.sh 推送手机] B --> H[记录最近会话
last.json] H --> I[供远程续聊使用] 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:#ffecd6,stroke:#e67e22,stroke-width:2px style E fill:#fdebd0,stroke:#b7950b,stroke-width:2px style F fill:#d5f5e3,stroke:#27ae60,stroke-width:2px style G fill:#fadbd8,stroke:#c0392b,stroke-width:2px style H fill:#fdebd0,stroke:#b7950b,stroke-width:2px style I fill:#d5f5e3,stroke:#27ae60,stroke-width:2px

2. 通知内容构成

手机推送消息由三部分组成:

  1. 任务状态:固定为 任务已完成
  2. 上下文信息:工作目录(目录: {cwd})与会话 ID 前 8 位(会话: {session_id}
  3. AI 最后输出:从会话记录中抽取的 AI 最后一条回复文本,超出 1000 字符时截断并在末尾添加省略号
任务已完成
目录: C:\my-project
会话: ab12cd34

AI 最后输出:
已完成全部重构,测试通过。主要改动包括:
1. 拆分配置模块,消除循环依赖
2. 补充 3 个单元测试用例
...

2.1 消息标题

推送标题按触发方动态显示,便于区分来源:

触发方 标题
Claude Code Claude Code
Codex Codex
DSH DSH

3. AI 最后输出的抽取机制

a4phone 读取 Stop 事件输入中的 transcript_path(会话 JSONL 记录),按 Agent 不同解析最后一条 AI 回复文本。

3.1 各 Agent 的解析规则

Agent 记录类型 抽取方式
Claude Code type="assistant" 消息 message.content[]type="text" 的 text 块
Codex type="response_item" 的 assistant 消息 payload.content[]type="output_text" 的 text 块
Codex 兜底 type="event_msg"task_complete payload.last_agent_message 字段
DSH 会话事件缓存 插件缓存最近一次 assistant/message 的文本输出

Codex 的 task_complete 事件比 assistant 消息晚约 1.4 秒落盘,Stop 触发时往往尚未写入,因此 a4phone 优先读取随消息即时写入的 response_itemtask_complete 仅作文件稳定时的兜底。

3.2 截断策略

会话记录可能非常大,a4phone 只取最后一段文本并截断到前 1000 字符(约 3000 字节,留足 ntfy 4KB 消息上限余量),兼顾推送长度限制与可读性。

4. 最近会话记录

Stop 事件触发时,a4phone 同时把最近会话信息写入 ~/.a4phone/last.json

{
  "session_id": "ab12cd34ef56...",
  "cwd": "C:\\my-project",
  "agent": "Claude Code",
  "transcript_path": "C:\\Users\\xxx\\.claude\\projects\\...\\xxx.jsonl",
  "ts": 1755324000000
}

该记录是远程续聊路由的核心依据:守护进程按 agent 字段自动选择续聊方式(Claude Code / Codex / DSH),按 session_id 恢复会话。

5. 去重与抑制

场景 处理方式
续聊子进程(A4P_RESUME=1 Stop 事件不再重复推送"任务已完成",结果由续聊流程统一推回手机
DSH 续聊轮次 插件检测到会话正在被续聊服务驱动(inflight),跳过重复的任务完成通知
DSH subagent 轮次 仅日志记录,不推送(避免 subagent 完成导致刷屏)

6. 关闭手机推送

如果只想保留电脑弹窗、不推送手机,把 ~/.a4phone/config.json 中的 topic 置空即可。恢复推送时重新运行 a4p setup 重新生成话题,或手动填写话题名称。

{
  "topic": "",
  "server": "https://ntfy.sh"
}

详细配置项见配置说明