Skip to content

feat(desktop): add Agent Usage UI backed by the NIP-AM local archive#2035

Draft
wpfleger96 wants to merge 3 commits into
mainfrom
duncan/agent-usage-archive
Draft

feat(desktop): add Agent Usage UI backed by the NIP-AM local archive#2035
wpfleger96 wants to merge 3 commits into
mainfrom
duncan/agent-usage-archive

Conversation

@wpfleger96

Copy link
Copy Markdown
Member

Adds a rolling token/cost usage view for agents, sourced from the local NIP-AM metrics archive. Covers both the Agents overview and a focused per-agent subview in the profile panel.

Backend

  • get_agent_usage_series Tauri command (archive/agent_usage.rs): reads the local SQLite archive for a rolling 7d/30d window, returns per-agent/per-model token and cost totals with partial/unknown-field flags when evidence is incomplete.
  • Wired persistedAgentMetrics notifier through the archive sync path so new metric events are picked up without a full resync.

Frontend

  • AgentUsageSection (features/agent-usage/ui/): ranked agent list in AgentsView, between the unified agents list and teams section. Row click-through opens the profile panel focused on usage.
  • AgentUsageFocusedView: per-agent 7d/30d totals with a by-model breakdown, rendered as a focused profile-panel subview (same pattern as Memories/Diagnostics) rather than a new tab.
  • UserProfilePanel/UserProfilePanelSections/UserProfilePanelTabs: threaded canViewUsage/onOpenUsage (owner-only, bot profiles only) and added a BarChart3 ingress row in the Info tab.
  • Loading skeleton, query-error retry, empty state, and a collection-off banner (with retained-data coverage copy when historical data still exists) are all covered.

Tests

  • Rust: unit tests for bucket-boundary math, cost-ladder aggregation (direct/cumulative/decrease-taint), ranking tie-breaks, and out-of-range request validation, plus integration tests exercising the command against a real SQLite archive (fresh ingest, pubkey filtering, pre-existing-row backfill).
  • agentUsage.test.mjs: pure-helper unit tests (formatting, partial/unknown-field detection, sorting).
  • tests/e2e/agent-usage.spec.ts: loading → resolved, window switch, both click-through paths into the focused view, error/retry, empty state, collection-off banner (with and without retained-data copy), and the partial-total badge.

npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 and others added 3 commits July 17, 2026 11:37
Adds the Rust backend half of the NIP-AM local agent usage feature
(Rev 3 frozen plan): a rebuildable agent_metric_index parsed from
archived kind-44200 rows, a pure per-field accounting ladder
(agent_usage.rs) computing token/cost deltas with adjacent-cumulative
preference and direct-value fallback, and the get_agent_usage_series
Tauri command wiring backfill, orphan repair, collection-enabled
detection, and A13's hasArchivedEvidence into one series response.

commit_archive now indexes kind-44200 rows in the same transaction as
the canonical event insert, and upsert_archived_event/gc_orphaned_events
return/enforce the new-row and cascade-delete invariants (A5/A6) that
persistedAgentMetrics and the index's rebuildability depend on.

get_agent_usage_series' SQLite core is split into a plain sync fn so
it can be driven directly against an in-memory Connection in tests
without a Tauri AppState.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Completes Phase 2 of the NIP-AM local agent usage feature. tauriArchive
decodes the backend's persistedAgentMetrics count defensively (missing
field -> 0, per A5/M2's backward-compatible wire contract) and exposes
an onAgentMetricsChanged notifier, fired only when a subscription
mutation touches kind 44200 specifically.

ArchiveSyncManager routes both its idle/size-triggered and destroy-time
flushes through one sendBatch helper that notifies usage listeners
only when the backend reports persistedAgentMetrics > 0 — rejections,
duplicate-only batches, and non-metric successes never notify, keeping
the refresh signal backend-authoritative.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Surfaces per-agent NIP-AM turn-metric usage (tokens, cost, model
breakdown, 7d/30d bucket series) in the desktop app, reading from the
Phase 2 get_agent_usage_series archive command:

- agent-usage/lib/agentUsage.ts: pure client-side helpers -- bucket
  boundary construction, bigint-safe token formatting, and the ranked
  overview-row projection shared by both UI surfaces.
- agent-usage/hooks.ts: react-query wrapper around
  get_agent_usage_series with the app's standard retry:1 default.
- agent-usage/ui/AgentUsageSection.tsx: ranked per-agent usage rows on
  the Agents overview, with a 7d/30d window switch, retry-on-error,
  and a collection-off banner (with or without retained-data coverage
  copy) linking to Local Archive settings.
- agent-usage/ui/AgentUsageFocusedView.tsx: per-agent drill-down
  (bucketed series + model breakdown) reached via an overview row or
  the profile panel's Info-tab usage ingress row; wired into
  AgentsView, UserProfilePanel, and ProfilePanelContext.
- Partial badge on rows whose total is a known lower bound
  (has_unknown_usage), surfaced via the accounting engine's per-field
  completeness contract.

Extends the Rust archive test suite (agent_usage_tests.rs) to close
gaps in the pure accounting engine: the f64 cost ladder had no direct
outcome.cost assertions, assign_bucket_index's start-inclusive/
end-exclusive edges were only exercised indirectly, validate_request's
chrono finite-range rejection was unexercised, and the A2 ranking
tiebreak (pubkey ascending on an exact totalTokens tie) had no test
distinguishing it from insertion order.

tests/e2e/agent-usage.spec.ts (wired into playwright.config.ts's smoke
project) exercises both UI surfaces against a mocked
get_agent_usage_series: ranked rows, window switching, click-through
to the focused view from both the overview row and the profile
panel's ingress row, retry recovery from a query error, the
collection-off banner and its settings deep link, and the Partial
badge.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 requested a review from a team as a code owner July 17, 2026 17:08
@wpfleger96
wpfleger96 marked this pull request as draft July 17, 2026 17:10
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