你的子 Agent 不该把你叫醒

Agent Island 的全部工作就是一个承诺:当一个 Claude Code 或 Codex 线程结束、并且真的轮到你时,你会被打断 —— 全屏闹钟、一声提示、一个把你带回那个线程的按钮。而闹钟一旦为没人在等的回合响起来,这个承诺就死了

编排式运行正好就是这种情况。把一个大任务丢给 Codex,它会扇出子线程 —— 派生出来的执行者、复审轮次、压缩运行。每一个都和真会话一样往 ~/.codex/sessions 写 rollout 文件,每一个都每隔几分钟「结束一个回合」。用一个天真的扫描器,那就是一场弹窗风暴:一小时几十个闹钟,没一个需要行动。用户很合理地把它读成「这闹钟坏了」

那个没奏效的修法

每份 Codex rollout 开头都有一行 session_meta,带一个 originator 字段。自动化流量在那儿很好认:codex exec 运行、探针、桥接。1.4.1 按这些子串过滤,也确实干掉了一部分噪音

然后来了一份报告说子 Agent 线程还是在弹闹钟 —— 而且是从 Codex App 里面来的。真实会话文件的导出用一行就解释清楚了:

"originator": "Codex Desktop"   // 你亲手打开的交互式会话
"originator": "Codex Desktop"   // 你从没碰过的派生子 Agent

一个派生出来的子 Agent,带的 originator 和你亲手打开的会话一模一样。光靠这个字段,两者无法区分。过滤器查的是错的信号

真正的标记在哪

分界线原来就在 session_meta 自己身上。机器驱动的线程带着交互式会话永远不会有的派生标记:

  • thread_source: "subagent" 加上非空的 parent_thread_id —— 编排器的子任务
  • 派生/复审/压缩线程带 source: {"subagent": …} 对象,探针带 {"internal": …} —— 而直接会话带的是像 "cli" 这样的普通字符串
  • 无头自动化带 "exec""mcp" 这类 source 字符串

拿出问题的那台机器上 77 份真实 rollout 做样本,按这些标记来判,过滤掉了 61 个机器线程,同时保住了全部 16 个交互式会话。originator 检查只保留作旧式自动化的兜底

两档,一个开关

并非所有机器线程都该被同样对待,所以扫描器现在分两档:

  • 自动化 —— exec 运行、MCP 流量、内部探针。永不呈现。没人守着一次批量运行
  • 子 Agent —— 编排器的子任务。默认静默,但设置里有一个开关(「子线程也响铃」),留给那些真的在盯自己扇出、希望每个子任务结束都打断自己的人

这个开关比看起来重要:三种子 Agent 标记会同时出现在同一批 rollout 上,所以每一个都必须遵守这个设置。只要有一个标记被无条件过滤,开关就会悄悄失效 —— 这个 bug 我们在评审时抓到了,没有发出去

关于大小写的一道疤

早先有一版用的是白名单 —— 「保留 originator 以 codex 开头的会话」。而 Codex App 上报的是 Codex Desktop,大写 C。前缀检查没匹配上,于是每一个桌面会话都被归成自动化,闹钟对最需要它的那批人彻底静音了。留下两条教训:匹配要忽略大小写;以及,宁可对已知的机器标记做黑名单,也不要对「看起来像人」做白名单 —— 新的合法 originator 会不断出现,而白名单恰恰会朝错误的那一侧失效


这个过滤器随 Agent Island 1.5.1 在 macOS 与 Windows 上一起发布,同时带风暴收敛:就算你打开了子 Agent 闹钟,一串集中结束也只会收敛成最新那一回合的一个闹钟