Skip to content

Agent Fleet view: live status board for all owner agents - #3684

Closed
maxmoneycash wants to merge 3 commits into
block:mainfrom
maxmoneycash:agent-fleet-view
Closed

Agent Fleet view: live status board for all owner agents#3684
maxmoneycash wants to merge 3 commits into
block:mainfrom
maxmoneycash:agent-fleet-view

Conversation

@maxmoneycash

Copy link
Copy Markdown

Problem

Owners running several agents — across channels, and increasingly across machines — have no single place to see what their fleet is doing. Today that means opening each agent's profile panel or channel one at a time to answer "which of my agents are working right now, which one crashed overnight, and what has all of this cost me?"

A commenter on #2716 describes building exactly this on top of kind 24200 ("I'm building a fleet view on top of kind:24200 and presence right now"). The desktop app is the natural home for that owner-side surface: it already holds the owner key, decrypts observer frames app-wide, and archives turn metrics locally.

What this adds

A new Agent Fleet view (/fleet, sidebar entry behind a new fleet preview feature, consistent with Pulse/Projects/Workflows) with one card per owned agent:

  • Live status — working / idle / needs-attention / offline, derived from the existing observer-frame pipeline (kind 24200 → activeAgentTurnsStore → unified agentWorkingSignal), with the same AgentStatusBadge used on the Agents page.
  • Current activity — working cards show the live channel chip with a ticking elapsed counter; idle cards show a headline from the agent's latest transcript item; crashed cards show the same friendly error copy as ManagedAgentRow (friendlyAgentLastError).
  • Last seen — newest observer frame or managed-agent lifecycle timestamp.
  • Usage totals — tokens and estimated cost aggregated from NIP-AM kind 44200 turn metrics, with per-session cumulative reconciliation so cumulative series are never double-counted (unreported fields render as "—", never a fake 0).
  • A summary strip with fleet-wide status counts and total tokens/cost.

Clicking a card opens the existing agent session panel via useOpenAgentActivity — the same universal ingress the profile panel uses — no duplicated transcript UI.

The roster is "all of the owner's agents": locally managed agents plus relay agents whose profile declares NIP-OA ownerPubkey == me (the same ownership rule useAgentObserverIngestion already uses), so agents running on another machine appear too.

Data sources — existing paths only

No new HTTP endpoints, no new Rust, no new event kinds:

  • Observer frames: the app-shell useAgentObserverIngestion bridge already ingests kind 24200 app-wide; the fleet view only reads observerRelayStore / activeAgentTurnsStore / agentWorkingSignal.
  • Turn metrics: archived kind 44200 events from the local SQLite archive (owner_p save subscription — the one useAgentMetricArchiveSeed / the Local Archive settings card manage) plus the relay's persisted, owner-addressed copies via the existing relayClient fetch/subscribe path. Decryption goes through the same decrypt_observer_event Tauri command the observer store uses (44200 shares the NIP-44 agent→owner scheme).
  • The new module-level metrics store is reset in resetCommunityState() per the community-switching singleton rules (AGENTS.md list updated in this PR).

This is complementary to #3068 (per-agent usage table on the Agents page, local log aggregates): that PR is per-agent local resource introspection; this one is the fleet-wide live board fed by relay-carried NIP-AM metrics.

Screenshots

Fleet board — mixed statuses

One working agent (live turn in #agents with elapsed counter), one idle with a transcript headline and cumulative usage, one crashed with friendly error copy, one offline, and a declared-owned agent running on another device.

fleet board

Working card

working card

Needs-attention card

Structured error copy (friendlyAgentLastError), not the raw JSON error string.

error card

Card click → existing session panel

The card deep-links into the agent's working channel with the existing Activity pane open.

open session

Default state (just desktop-screenshot --name fleet-empty --route /fleet)

default state

Screenshots are hosted on a branch in my fork (agent-screenshots/fleet) because scripts/post-screenshots.sh pushes to origin, which fork contributors can't write to. Happy to re-post via the script once the PR exists if a maintainer prefers.

Testing

  • node --test unit coverage for every pure module: NIP-AM aggregation (cumulative vs delta, unreliable deltas, null-vs-zero semantics), fleet status derivation, last-seen/headline selection, formatting, and roster composition (ownership gate, collision handling).
  • New smoke spec fleet-screenshots.spec.ts: seeds a mixed fleet plus decoded 44200 payloads through a new __BUZZ_E2E_SEED_TURN_METRICS__ bridge hook (mirrors the observer-event seed hook), asserts summary counts, per-card states, aggregation, the remote-owned roster path, and the card-click ingress into the existing session panel.
  • just desktop-check, pnpm typecheck, full desktop unit suite (3757 passing), cargo fmt --check + workspace clippy clean (no Rust touched).

Diff is additive and confined to desktop/src + route/sidebar registration, the e2e spec, and the fleet entry in preview-features.json.

Related: #2716 (fleet-view comment), #3068 (complementary per-agent usage dashboard).

🤖 Generated with Claude Code

maxmoneycash and others added 3 commits July 29, 2026 20:59
…etric usage store

New desktop/src/features/fleet/ module for the Agent Fleet board:

- fleetAgents.ts: pure roster composition (managed agents + NIP-OA
  declared-owned relay agents, the same ownership rule the observer
  ingestion uses), with useFleetAgents.ts as the query-composing hook.
- fleetStatus.ts: pure live-status/last-seen/headline derivations over
  the existing observer stores plus compact token/cost formatting.
- turnMetrics.ts + turnMetricsStore.ts: NIP-AM (kind 44200) usage
  aggregation fed exclusively from existing data paths — the local
  SQLite archive (owner_p save subscription) and the relay's persisted,
  owner-addressed metric events, decrypted through the same
  decrypt_observer_event command the observer store uses.
- FleetView/FleetAgentCard/FleetScreen: live status grid reusing
  AgentStatusBadge, the unified agent working signal, and
  useOpenAgentActivity (cards open the existing agent session panel).
- Unit tests for every pure module.

Addresses the fleet-view use case raised in block#2716.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Max Mohammadi <maxmohammadi@gmail.com>
- /fleet route (TanStack virtual file route + regenerated route tree)
  lazy-loading FleetScreen, mirroring the Agents route pattern.
- Sidebar entry behind a new 'fleet' preview feature gate, consistent
  with Pulse/Projects/Workflows; goFleet navigation + selected-view
  derivation in AppShell.helpers.
- resetFleetTurnMetricsStore() wired into resetCommunityState() per the
  community-switching singleton rules; AGENTS.md singleton list updated
  in the same PR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Max Mohammadi <maxmohammadi@gmail.com>
- __BUZZ_E2E_SEED_TURN_METRICS__ bridge hook injecting decoded NIP-AM
  payloads into the fleet usage store, mirroring the observer-event
  seed hook.
- fleet-screenshots.spec.ts (smoke): mixed-status board (working live
  turn, idle with transcript headline, crashed with friendly error
  copy, offline, and a declared-owned remote agent), aggregation
  assertions (cumulative series must not double-count), and the
  card-click ingress into the existing agent session panel.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Max Mohammadi <maxmohammadi@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant