feat(desktop): owner-global observer ingestion at app level#1493
Draft
tellaho wants to merge 1 commit into
Draft
feat(desktop): owner-global observer ingestion at app level#1493tellaho wants to merge 1 commit into
tellaho wants to merge 1 commit into
Conversation
Make agent observer ingestion (kind 24200 frame decryption + derived active-turn liveness) a single app-level concern instead of a per-surface bridge mount, so activity data no longer depends on which screen happens to be open. - Add useAgentObserverIngestion (features/agents/useAgentObserverIngestion.ts): mounted once in AppShell; registers all locally managed agents (real status) plus relay agents the current identity declared-owns via NIP-OA ownerPubkey (treated as deployed, resolved through one users-batch profile query), then mounts the observer + active-turns bridges on that combined list - Extract pure combineObserverIngestionAgents() with unit coverage: managed status passthrough, owned-relay inclusion, foreign/ownerless exclusion, managed/relay dedupe, case-insensitive matching, unresolved identity fallback - Remove now-redundant per-surface bridge mounts and their synthetic agent-list memos: ChannelScreen (observerBridgeAgents), UserProfilePanel (bridgeAgents/observerBridgeAgents), useManagedAgentActions, useActiveWorkingChannelsById (sidebar), usePreventSleep - Consolidating to one useManagedAgentObserverBridge mount also removes the fragile last-mount-wins wiring of setSessionConfigCapturedCallback across co-mounted bridges This closes the failure mode fixed point-wise in a8dada7 (a surface receiving raw observer frames while derived liveness stays stale because its bridge was not mounted) for every current and future surface: sidebar working badges, profile badges, and activity panels all read from stores that are now fed app-wide. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This was referenced Jul 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Category: improvement
User Impact: Agent activity and working indicators (sidebar badges, profile badges, activity panels) now update reliably no matter which screen is open — including for remote agents you own but don't run locally.
Problem: Observer ingestion was mount-timing incidental: frames were only received, decrypted, and folded into derived liveness state while some surface happened to mount its own bridge with the right agent list. That made indicators brittle and inconsistent — a panel could show raw turn events while another surface's liveness stayed stale (the bug class fixed point-wise in #1492), and remote owned agents were only registered when specific panels were open.
Solution: Ingestion becomes a single app-level concern. A new
useAgentObserverIngestionhook mounts once inAppShell, registers all locally managed agents plus relay agents the current identity declared-owns (NIP-OAownerPubkey, resolved via one batched profile query), and feeds both the observer store and the derived active-turns store app-wide. All five per-surface bridge mounts are removed; surfaces now only read from the stores. This establishes the product invariant: if you own an agent, its turn activity is ingested app-wide.File changes
desktop/src/features/agents/useAgentObserverIngestion.ts
New app-level ingestion hook plus pure
combineObserverIngestionAgents(): managed agents keep their real status; declared-owned relay agents not managed locally are added asdeployed. Non-owned agents are excluded — their frames are#p-addressed to their owner and never arrive on our subscription anyway.desktop/src/features/agents/useAgentObserverIngestion.test.mjs
Unit coverage for the combination logic: status passthrough, owned-relay inclusion, foreign/ownerless exclusion, managed/relay dedupe, case-insensitive matching, unresolved-identity fallback.
desktop/src/app/AppShell.tsx
Mounts the ingestion hook alongside the other always-on workspace managers.
desktop/src/features/channels/ui/ChannelScreen.tsx
Removes the per-screen observer/turns bridge mounts and the synthetic
observerBridgeAgentsmemo (profile-panel agents are covered by app-level owned-relay registration).desktop/src/features/profile/ui/UserProfilePanel.tsx
Removes the panel-local bridge mounts and both synthetic agent-list memos — the owned-relay "deployed" seeding it did is now the app-level rule.
desktop/src/features/sidebar/lib/useActiveWorkingChannelsById.ts
Sidebar working-badge hook becomes read-only over the derived store.
desktop/src/features/agents/ui/useManagedAgentActions.ts
Agents-page hook becomes read-only over the derived store.
desktop/src/features/agents/usePreventSleep.ts
Drops its own bridge mount; keeps reading observer snapshots for activity tracking.
Reproduction Steps
ownerPubkey, not managed locally): open its Activity from a profile anywhere — frames decrypt without needing the specific panels that used to seed the known-agents set.cd desktop && pnpm test(1558/1558, 7 new).No visual/markup changes — this is ingestion plumbing consolidation.