Lógica de estado de la barra de menú

Qué se muestra

  • Mostramos el estado actual del trabajo del agente en el icono de la barra de menú y en la primera fila de estado del menú.
  • El estado de salud se oculta mientras hay trabajo activo; vuelve cuando todas las sesiones están inactivas.
  • El bloque “Nodes” en el menú lista solo dispositivos (nodos emparejados vía node.list), no entradas de cliente/presencia.
  • Una sección “Usage” aparece bajo Context cuando hay instantáneas de uso del proveedor disponibles.

Modelo de estado

  • Sesiones: los eventos llegan con runId (por ejecución) más sessionKey en el payload. La sesión “main” es la clave main; si está ausente, recurrimos a la sesión actualizada más recientemente.
  • Prioridad: main siempre gana. Si main está activa, su estado se muestra inmediatamente. Si main está inactiva, se muestra la sesión no-main más recientemente activa. No alternamos durante la actividad; solo cambiamos cuando la sesión actual pasa a inactiva o main se activa.
  • Tipos de actividad:
    • job: ejecución de comando de alto nivel (state: started|streaming|done|error).
    • tool: phase: start|result con toolName y meta/args.

Enum IconState (Swift)

  • idle
  • workingMain(ActivityKind)
  • workingOther(ActivityKind)
  • overridden(ActivityKind) (override de debug)

ActivityKind → glifo

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

Mapeo visual

  • idle: criatura normal.
  • workingMain: badge con glifo, tinte completo, animación de patas “trabajando”.
  • workingOther: badge con glifo, tinte atenuado, sin movimiento.
  • overridden: usa el glifo/tinte elegido sin importar la actividad.

Texto de la fila de estado (menú)

  • Mientras hay trabajo activo: <Rol de sesión> · <etiqueta de actividad>
    • Ejemplos: Main · exec: pnpm test, Other · read: apps/macos/Sources/OpenClaw/AppState.swift.
  • Cuando está inactivo: recurre al resumen de salud.

Ingesta de eventos

  • Fuente: eventos agent del canal de control (ControlChannel.handleAgentEvent).
  • Campos parseados:
    • stream: "job" con data.state para inicio/fin.
    • stream: "tool" con data.phase, name, opcionales meta/args.
  • Etiquetas:
    • exec: primera línea de args.command.
    • read/write: ruta abreviada.
    • edit: ruta más tipo de cambio inferido de meta/conteos de diff.
    • fallback: nombre de la herramienta.

Override de debug

  • Settings ▸ Debug ▸ “Icon override” picker:
    • System (auto) (por defecto)
    • Working: main (por tipo de herramienta)
    • Working: other (por tipo de herramienta)
    • Idle
  • Almacenado vía @AppStorage("iconOverride"); mapeado a IconState.overridden.

Lista de verificación de pruebas

  • Disparar un job de sesión main: verificar que el icono cambia inmediatamente y la fila de estado muestra la etiqueta de main.
  • Disparar un job de sesión no-main mientras main está inactiva: el icono/estado muestra no-main; permanece estable hasta que termina.
  • Iniciar main mientras otra está activa: el icono cambia a main instantáneamente.
  • Ráfagas rápidas de herramientas: asegurar que el badge no parpadea (gracia de TTL en resultados de herramientas).
  • La fila de salud reaparece cuando todas las sesiones están inactivas.