菜单栏状态逻辑

显示内容

  • 我们会在菜单栏图标和菜单的第一行状态中显示当前 Agent 的工作状态。
  • 工作进行时会隐藏健康状态;当所有 Session 都空闲时才会重新显示。
  • 菜单中的”Nodes”区块只列出设备(通过 node.list 配对的节点),不包括客户端/在线状态条目。
  • 当有 Provider 使用快照数据时,会在 Context 下方显示”Usage”部分。

状态模型

  • Session:事件会携带 runId(每次运行)和负载中的 sessionKey。“主” Session 的键是 main;如果没有,则回退到最近更新的 Session。
  • 优先级:main 始终优先。如果 main 处于活动状态,会立即显示它的状态。如果 main 空闲,则显示最近活动的非 main Session。我们不会在活动中途切换;只有当前 Session 空闲或 main 变为活动状态时才会切换。
  • 活动类型:
    • job:高级命令执行(state: started|streaming|done|error)。
    • toolphase: start|result,带有 toolNamemeta/args

IconState 枚举(Swift)

  • idle
  • workingMain(ActivityKind)
  • workingOther(ActivityKind)
  • overridden(ActivityKind)(调试覆盖)

ActivityKind → 图标

  • exec → 💻
  • read → 📄
  • write → ✍️
  • edit → 📝
  • attach → 📎
  • 默认 → 🛠️

视觉映射

  • idle:正常的小动物图标。
  • workingMain:带图标徽章,完整着色,腿部”工作中”动画。
  • workingOther:带图标徽章,柔和着色,无奔跑动画。
  • overridden:无论活动状态如何,都使用选定的图标/着色。

状态行文本(菜单)

  • 工作进行时:<Session 角色> · <活动标签>
    • 示例:Main · exec: pnpm testOther · read: apps/macos/Sources/OpenClaw/AppState.swift
  • 空闲时:回退到健康状态摘要。

事件接收

  • 来源:控制通道 agent 事件(ControlChannel.handleAgentEvent)。
  • 解析字段:
    • stream: "job" 带有 data.state 用于启动/停止。
    • stream: "tool" 带有 data.phasename、可选的 meta/args
  • 标签:
    • execargs.command 的第一行。
    • read/write:缩短的路径。
    • edit:路径加上从 meta/差异计数推断的变更类型。
    • 回退:工具名称。

调试覆盖

  • Settings ▸ Debug ▸ “Icon override” 选择器:
    • System (auto)(默认)
    • Working: main(按工具类型)
    • Working: other(按工具类型)
    • Idle
  • 通过 @AppStorage("iconOverride") 存储;映射到 IconState.overridden

测试清单

  • 触发 main Session 任务:验证图标立即切换,状态行显示 main 标签。
  • 在 main 空闲时触发非 main Session 任务:图标/状态显示非 main;保持稳定直到完成。
  • 在其他 Session 活动时启动 main:图标立即切换到 main。
  • 快速工具爆发:确保徽章不闪烁(工具结果有 TTL 宽限期)。
  • 所有 Session 空闲后健康状态行重新出现。