多会话的编码 Agent 状态要按紧急程度排序

单独一个 Claude Code 或 Codex 的 logo 看起来很简单,直到好几个会话同时在跑。一个线程可能在等答案,另一个可能卡住了,第三个可能还在工作。如果监控用「哪份记录最近被写过」来代表这家服务商,价值很低的活动就会盖住那个真正需要用户的会话

正确的归约分两步:先独立给每个会话分类,再按用户紧急程度给这些状态排序。新鲜度只在两个同等紧急的状态打平时才登场

按最新优先聚合为什么会失败

修改时间回答的是「哪个会话最后写过」,它回答不了「哪个会话值得被注意」。一个后台工作者可能在另一个线程停在授权提示之后,继续追加例行进展。一次刚被确认过的交接,也可能还留在「需要你」的窗口里,而它的同伴已经恢复了有用的工作

只看新鲜度的规则会带来三种常见失败:

  • 一个在工作的会话盖住了另一个更老线程里没人回答的问题
  • 一次已确认的交接把服务商图标冻住,尽管另一个会话正在跑
  • 一次普通的空闲写入藏起了一个本该被查看的卡住会话

用一张显式的优先级表

Agent Island v1.7.1 在已发布的 macOS 与 Windows 活动监控里用的是同一套排序:

未确认的需要你 = 4
卡住           = 3
工作中         = 2
已确认的需要你 = 1
空闲 / 需认证 / 被限流 = 0

没人回答的交接排第一,因为下一个有用的动作归用户。卡住排第二,因为一个异常不该消失在正常活动后面。没有更紧急状态时,工作中保持可见。已确认的交接掉到工作中之下:它仍然是这个线程历史的一部分,但在用户处理完之后,它不该再把服务商指示器钉住

在这个特定的选择器里,空闲、认证和限流状态使用基础聚合优先级。它们的详细呈现与错误信号在别处处理,这张表不该被当成一套通用的严重性分类法

让新鲜度打平局,而不是定政策

优先级定好之后,监控先按优先级排,再按修改时间排。只有当两个候选的可行动性相同时,更新的那个才胜出。这让结果保持确定,同时不让时间戳重新定义含义

ranked = sessions
  .filter(provider)
  .map(session => (session, priority(session)))
  .sort(priority descending, modified descending)

providerState = ranked.first

这个区分对测试很要紧。测试套件应该让状态和时间各自独立变化:一个更老的、没人回答的交接必须赢过一个更新的工作中会话;而两个都在工作时,更新的那个赢下平局

不要把提醒队列也压成一个会话

服务商指示器和提醒队列解决的是不同问题。logo 需要一个有代表性的状态,提醒则需要保住每一次未完成的交接。因此 Agent Island 挑一个最佳会话用于显示,同时另外收集每一个「需要你」的线程,最新的在前

这种分离防止一个线程的确认取消掉另一个线程的闹钟。提醒身份包含服务商、会话和回合,所以两个在等的线程仍然是两份独立的义务,哪怕菜单栏上只显示一个聚合状态

一张紧凑的多会话测试矩阵

  1. 更老的未回答需要你 + 更新的工作中:显示需要你
  2. 更老的卡住 + 更新的工作中:显示卡住
  3. 已确认的需要你 + 工作中:显示工作中
  4. 两个工作中会话:选修改时间更新的那个
  5. 两次未回答的交接:显示更新的那个,两条提醒都保留
  6. 确认两次交接中的一个:另一条提醒仍然可行动
  7. 某家服务商没有会话:返回空闲,不选任何线程

聚合状态证明不了什么

被选中的状态是一个显示决定,不是「其它会话不存在了」的证据。它绝不该覆盖那些用于深链、提醒和诊断的逐线程记录。保留完整集合的监控,之后可以改变呈现策略而不丢掉底层事实

这里描述的优先级实现,在两个平台打了 tag 的 v1.7.1 版本 源码里都能看到。更大的设计原则适用于任何把并发工作者压成一块状态界面的开发者工具:按可行动性聚合、保留逐个工作者的身份、只用新鲜度来裁决同级