What is shown
- The current agent work state renders in the menu bar icon and in the first status row of the menu.
- Health status is hidden while work is active; it returns once all sessions are idle.
- A root “Context” item opens a submenu with recent sessions instead of expanding them in the root menu.
- A “Devices” block in the root menu lists paired devices only (from
node.list), not client/presence entries. - A root “Usage” section appears below Context when provider usage snapshots are available, followed by cost details when available. Cost totals for Today and the daily chart use the Mac’s local time zone.
- When two or more Gateways are available, the first status row includes the primary Gateway name and a root “Gateways” section lists every Gateway with its health and primary marker. Select a row to open or focus that Gateway’s dashboard; hold Option to reveal “Set as Primary…” for eligible saved Gateways.
- Quick Chat opens the floating main-session composer; its current global shortcut appears beside the item.
- Settings… (Cmd-,) opens Dashboard settings. App and device preferences live under This Mac, voice controls under Talk, and app update preferences under Updates.
- Connection… opens the native Connection window, a standard macOS settings window with Connection and Gateways toolbar tabs, plus Debug while the developer toggle is enabled. It remains available when the Gateway is unreachable.
- About OpenClaw opens the standard macOS About panel with version, build information, and credits.
State model
- Source:
WorkActivityStore(apps/macos/Sources/OpenClaw/WorkActivityStore.swift). - Events arrive as
ControlAgentEventwith arunId; the handler (ControlChannel.routeWorkActivity) readssessionKeyfrom the event payload and defaults to"main"if absent. - Priority: the main session (
sessionKey == "main"by default) always wins. If main is active, its state shows immediately. If main is idle, the most recently active non-main session shows instead. The store does not flip mid-activity; it only switches when the current session goes idle or main becomes active. - Activity kinds:
job: high-level command execution (state: started|streaming|done|error|...).tool:phase: start|resultwithname, optionalmeta/args.
IconState enum (Swift)
idleworkingMain(ActivityKind)workingOther(ActivityKind)overridden(ActivityKind)(debug override)
ActivityKind -> badge symbol
ActivityKind wraps a ToolKind (bash, read, write, edit, attach, other) or a bare job. Each maps to an SF Symbol badge drawn over the critter icon (IconState.badgeSymbolName):
Visual mapping
idle: normal critter, no badge.workingMain: badge with symbol, full tint (.primaryprominence), leg “working” animation.workingOther: badge with symbol, muted tint (.secondaryprominence), no scurry.overridden: uses the chosen symbol/tint regardless of real activity.
Context submenu
- The root menu shows one “Context” row with a session count/status; it opens a submenu (
MenuSessionsInjector). - The submenu header shows the active session count for the last 24 hours.
- Each session row keeps its token bar, age, preview, thinking/verbose toggle, reset, compact, and delete actions.
- Loading, disconnected, and session-load error messages render inside the Context submenu.
- Usage and cost sections stay root-level below Context so they remain glanceable without opening the submenu.
Status row text (menu)
- With two or more Gateways, the connection label appends the primary Gateway’s catalog display name, such as
OpenClaw Active — Mac Studio. - While work is active:
<Session role> · <activity label>("\(roleLabel) · \(activity.label)"inMenuContentView), where role label isMainorOther. - When idle: falls back to the health summary.
Event ingestion
- Source: control-channel
agentevents, routed byControlChannel.routeWorkActivity(from:). - Parsed fields:
stream: "job"withdata.statefor start/stop.stream: "tool"withdata.phase,data.name, optionaldata.meta/data.args.
- Tool labels come from
ToolDisplayRegistry.resolve(name:args:meta:); unresolved names fall back to the raw tool name.
Debug override
- Enable the developer toggle in Dashboard → Settings → This Mac → Developer, then open Connection… → Debug → Icon override:
System (auto)(default)Working: main/Working: other(per tool kind: bash, read, write, edit, other)Idle
- Stored under
UserDefaultskeyopenclaw.iconOverride; mapped toIconState.overridden.
Testing checklist
- Trigger main session job: icon switches immediately and status row shows the main label.
- Trigger non-main session job while main is idle: icon/status shows the non-main session; stays stable until it finishes.
- Start main while another session is active: icon flips to main instantly.
- Rapid tool bursts: badge does not flicker (2s grace window before clearing a finished tool,
WorkActivityStore.toolResultGrace). - Health row reappears once all sessions are idle.