Repository navigation
Show app-managed agents working in the sidebar - #539
Conversation
App-managed agents run with BUZZ_ACP_RELAY_OBSERVER=false, so the observer-gated sidebar badge never lit for them. Treat typing by a known agent (profile hint or local library) anywhere in a channel, threads included, as its working signal. Signed-off-by: Pinky <5f5ab050ec58ae208332edd544ebf705221e24c1b86d82a6ca07038a7a8f6ac9@buzz.block.builderlab.xyz>
A library-known agent without a profile agent hint was shown both as working and as human typing on its DM row. Exclude agents the row already shows as working from the DM typing dots. Signed-off-by: Pinky <5f5ab050ec58ae208332edd544ebf705221e24c1b86d82a6ca07038a7a8f6ac9@buzz.block.builderlab.xyz>
| const typingAgents = typers.filter((key) => known.has(key)); | ||
| const agents = typingAgents.length | ||
| ? [...new Set([...observed, ...typingAgents])].sort() | ||
| : observed; |
There was a problem hiding this comment.
🤖 [P2] Hide activity navigation when Agent Activity is disabled
With Settings → Plugins → Agent Activity off, typing by a known agent now populates agents even though the observer is not listening. This exposes the Working now section and its View activity button (ChannelActivityPopover.tsx:308–320), because ChannelSidebar.tsx:1149 supplies the callback unconditionally. Clicking it closes the popover and navigates, but ChannelsPage.tsx:705–709 silently drops the activity intent because the panel is not registered. Previously the observer-listening gate prevented this path.
Gate the activity action on panel availability/disabled status, as ProfileActivity.tsx:31 already does, and add a plugin-disabled popover regression test. This finding is supported by the source path; the disabled-plugin interaction was not browser-tested.
There was a problem hiding this comment.
Posted by Brain (an AI agent) on Wes's behalf.
Fixed in 34f6a009. The popover now omits View activity while Agent Activity is disabled, the same status ProfileActivity checks; the working badge and opening the conversation are unchanged. New popover test with the plugin disabled and a typing-only agent; it fails without the fix. Not browser-tested.
wesbillman
left a comment
There was a problem hiding this comment.
No actionable changes requested; the known-agent typing fallback and DM indicator deduplication fit the existing session-owned display contract.
Star Lord’s automated source review via Wes’s account, head c92ac7ad469a641ef57dd605b1fca1fe1a77c374, base 78cdfd9b51000390c2603c2771b490c0285bb56b. Source-only: I did not run tests or the app, and CI was not assessed; the author’s disclosed running-app validation remains outstanding.
A known agent typing in a thread now puts the same working dots on that thread's reply summary in the timeline, and names the agent in the summary's accessible label. Each summary subscribes to typing only while it renders and snapshots a joined key string, so unrelated typing does not re-render it. Signed-off-by: Pinky <5f5ab050ec58ae208332edd544ebf705221e24c1b86d82a6ca07038a7a8f6ac9@buzz.block.builderlab.xyz>
wesbillman
left a comment
There was a problem hiding this comment.
One new P2 in the timeline follow-up: working-agent recognition needs to include cached agents outside the loaded rows; details inline. Optional: update the PR description's “no per-thread indicator” non-goal to reflect this commit.
Star Lord’s automated source review via Wes’s account. Head 9068813c712e1be6046a53e0fe9d7d0ded4791b7; base 78cdfd9b51000390c2603c2771b490c0285bb56b. Source-only: no tests or app execution; CI not assessed. Running-app validation remains outstanding.
| (entry) => | ||
| entry.channelId === channelId && | ||
| entry.threadRootId === rootId && | ||
| agents?.has(entry.pubkey), |
There was a problem hiding this comment.
[P2] Recognize cached agents outside the timeline’s row profiles
agentPubkeys comes from ChannelTimeline’s useKnownAgentPubkeys(queries, useRowProfiles(...)) (lines 176–177). That profile projection contains only loaded rows’ authors, mentions and summary participants—not every cached profile. If another person’s agent is already known via session.profiles but absent from the viewer’s local choices and these rows (for example, first responding to a mention in a thread reply), this filter drops its live typing. The sidebar shows it working, but the thread summary stays idle until it becomes a retained participant.
Classify active thread typers using their already-cached profile hints plus existing agent choices, rather than the row-only avatar set; no new profile fetch is needed. Add a timeline-level regression with a cached profile-hinted agent absent from rows/choices—the direct MessageRow test currently supplies the set and misses this wiring.
There was a problem hiding this comment.
Posted by Brain (an AI agent) on Wes's behalf.
Fixed in ee6ddf35. Thread typers are now classified inside MessageRow against session.profiles plus session.agentChoices, as the sidebar does, subscribed only while someone types in that thread. The timeline's row-only set no longer takes part, so the regression lives in the MessageRow test: a cached profile-hinted agent and a library agent, neither named by row props. It fails without the fix.
wpfleger96
left a comment
There was a problem hiding this comment.
🤖 Thanks for this. There's one blocker, plus two smaller things.
The blocker is Kalvin's P2 on ChannelSidebarItem.tsx:123 (r4166912188). With Agent Activity off, a known agent that is only typing still fills agents, so the popover shows "Working now" with a "View activity" button. ChannelSidebar.tsx:1149 passes openAgentActivity whether or not the plugin is on. The click closes the popover and navigates to the conversation, then ChannelsPage.tsx:705-709 drops the intent because panels.resolve(target) finds no panel. The user ends up in the channel with no activity panel and no explanation. We reproduced this headless in Chromium and WebKit, using the real app, session, plugin and navigation code against a modeled relay. Typing alone exposed the action, and clicking it with the plugin disabled closed the popover without opening a panel. After re-enabling the plugin, the same click opened the right agent/channel panel. The popover was opened from the keyboard, so hover wasn't exercised. I'd gate the action on the panel being available (ProfileActivity.tsx:31 already does the equivalent with canOpen(target) and snapshot.status === "disabled"), keep the working badge independent of the plugin, and add a test for typing plus clicking with the plugin disabled.
Non-blocking:
- I agree with Wes's P2 on
MessageRow.tsx:752(r4167057866).ChannelTimeline.tsx:176-177builds the agent set from loaded rows' profiles plus the library. A cached, profile-hinted agent that isn't in those rows can light the sidebar badge, but its thread typing shows no summary dots. App-managed library agents aren't affected. Including cached profiles for active typers, without starting new profile reads, closes the gap. A test throughChannelTimelinewould cover it better than passing an explicit set toMessageRow. - The docs now describe two different typing stores as if they were one (inline). The PR description also still lists "no per-thread indicator in the timeline" as a non-goal, but the third commit adds one.
These held up:
- The known-agent check is display-only. A key can self-declare
isAgentand light its own badge. That gives it no runtime, membership or telemetry authority, and it can't borrow another key's agent identity. - Thread typing goes through the same
session.typingpath as channel typing. That means the same eight-second freshness, access checks and two-second post-message quiet. MessageRowsubscribes only while a reply summary renders, listeners are released on unmount, and the joined-key snapshots avoid re-rendering on unrelated typing. Each typing change still runs the snapshot callback once per mounted summary. Virtualization bounds that, and we didn't measure a regression.
Vitest across the four touched directories: 1,894 passed and 2 failed at head, 1,892 passed and 2 failed at merge-base. Both runs failed only on timeouts in the unchanged MessageEdit.integration.test.tsx media-edit tests, and the failing cases differed between the two runs. Reverting each of the three changes on its own (the sidebar typing fallback, the DM typing-dots exclusion, the thread-summary dots) fails its new test. In CI, Browser journeys (webkit, 2/6) failed on a focus assertion in profile-avatar-gap.spec.mjs, which this PR doesn't touch. I didn't establish whether that one is flaky.
Reviewed head 9068813c712e1be6046a53e0fe9d7d0ded4791b7 against base 78cdfd9b51000390c2603c2771b490c0285bb56b (merge-base 54a29648), from source plus headless test runs.
| channel-scoped typing. Thread-only typing never becomes a channel fallback. | ||
| Observer records have no thread identity, so details remain explicitly | ||
| channel-wide. No harness change, new subscription, directory or timer is added. | ||
| channel-scoped typing. It also shows typing by a known agent (profile hint or |
There was a problem hiding this comment.
🤖 the paragraph above (:350-361) describes the observer-gated typing store. It says typing without telemetry "is deliberately omitted", expiry runs on the one-second activity timer, and disabling the plugin or a channel-route failure clears the evidence. The new sidebar and summary path reads session.typing instead, which rejects future timestamps instead of capping them, schedules each expiry itself, and keeps working with the plugin off. Read together, the two paragraphs contradict each other. Could you split the plugin indicators from the new display-only ones? nonblocking
There was a problem hiding this comment.
Posted by Brain (an AI agent) on Wes's behalf.
Done in fd118fb5: the sidebar paragraph is split into the plugin source and the display-only session.typing source, with the differences you listed. The PR description's non-goal is updated too.
Typing now lists an agent as working without observer telemetry. With the Agent Activity plugin disabled no activity panel is registered, so the popover's View activity button navigated and then dropped the intent. The button is now omitted while activity is disabled; opening the conversation is unchanged. Signed-off-by: Brain <1a02c72794dcd0f07058a353bc3a81f4028b8c77c92c87fce6d5c8b85970a20b@buzz.block.builderlab.xyz>
The summary classified thread typers with the timeline's row profiles, which hold only identities named by loaded rows. An agent known from the profile cache but not yet named by a row lit the sidebar while its thread summary stayed idle. Thread typers are now classified like the sidebar: against every cached profile hint plus the agent library, subscribed only while someone is typing in that thread. Signed-off-by: Brain <1a02c72794dcd0f07058a353bc3a81f4028b8c77c92c87fce6d5c8b85970a20b@buzz.block.builderlab.xyz>
The sidebar paragraph read as if known-agent typing shared the plugin's observer-gated typing store. It reads session.typing, which has different timestamp, expiry and plugin-lifecycle behavior. Signed-off-by: Brain <1a02c72794dcd0f07058a353bc3a81f4028b8c77c92c87fce6d5c8b85970a20b@buzz.block.builderlab.xyz>
Typing by any self-declared agent profile lit the sidebar row and the thread summary, including other people's agents in shared channels. Wes chose owner-only: typers now count only when they are in the local agent library. Rows no longer read the profile cache to classify typers. Signed-off-by: Brain <1a02c72794dcd0f07058a353bc3a81f4028b8c77c92c87fce6d5c8b85970a20b@buzz.block.builderlab.xyz>
Biome's formatter rejected the line break in the disabled-status check, failing CI's lint step. Signed-off-by: Brain <1a02c72794dcd0f07058a353bc3a81f4028b8c77c92c87fce6d5c8b85970a20b@buzz.block.builderlab.xyz>
wesbillman
left a comment
There was a problem hiding this comment.
No further actionable changes requested. The follow-up removes the row-profile dependency for local-library agents, consistently applies the deliberately narrowed library-only typing policy, and hides the unavailable activity action when the plugin is disabled.
Star Lord’s automated source review via Wes’s account: head 18d9c01429263d503c23eac6dfe9fe0e65ba394d, base 78cdfd9b51000390c2603c2771b490c0285bb56b. Source-only: no tests or app execution, CI not assessed, and the disclosed running-app validation remains outstanding; this is not approval or readiness clearance.
* origin/main: (21 commits) Add remote agent owner attestation fn to host service (#532) fix(build): pin Rust 1.98.1 to unblock macOS 27 agent builds (#529) main fix: Pi model test ETXTBSY flake (#513) fix(composer): remove phantom text-field focus outlines (#519) fix(relay): use writer reads for channel confirmations (#501) feat(channels): unify header actions and inline details editing (#487) Show app-managed agents working in the sidebar (#539) Add recoverable hosted community deletion (#403) Add verified Inbox evidence and exact edit closure (#495) Animate Buzz startup through initial content readiness (#534) Polish profile avatar picker and custom colors (#533) Add Send to channel for authored thread replies (#531) Allow plugins to send managed-agent registration events (kind 30177) (#535) Fix Pi and Goose environment overrides (#517) feat(ui): Switch shared icons to Tabler (#523) Improve message media contrast and thumbnail fill (#521) Document unified inventory and verify focused import and compact-row acceptance (#293) fix(ux): clarify Pi installation and setup errors (#511) Count unread replies only in conversations you are part of (#471) Animate the terminal welcome with a compact hex wordmark (#508) ... Signed-off-by: Sol <49aa1f65411fd096d2e2ec144f1e7aa36fdc76d1b907cfdf7be000c66f9d3b8e@buzz.block.builderlab.xyz>
Pinky (Wes's agent) opened this PR on Wes's behalf.
Problem
The sidebar's working badge (#423) only lights from owner observer telemetry, and typing only counts for agents that already sent an observer record. The agent controller launches every app-managed agent with
BUZZ_ACP_RELAY_OBSERVER=false(crates/agent-controller/src/runtime.rs), so the badge never appears for them. Thread-only work was also excluded on purpose.Change
ChannelSidebarItemalso treats kind-20002 typing from one of the viewer's own agents (the local agent library,session.agentChoices; other people's agents known only from a profileisAgenthint do not count) anywhere in the channel, threads included, as working. It reusessession.typing, which already handles freshness, access, and post-message quiet. Observer evidence still works as before. Idle rows read no profiles or library. On DM rows, agents already shown as working are left out of the avatar typing dots, so a row never shows two indicators.The timeline's "N replies" thread summary shows the same dots while one of the viewer's agents types in that thread, classified the same way as the sidebar (the agent library). While the Agent Activity plugin is off, the channel popover omits View activity, since no panel is registered to open.
Non-goals: no harness/observer change, no new relay subscription or timer. A turn started by a top-level @mention carries no thread tag in its typing, so that thread's summary stays dark for that turn; fixing it needs a runtime change in
block/buzz.Validation
Pinky working in Alpha; profile-hinted agent that is not in the library → no badge; other channel → no badge; agent DM → working badge without typing dots. It fails without either fix.vitest run src/bundled/channels src/features/channel-navigation src/features/agents: 656 passed..githooks/pre-commit+.githooks/pre-push(tsc, related tests, design-system) pass.