{
  "title": "Menu Bar Status Logic",
  "content": "* We surface the current agent work state in the menu bar icon and in the first status row of the menu.\n* Health status is hidden while work is active; it returns when all sessions are idle.\n* The “Nodes” block in the menu lists **devices** only (paired nodes via `node.list`), not client/presence entries.\n* A “Usage” section appears under Context when provider usage snapshots are available.\n\n* Sessions: events arrive with `runId` (per-run) plus `sessionKey` in the payload. The “main” session is the key `main`; if absent, we fall back to the most recently updated session.\n* Priority: main always wins. If main is active, its state is shown immediately. If main is idle, the most recently active non‑main session is shown. We do not flip‑flop mid‑activity; we only switch when the current session goes idle or main becomes active.\n* Activity kinds:\n  * `job`: high‑level command execution (`state: started|streaming|done|error`).\n  * `tool`: `phase: start|result` with `toolName` and `meta/args`.\n\n## IconState enum (Swift)\n\n* `idle`\n* `workingMain(ActivityKind)`\n* `workingOther(ActivityKind)`\n* `overridden(ActivityKind)` (debug override)\n\n### ActivityKind → glyph\n\n* `exec` → 💻\n* `read` → 📄\n* `write` → ✍️\n* `edit` → 📝\n* `attach` → 📎\n* default → 🛠️\n\n* `idle`: normal critter.\n* `workingMain`: badge with glyph, full tint, leg “working” animation.\n* `workingOther`: badge with glyph, muted tint, no scurry.\n* `overridden`: uses the chosen glyph/tint regardless of activity.\n\n## Status row text (menu)\n\n* While work is active: `<Session role> · <activity label>`\n  * Examples: `Main · exec: pnpm test`, `Other · read: apps/macos/Sources/OpenClaw/AppState.swift`.\n* When idle: falls back to the health summary.\n\n* Source: control‑channel `agent` events (`ControlChannel.handleAgentEvent`).\n* Parsed fields:\n  * `stream: \"job\"` with `data.state` for start/stop.\n  * `stream: \"tool\"` with `data.phase`, `name`, optional `meta`/`args`.\n* Labels:\n  * `exec`: first line of `args.command`.\n  * `read`/`write`: shortened path.\n  * `edit`: path plus inferred change kind from `meta`/diff counts.\n  * fallback: tool name.\n\n* Settings ▸ Debug ▸ “Icon override” picker:\n  * `System (auto)` (default)\n  * `Working: main` (per tool kind)\n  * `Working: other` (per tool kind)\n  * `Idle`\n* Stored via `@AppStorage(\"iconOverride\")`; mapped to `IconState.overridden`.\n\n* Trigger main session job: verify icon switches immediately and status row shows main label.\n* Trigger non‑main session job while main idle: icon/status shows non‑main; stays stable until it finishes.\n* Start main while other active: icon flips to main instantly.\n* Rapid tool bursts: ensure badge does not flicker (TTL grace on tool results).\n* Health row reappears once all sessions idle.",
  "code_samples": [],
  "headings": [
    {
      "level": "h2",
      "text": "What is shown",
      "id": "what-is-shown"
    },
    {
      "level": "h2",
      "text": "State model",
      "id": "state-model"
    },
    {
      "level": "h2",
      "text": "IconState enum (Swift)",
      "id": "iconstate-enum-(swift)"
    },
    {
      "level": "h3",
      "text": "ActivityKind → glyph",
      "id": "activitykind-→-glyph"
    },
    {
      "level": "h3",
      "text": "Visual mapping",
      "id": "visual-mapping"
    },
    {
      "level": "h2",
      "text": "Status row text (menu)",
      "id": "status-row-text-(menu)"
    },
    {
      "level": "h2",
      "text": "Event ingestion",
      "id": "event-ingestion"
    },
    {
      "level": "h2",
      "text": "Debug override",
      "id": "debug-override"
    },
    {
      "level": "h2",
      "text": "Testing checklist",
      "id": "testing-checklist"
    }
  ],
  "url": "llms-txt#menu-bar-status-logic",
  "links": []
}