mirror of
https://github.com/block/buzz.git
synced 2026-11-15 13:51:12 +00:00
## Summary - add a persistent, community-scoped Bestie designation for local managed agents - surface the designated agent in the sidebar, agent library, profile actions, message toolbar, and draggable floating shortcut - bloom the floating shortcut into a lightweight compact composer that reuses the normal DM timeline, reactions, presence, and send behavior - support message handoff with a bounded snapshot and a full Buzz thread link so the agent can retrieve the complete conversation ## UX details - the floating avatar and expanded panel stay above app chrome and drag as one aligned surface - closing the expanded panel returns it to its top-right anchor - each mini-composer opening starts visually fresh while messages sent during that opening remain conversational - the designated Bestie's duplicate DM entry is hidden from the regular DM list - Bestie actions are suppressed inside the mini timeline to avoid recursive handoff ## Reliability and maintainability - preserve existing retention database paths across upgrades - serialize assignment and deletion, clearing matching assignments across community scopes before an agent is removed - fence async conversation resolution against workspace and assignment changes - validate stale assignments against existing local agents before hiding DMs - share lightweight assignment state across agent cards and keep protected-feature behavior out of the shared timeline API ## Testing - `pnpm --dir desktop test` — 5,886 tests passed - `pnpm --dir desktop exec tsc --noEmit` - `pnpm --dir desktop exec biome check ...` - `cargo clippy --manifest-path desktop/src-tauri/Cargo.toml --all-targets -- -D warnings` - focused native retention and Bestie assignment/command tests — 34 passed - `VITE_BUZZ_BESTIE=1 pnpm --dir desktop build:e2e` - `VITE_BUZZ_BESTIE=1 pnpm --dir desktop exec playwright test --project=smoke tests/e2e/bestie.spec.ts` - pre-commit and differential pre-push hooks ## Rollout The UI remains gated by the `bestie` build feature. Screenshots covering setup, empty, assigned, floating, and message-handoff states are included in the PR discussion. --------- Signed-off-by: Arjun Mahanti <arjun@squareup.com> Co-authored-by: Codex <noreply@openai.com>
90 lines
2.9 KiB
TypeScript
90 lines
2.9 KiB
TypeScript
import {
|
|
createContext,
|
|
createElement,
|
|
type ReactNode,
|
|
useContext,
|
|
useMemo,
|
|
} from "react";
|
|
|
|
import { useManagedAgentsQuery } from "@/features/agents/hooks";
|
|
import type { TimelineMessage } from "@/features/messages/types";
|
|
import type { Channel, ManagedAgent } from "@/shared/api/types";
|
|
import { useFeatureEnabled } from "@/shared/features";
|
|
import { BestieGlobalOverlay } from "./bestie/BestieGlobalOverlay";
|
|
import { BestieCardBadge } from "./bestie/BestieCardBadge";
|
|
import { BestieMessageAction } from "./bestie/BestieMessageAction";
|
|
import { BestieProfileAction } from "./bestie/BestieProfileSection";
|
|
import { BestieSidebarEntry } from "./bestie/BestieSidebarEntry";
|
|
import { filterBestieDmChannels } from "./bestie/filterBestieDmChannels";
|
|
import { findAssignedLocalAgent } from "./bestie/findAssignedLocalAgent";
|
|
import { useBestieAssignmentQuery } from "./bestie/useBestie";
|
|
|
|
const ProtectedMessageActionsContext = createContext(true);
|
|
|
|
export function ProtectedGlobalOverlay() {
|
|
const enabled = useFeatureEnabled("bestie");
|
|
return enabled ? createElement(BestieGlobalOverlay) : null;
|
|
}
|
|
|
|
export function ProtectedMessageAction(props: {
|
|
channelId?: string | null;
|
|
message: TimelineMessage;
|
|
}) {
|
|
const enabled = useFeatureEnabled("bestie");
|
|
const actionsAllowed = useContext(ProtectedMessageActionsContext);
|
|
return enabled && actionsAllowed
|
|
? createElement(BestieMessageAction, props)
|
|
: null;
|
|
}
|
|
|
|
export function ProtectedMessageActionsBoundary({
|
|
children,
|
|
}: {
|
|
children: ReactNode;
|
|
}) {
|
|
return createElement(
|
|
ProtectedMessageActionsContext.Provider,
|
|
{ value: false },
|
|
children,
|
|
);
|
|
}
|
|
|
|
export function ProtectedAgentBestieAction(props: { agent: ManagedAgent }) {
|
|
const enabled = useFeatureEnabled("bestie");
|
|
return enabled ? createElement(BestieProfileAction, props) : null;
|
|
}
|
|
|
|
export function ProtectedBestieCardBadge(props: {
|
|
agent: ManagedAgent;
|
|
isBestie: boolean;
|
|
}) {
|
|
const enabled = useFeatureEnabled("bestie");
|
|
return enabled ? createElement(BestieCardBadge, props) : null;
|
|
}
|
|
|
|
export function ProtectedBestieSidebarEntry() {
|
|
const enabled = useFeatureEnabled("bestie");
|
|
return enabled ? createElement(BestieSidebarEntry) : null;
|
|
}
|
|
|
|
export function useProtectedBestiePubkey(agents: ManagedAgent[]) {
|
|
const enabled = useFeatureEnabled("bestie");
|
|
const { assignmentQuery } = useBestieAssignmentQuery(enabled);
|
|
if (!enabled) return null;
|
|
return findAssignedLocalAgent(agents, assignmentQuery.data)?.pubkey ?? null;
|
|
}
|
|
|
|
export function useProtectedVisibleDirectMessages(
|
|
channels: Channel[],
|
|
currentPubkey: string | undefined,
|
|
) {
|
|
const enabled = useFeatureEnabled("bestie");
|
|
const managedAgentsQuery = useManagedAgentsQuery({ enabled });
|
|
const bestiePubkey = useProtectedBestiePubkey(managedAgentsQuery.data ?? []);
|
|
|
|
return useMemo(
|
|
() => filterBestieDmChannels(channels, currentPubkey, bestiePubkey),
|
|
[bestiePubkey, channels, currentPubkey],
|
|
);
|
|
}
|