mirror of
https://github.com/block/buzz.git
synced 2026-11-04 00:12:02 +00:00
Support relay agents in channel activity
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -35,8 +35,8 @@ const overrides = new Map([
|
||||
["src/app/AppShell.tsx", 860], // message edit state + handlers + ChannelPane edit prop threading + scrollback pagination + workflows view + memory-leak safeguards
|
||||
["src/features/channels/hooks.ts", 550], // canvas query + mutation hooks + DM hide mutation
|
||||
["src/features/channels/ui/ChannelManagementSheet.tsx", 800],
|
||||
["src/features/channels/ui/ChannelPane.tsx", 520], // composer/timeline/sidebar orchestration + anchored agent activity footers
|
||||
["src/features/channels/ui/ChannelScreen.tsx", 550], // profile panel state + mutual exclusion wiring + ProfilePanelProvider context + agent typing classification
|
||||
["src/features/channels/ui/ChannelPane.tsx", 525], // composer/timeline/sidebar orchestration + anchored agent activity footers
|
||||
["src/features/channels/ui/ChannelScreen.tsx", 610], // profile panel state + mutual exclusion wiring + ProfilePanelProvider context + relay-aware agent typing classification
|
||||
["src/features/messages/hooks.ts", 500], // message query/mutation hooks + optimistic updates
|
||||
["src/features/messages/ui/MessageComposer.tsx", 700], // media upload handlers (paste, drop, dialog) + channelId reset effect + edit mode (pre-fill, save, cancel, escape)
|
||||
["src/features/settings/ui/SettingsView.tsx", 600],
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use tauri::AppHandle;
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
use crate::{
|
||||
app_state::AppState,
|
||||
managed_agents::{
|
||||
append_log_marker, login_shell_path, managed_agent_log_path, missing_command_message,
|
||||
normalize_agent_args, open_log_file, resolve_command, ManagedAgentProcess,
|
||||
@@ -561,6 +562,11 @@ pub fn spawn_agent_child(
|
||||
} else {
|
||||
command.env_remove("SPROUT_AUTH_TAG");
|
||||
}
|
||||
if let Some(state) = app.try_state::<AppState>() {
|
||||
if let Ok(keys) = state.keys.lock() {
|
||||
command.env("SPROUT_ACP_AGENT_OWNER", keys.public_key().to_hex());
|
||||
}
|
||||
}
|
||||
|
||||
command.env("SPROUT_ACP_RELAY_OBSERVER", "true");
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import { MembersSidebar } from "@/features/channels/ui/MembersSidebar";
|
||||
import {
|
||||
useManagedAgentsQuery,
|
||||
usePersonasQuery,
|
||||
useRelayAgentsQuery,
|
||||
} from "@/features/agents/hooks";
|
||||
import { useManagedAgentObserverBridge } from "@/features/agents/observerRelayStore";
|
||||
import {
|
||||
@@ -52,6 +53,7 @@ import { AgentSessionProvider } from "@/shared/context/AgentSessionContext";
|
||||
import { ProfilePanelProvider } from "@/shared/context/ProfilePanelContext";
|
||||
import { useChannelAgentSessions } from "./useChannelAgentSessions";
|
||||
import { useChannelProfilePanel } from "./useChannelProfilePanel";
|
||||
import type { BotActivityAgent } from "./BotActivityBar";
|
||||
type ChannelScreenProps = {
|
||||
activeChannel: Channel | null;
|
||||
currentIdentity?: Identity;
|
||||
@@ -165,46 +167,66 @@ export function ChannelScreen({
|
||||
enabled: messageProfilePubkeys.length > 0,
|
||||
});
|
||||
const managedAgentsQuery = useManagedAgentsQuery();
|
||||
const relayAgentsQuery = useRelayAgentsQuery();
|
||||
useManagedAgentObserverBridge(managedAgentsQuery.data ?? []);
|
||||
const { botTypingEntries, humanTypingPubkeys } = React.useMemo<{
|
||||
botTypingEntries: TypingIndicatorEntry[];
|
||||
humanTypingPubkeys: string[];
|
||||
}>(() => {
|
||||
const localAgentSet = new Set(
|
||||
(managedAgentsQuery.data ?? [])
|
||||
.filter((agent) => agent.backend.type === "local")
|
||||
.map((agent) => agent.pubkey.toLowerCase()),
|
||||
const managedAgentSet = new Set(
|
||||
(managedAgentsQuery.data ?? []).map((agent) =>
|
||||
agent.pubkey.toLowerCase(),
|
||||
),
|
||||
);
|
||||
const relayAgentSet = new Set(
|
||||
(relayAgentsQuery.data ?? []).map((agent) => agent.pubkey.toLowerCase()),
|
||||
);
|
||||
const channelTypingEntries = typingEntries.filter(
|
||||
(entry) => entry.threadHeadId === null,
|
||||
);
|
||||
const agentTypingEntries = typingEntries.filter((entry) =>
|
||||
localAgentSet.has(entry.pubkey.toLowerCase()),
|
||||
const agentTypingEntries = typingEntries.filter(
|
||||
(entry) =>
|
||||
managedAgentSet.has(entry.pubkey.toLowerCase()) ||
|
||||
relayAgentSet.has(entry.pubkey.toLowerCase()),
|
||||
);
|
||||
return {
|
||||
botTypingEntries: agentTypingEntries,
|
||||
humanTypingPubkeys: channelTypingEntries
|
||||
.filter((entry) => !localAgentSet.has(entry.pubkey.toLowerCase()))
|
||||
.filter(
|
||||
(entry) =>
|
||||
!managedAgentSet.has(entry.pubkey.toLowerCase()) &&
|
||||
!relayAgentSet.has(entry.pubkey.toLowerCase()),
|
||||
)
|
||||
.map((entry) => entry.pubkey),
|
||||
};
|
||||
}, [managedAgentsQuery.data, typingEntries]);
|
||||
}, [managedAgentsQuery.data, relayAgentsQuery.data, typingEntries]);
|
||||
const messageProfiles = React.useMemo(() => {
|
||||
const base =
|
||||
mergeCurrentProfileIntoLookup(
|
||||
messageProfilesQuery.data?.profiles,
|
||||
currentProfile,
|
||||
) ?? {};
|
||||
// Merge managed agent names so system messages resolve instantly
|
||||
// Merge agent names so system messages and activity pills resolve instantly
|
||||
// (without waiting for the relay profile batch query).
|
||||
const agents = managedAgentsQuery.data ?? [];
|
||||
const merged = { ...base };
|
||||
for (const agent of agents) {
|
||||
for (const agent of managedAgentsQuery.data ?? []) {
|
||||
const key = agent.pubkey.toLowerCase();
|
||||
if (!merged[key]?.displayName) {
|
||||
merged[key] = {
|
||||
...merged[key],
|
||||
displayName: agent.name,
|
||||
avatarUrl: null,
|
||||
avatarUrl: merged[key]?.avatarUrl ?? null,
|
||||
nip05Handle: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
for (const agent of relayAgentsQuery.data ?? []) {
|
||||
const key = agent.pubkey.toLowerCase();
|
||||
if (!merged[key]?.displayName) {
|
||||
merged[key] = {
|
||||
...merged[key],
|
||||
displayName: agent.name,
|
||||
avatarUrl: merged[key]?.avatarUrl ?? null,
|
||||
nip05Handle: null,
|
||||
};
|
||||
}
|
||||
@@ -214,9 +236,43 @@ export function ChannelScreen({
|
||||
currentProfile,
|
||||
managedAgentsQuery.data,
|
||||
messageProfilesQuery.data?.profiles,
|
||||
relayAgentsQuery.data,
|
||||
]);
|
||||
const channelMembersQuery = useChannelMembersQuery(activeChannel?.id ?? null);
|
||||
const channelMembers = channelMembersQuery.data;
|
||||
const activityAgents = React.useMemo<BotActivityAgent[]>(() => {
|
||||
const byPubkey = new Map<string, BotActivityAgent>();
|
||||
for (const agent of managedAgentsQuery.data ?? []) {
|
||||
byPubkey.set(agent.pubkey.toLowerCase(), {
|
||||
name: agent.name,
|
||||
pubkey: agent.pubkey,
|
||||
});
|
||||
}
|
||||
for (const agent of relayAgentsQuery.data ?? []) {
|
||||
const key = agent.pubkey.toLowerCase();
|
||||
if (!byPubkey.has(key)) {
|
||||
byPubkey.set(key, { name: agent.name, pubkey: agent.pubkey });
|
||||
}
|
||||
}
|
||||
for (const member of channelMembers ?? []) {
|
||||
const key = member.pubkey.toLowerCase();
|
||||
if (member.role === "bot" && !byPubkey.has(key)) {
|
||||
byPubkey.set(key, {
|
||||
name:
|
||||
member.displayName ??
|
||||
messageProfiles[key]?.displayName ??
|
||||
`Agent ${member.pubkey.slice(0, 8)}`,
|
||||
pubkey: member.pubkey,
|
||||
});
|
||||
}
|
||||
}
|
||||
return [...byPubkey.values()];
|
||||
}, [
|
||||
channelMembers,
|
||||
managedAgentsQuery.data,
|
||||
messageProfiles,
|
||||
relayAgentsQuery.data,
|
||||
]);
|
||||
const personasQuery = usePersonasQuery();
|
||||
const personaLookup = React.useMemo(() => {
|
||||
const agents = managedAgentsQuery.data ?? [];
|
||||
@@ -470,6 +526,7 @@ export function ChannelScreen({
|
||||
<React.Suspense fallback={<ViewLoadingFallback kind="channel" />}>
|
||||
<ChannelPane
|
||||
activeChannel={activeChannel}
|
||||
activityAgents={activityAgents}
|
||||
agentSessionAgents={channelAgentSessionAgents}
|
||||
botTypingEntries={botTypingEntries}
|
||||
channelFind={channelFind}
|
||||
|
||||
@@ -47,10 +47,8 @@ export function useChannelAgentSessions({
|
||||
channelMembers.map((member) => normalizePubkey(member.pubkey)),
|
||||
);
|
||||
|
||||
return managedAgents.filter(
|
||||
(agent) =>
|
||||
agent.backend.type === "local" &&
|
||||
memberPubkeys.has(normalizePubkey(agent.pubkey)),
|
||||
return managedAgents.filter((agent) =>
|
||||
memberPubkeys.has(normalizePubkey(agent.pubkey)),
|
||||
);
|
||||
}, [channelMembers, managedAgents]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user