fix(desktop): defer agent page secondary requests (#1217)

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
Wes
2026-06-23 12:46:47 -07:00
committed by GitHub
co-authored by Pinky
parent 3ef2a8e5c7
commit bee2d64cf7
7 changed files with 282 additions and 218 deletions
+8 -4
View File
@@ -125,16 +125,17 @@ function invalidateManagedAgentQueriesInBackground(
);
}
export function useAcpRuntimesQuery() {
export function useAcpRuntimesQuery(options?: { enabled?: boolean }) {
return useQuery({
enabled: options?.enabled ?? true,
queryKey: acpRuntimesQueryKey,
queryFn: discoverAcpRuntimes,
staleTime: 60_000,
});
}
export function useAvailableAcpRuntimes() {
const query = useAcpRuntimesQuery();
export function useAvailableAcpRuntimes(options?: { enabled?: boolean }) {
const query = useAcpRuntimesQuery(options);
const available = React.useMemo(
() =>
(query.data ?? []).filter(
@@ -155,8 +156,9 @@ export function useInstallAcpRuntimeMutation() {
});
}
export function useBackendProvidersQuery() {
export function useBackendProvidersQuery(options?: { enabled?: boolean }) {
return useQuery({
enabled: options?.enabled ?? true,
queryKey: backendProvidersQueryKey,
queryFn: discoverBackendProviders,
staleTime: 30_000,
@@ -175,11 +177,13 @@ export function usePersonasQuery() {
export function useManagedAgentPrereqsQuery(
acpCommand: string,
mcpCommand: string,
options?: { enabled?: boolean },
) {
const normalizedAcpCommand = acpCommand.trim();
const normalizedMcpCommand = mcpCommand.trim();
return useQuery({
enabled: options?.enabled ?? true,
queryKey: [
...managedAgentPrereqsQueryKey,
normalizedAcpCommand,
+228 -200
View File
@@ -180,218 +180,246 @@ export function AgentsView() {
</div>
</div>
<CreateAgentDialog
onCreated={(result) => {
agents.setLogAgentPubkey(result.agent.pubkey);
agents.setCreatedAgent(result);
}}
onOpenChange={agents.setIsCreateOpen}
open={agents.isCreateOpen}
/>
<AddAgentToChannelDialog
agent={agents.agentToAddToChannel}
onAdded={agents.handleAddedToChannel}
onOpenChange={(open) => {
if (!open) {
agents.setAgentToAddToChannel(null);
{agents.isCreateOpen ? (
<CreateAgentDialog
onCreated={(result) => {
agents.setLogAgentPubkey(result.agent.pubkey);
agents.setCreatedAgent(result);
}}
onOpenChange={agents.setIsCreateOpen}
open={agents.isCreateOpen}
/>
) : null}
{agents.agentToAddToChannel ? (
<AddAgentToChannelDialog
agent={agents.agentToAddToChannel}
onAdded={agents.handleAddedToChannel}
onOpenChange={(open) => {
if (!open) {
agents.setAgentToAddToChannel(null);
}
}}
open={agents.agentToAddToChannel !== null}
/>
) : null}
{agents.createdAgent ? (
<SecretRevealDialog
created={agents.createdAgent}
onOpenChange={(open) => {
if (!open) {
agents.setCreatedAgent(null);
}
}}
/>
) : null}
{personas.personaDialogState ? (
<PersonaDialog
description={personas.personaDialogState.description}
error={
personas.updatePersonaMutation.error instanceof Error
? personas.updatePersonaMutation.error
: personas.createPersonaMutation.error instanceof Error
? personas.createPersonaMutation.error
: null
}
}}
open={agents.agentToAddToChannel !== null}
/>
<SecretRevealDialog
created={agents.createdAgent}
onOpenChange={(open) => {
if (!open) {
agents.setCreatedAgent(null);
initialValues={personas.personaDialogState.initialValues}
isImportPending={
personas.personaImportActions.isApplyingPersonaImportUpdate
}
}}
/>
<PersonaDialog
description={personas.personaDialogState?.description ?? ""}
error={
personas.updatePersonaMutation.error instanceof Error
? personas.updatePersonaMutation.error
: personas.createPersonaMutation.error instanceof Error
? personas.createPersonaMutation.error
isPending={
personas.createPersonaMutation.isPending ||
personas.updatePersonaMutation.isPending
}
runtimes={personas.acpRuntimesQuery.data ?? []}
runtimesLoading={personas.acpRuntimesQuery.isLoading}
onImportUpdateFile={
personas.personaImportActions.handleEditDialogImportUpdateFile
}
onOpenChange={(open) => {
if (!open) {
personas.setPersonaDialogState(null);
}
}}
onSubmit={personas.handleSubmit}
open={personas.personaDialogState !== null}
submitLabel={personas.personaDialogState.submitLabel}
title={personas.personaDialogState.title}
/>
) : null}
{personas.personaToDelete ? (
<PersonaDeleteDialog
onConfirm={(persona) => {
void personas.handleDelete(persona);
}}
onOpenChange={(open) => {
if (!open) {
personas.setPersonaToDelete(null);
}
}}
open={personas.personaToDelete !== null}
persona={personas.personaToDelete}
/>
) : null}
{personas.isCatalogDialogOpen ? (
<PersonaCatalogDialog
error={
personas.personasQuery.error instanceof Error
? personas.personasQuery.error
: null
}
initialValues={personas.personaDialogState?.initialValues ?? null}
isImportPending={
personas.personaImportActions.isApplyingPersonaImportUpdate
}
isPending={
personas.createPersonaMutation.isPending ||
personas.updatePersonaMutation.isPending
}
runtimes={personas.acpRuntimesQuery.data ?? []}
runtimesLoading={personas.acpRuntimesQuery.isLoading}
onImportUpdateFile={
personas.personaImportActions.handleEditDialogImportUpdateFile
}
onOpenChange={(open) => {
if (!open) {
personas.setPersonaDialogState(null);
}
}}
onSubmit={personas.handleSubmit}
open={personas.personaDialogState !== null}
submitLabel={personas.personaDialogState?.submitLabel ?? "Save"}
title={personas.personaDialogState?.title ?? "Persona"}
/>
<PersonaDeleteDialog
onConfirm={(persona) => {
void personas.handleDelete(persona);
}}
onOpenChange={(open) => {
if (!open) {
personas.setPersonaToDelete(null);
}
}}
open={personas.personaToDelete !== null}
persona={personas.personaToDelete}
/>
<PersonaCatalogDialog
error={
personas.personasQuery.error instanceof Error
? personas.personasQuery.error
: null
}
feedbackErrorMessage={
personas.personaFeedbackSurface === "catalog"
? personas.personaErrorMessage
: null
}
feedbackNoticeMessage={
personas.personaFeedbackSurface === "catalog"
? personas.personaNoticeMessage
: null
}
isLoading={personas.personasQuery.isLoading}
isPending={personas.setPersonaActiveMutation.isPending}
onClearFeedback={() => {
personas.clearFeedback("catalog");
}}
onOpenChange={personas.setIsCatalogDialogOpen}
onSelectPersona={(persona, active) => {
void personas.handleSetActive(persona, active, "catalog");
}}
open={personas.isCatalogDialogOpen}
personas={personas.catalogPersonas}
/>
<TeamDialog
description={teamActions.teamDialogState?.description ?? ""}
error={
teamActions.updateTeamMutation.error instanceof Error
? teamActions.updateTeamMutation.error
: teamActions.createTeamMutation.error instanceof Error
? teamActions.createTeamMutation.error
feedbackErrorMessage={
personas.personaFeedbackSurface === "catalog"
? personas.personaErrorMessage
: null
}
initialValues={teamActions.teamDialogState?.initialValues ?? null}
isImportPending={teamActions.isApplyingTeamImportUpdate}
isPending={
teamActions.createTeamMutation.isPending ||
teamActions.updateTeamMutation.isPending
}
onImportUpdateFile={teamActions.handleEditDialogImportUpdateFile}
onOpenChange={(open) => {
if (!open) {
teamActions.setTeamDialogState(null);
}
}}
onDeleteRemovedPersonas={teamActions.handleDeleteRemovedPersonas}
onSubmit={teamActions.handleTeamSubmit}
open={teamActions.teamDialogState !== null}
personas={personas.libraryPersonas}
submitLabel={teamActions.teamDialogState?.submitLabel ?? "Save"}
title={teamActions.teamDialogState?.title ?? "Team"}
/>
<TeamDeleteDialog
onConfirm={(team) => {
void teamActions.handleDeleteTeam(team);
}}
onOpenChange={(open) => {
if (!open) {
teamActions.setTeamToDelete(null);
feedbackNoticeMessage={
personas.personaFeedbackSurface === "catalog"
? personas.personaNoticeMessage
: null
}
}}
open={teamActions.teamToDelete !== null}
team={teamActions.teamToDelete}
/>
<AddTeamToChannelDialog
onDeployed={teamActions.handleTeamDeployed}
onOpenChange={(open) => {
if (!open) {
teamActions.setTeamToAddToChannel(null);
isLoading={personas.personasQuery.isLoading}
isPending={personas.setPersonaActiveMutation.isPending}
onClearFeedback={() => {
personas.clearFeedback("catalog");
}}
onOpenChange={personas.setIsCatalogDialogOpen}
onSelectPersona={(persona, active) => {
void personas.handleSetActive(persona, active, "catalog");
}}
open={personas.isCatalogDialogOpen}
personas={personas.catalogPersonas}
/>
) : null}
{teamActions.teamDialogState ? (
<TeamDialog
description={teamActions.teamDialogState.description}
error={
teamActions.updateTeamMutation.error instanceof Error
? teamActions.updateTeamMutation.error
: teamActions.createTeamMutation.error instanceof Error
? teamActions.createTeamMutation.error
: null
}
}}
open={teamActions.teamToAddToChannel !== null}
personas={personas.libraryPersonas}
team={teamActions.teamToAddToChannel}
/>
<BatchImportDialog
fileName={personas.batchImportFileName}
onComplete={personas.handleBatchImportComplete}
onOpenChange={(open) => {
if (!open) {
personas.setBatchImportResult(null);
initialValues={teamActions.teamDialogState.initialValues}
isImportPending={teamActions.isApplyingTeamImportUpdate}
isPending={
teamActions.createTeamMutation.isPending ||
teamActions.updateTeamMutation.isPending
}
}}
open={personas.batchImportResult !== null}
result={personas.batchImportResult}
/>
<TeamImportDialog
fileName={teamActions.teamImportPreview?.fileName ?? ""}
onComplete={teamActions.handleTeamImportComplete}
onOpenChange={(open) => {
if (!open) {
teamActions.setTeamImportPreview(null);
onImportUpdateFile={teamActions.handleEditDialogImportUpdateFile}
onOpenChange={(open) => {
if (!open) {
teamActions.setTeamDialogState(null);
}
}}
onDeleteRemovedPersonas={teamActions.handleDeleteRemovedPersonas}
onSubmit={teamActions.handleTeamSubmit}
open={teamActions.teamDialogState !== null}
personas={personas.libraryPersonas}
submitLabel={teamActions.teamDialogState.submitLabel}
title={teamActions.teamDialogState.title}
/>
) : null}
{teamActions.teamToDelete ? (
<TeamDeleteDialog
onConfirm={(team) => {
void teamActions.handleDeleteTeam(team);
}}
onOpenChange={(open) => {
if (!open) {
teamActions.setTeamToDelete(null);
}
}}
open={teamActions.teamToDelete !== null}
team={teamActions.teamToDelete}
/>
) : null}
{teamActions.teamToAddToChannel ? (
<AddTeamToChannelDialog
onDeployed={teamActions.handleTeamDeployed}
onOpenChange={(open) => {
if (!open) {
teamActions.setTeamToAddToChannel(null);
}
}}
open={teamActions.teamToAddToChannel !== null}
personas={personas.libraryPersonas}
team={teamActions.teamToAddToChannel}
/>
) : null}
{personas.batchImportResult ? (
<BatchImportDialog
fileName={personas.batchImportFileName}
onComplete={personas.handleBatchImportComplete}
onOpenChange={(open) => {
if (!open) {
personas.setBatchImportResult(null);
}
}}
open={personas.batchImportResult !== null}
result={personas.batchImportResult}
/>
) : null}
{teamActions.teamImportPreview ? (
<TeamImportDialog
fileName={teamActions.teamImportPreview.fileName}
onComplete={teamActions.handleTeamImportComplete}
onOpenChange={(open) => {
if (!open) {
teamActions.setTeamImportPreview(null);
}
}}
open={teamActions.teamImportPreview !== null}
preview={teamActions.teamImportPreview.preview}
/>
) : null}
{teamActions.teamImportTarget ? (
<TeamImportUpdateDialog
fileName={teamActions.teamImportTargetPreview?.fileName ?? ""}
isPending={
teamActions.isApplyingTeamImportUpdate ||
teamActions.updateTeamMutation.isPending
}
}}
open={teamActions.teamImportPreview !== null}
preview={teamActions.teamImportPreview?.preview ?? null}
/>
<TeamImportUpdateDialog
fileName={teamActions.teamImportTargetPreview?.fileName ?? ""}
isPending={
teamActions.isApplyingTeamImportUpdate ||
teamActions.updateTeamMutation.isPending
}
onApply={teamActions.handleTeamImportUpdateApply}
onClear={teamActions.clearImportUpdateAndReturnToEdit}
onOpenChange={(open) => {
if (!open) {
teamActions.closeImportUpdateDialog();
onApply={teamActions.handleTeamImportUpdateApply}
onClear={teamActions.clearImportUpdateAndReturnToEdit}
onOpenChange={(open) => {
if (!open) {
teamActions.closeImportUpdateDialog();
}
}}
open={teamActions.teamImportTarget !== null}
personas={personas.libraryPersonas}
preview={teamActions.teamImportTargetPreview?.preview ?? null}
team={teamActions.teamImportTarget}
/>
) : null}
{personas.personaImportActions.personaImportTarget ? (
<PersonaImportUpdateDialog
fileName={
personas.personaImportActions.personaImportTargetPreview
?.fileName ?? ""
}
}}
open={teamActions.teamImportTarget !== null}
personas={personas.libraryPersonas}
preview={teamActions.teamImportTargetPreview?.preview ?? null}
team={teamActions.teamImportTarget}
/>
<PersonaImportUpdateDialog
fileName={
personas.personaImportActions.personaImportTargetPreview?.fileName ??
""
}
isPending={
personas.personaImportActions.isApplyingPersonaImportUpdate ||
personas.updatePersonaMutation.isPending
}
onApply={personas.personaImportActions.handleImportUpdateApply}
onClear={personas.personaImportActions.clearImportUpdateAndReturnToEdit}
onOpenChange={(open) => {
if (!open) {
personas.personaImportActions.closeImportUpdateDialog();
isPending={
personas.personaImportActions.isApplyingPersonaImportUpdate ||
personas.updatePersonaMutation.isPending
}
}}
open={personas.personaImportActions.personaImportTarget !== null}
persona={personas.personaImportActions.personaImportTarget}
preview={
personas.personaImportActions.personaImportTargetPreview?.preview ??
null
}
/>
onApply={personas.personaImportActions.handleImportUpdateApply}
onClear={
personas.personaImportActions.clearImportUpdateAndReturnToEdit
}
onOpenChange={(open) => {
if (!open) {
personas.personaImportActions.closeImportUpdateDialog();
}
}}
open={personas.personaImportActions.personaImportTarget !== null}
persona={personas.personaImportActions.personaImportTarget}
preview={
personas.personaImportActions.personaImportTargetPreview?.preview ??
null
}
/>
) : null}
</>
);
}
@@ -52,16 +52,18 @@ export function CreateAgentDialog({
onOpenChange: (open: boolean) => void;
}) {
const createMutation = useCreateManagedAgentMutation();
const providersQuery = useAvailableAcpRuntimes();
const allProvidersQuery = useAcpRuntimesQuery();
const backendProvidersQuery = useBackendProvidersQuery();
const providersQuery = useAvailableAcpRuntimes({ enabled: open });
const allProvidersQuery = useAcpRuntimesQuery({ enabled: open });
const backendProvidersQuery = useBackendProvidersQuery({ enabled: open });
const { lastRuntimeId, setLastRuntime } = useLastRuntime();
const [acpCommand, setAcpCommand] = React.useState("buzz-acp");
const [agentCommand, setAgentCommand] = React.useState("buzz-agent");
const [agentArgs, setAgentArgs] = React.useState("acp");
const [mcpCommand, setMcpCommand] = React.useState("");
const [mcpToolsets, setMcpToolsets] = React.useState("");
const prereqsQuery = useManagedAgentPrereqsQuery(acpCommand, mcpCommand);
const prereqsQuery = useManagedAgentPrereqsQuery(acpCommand, mcpCommand, {
enabled: open,
});
const [name, setName] = React.useState("");
const [relayUrl, setRelayUrl] = React.useState("");
const [spawnAfterCreate, setSpawnAfterCreate] = React.useState(true);
@@ -535,11 +535,13 @@ function AgentActionsMenu({
</DropdownMenuContent>
</DropdownMenu>
<EditAgentDialog
agent={agent}
onOpenChange={setEditOpen}
open={editOpen}
/>
{editOpen ? (
<EditAgentDialog
agent={agent}
onOpenChange={setEditOpen}
open={editOpen}
/>
) : null}
</>
);
}
@@ -31,7 +31,8 @@ import {
export function useManagedAgentActions() {
const relayAgentsQuery = useRelayAgentsQuery();
const managedAgentsQuery = useManagedAgentsQuery();
const channelsQuery = useChannelsQuery();
const [shouldLoadChannels, setShouldLoadChannels] = React.useState(false);
const channelsQuery = useChannelsQuery({ enabled: shouldLoadChannels });
const startMutation = useStartManagedAgentMutation();
const stopMutation = useStopManagedAgentMutation();
const deleteMutation = useDeleteManagedAgentMutation();
@@ -53,6 +54,13 @@ export function useManagedAgentActions() {
const managedAgentLogQuery = useManagedAgentLogQuery(logAgentPubkey);
React.useEffect(() => {
const timeoutId = window.setTimeout(() => {
setShouldLoadChannels(true);
}, 0);
return () => window.clearTimeout(timeoutId);
}, []);
const managedAgents = React.useMemo(
() =>
[...(managedAgentsQuery.data ?? [])].sort((left, right) => {
@@ -151,14 +159,24 @@ export function useManagedAgentActions() {
}
}
async function getChannelsForAction() {
if (channelsQuery.data) {
return channelsQuery.data;
}
const result = await channelsQuery.refetch();
return result.data ?? [];
}
async function handleStop(pubkey: string) {
clearFeedback();
try {
const agent = managedAgents.find((a) => a.pubkey === pubkey);
if (!agent) return;
const channels = await getChannelsForAction();
const result = await stopManagedAgentWithRules({
agent,
channels: channelsQuery.data ?? [],
channels,
relayAgents: relayAgentsQuery.data ?? [],
stopManagedAgent: stopMutation.mutateAsync,
});
@@ -193,9 +211,10 @@ export function useManagedAgentActions() {
try {
const agent = managedAgents.find((a) => a.pubkey === pubkey);
if (!agent) return;
const channels = await getChannelsForAction();
const result = await deleteManagedAgentWithRules({
agent,
channels: channelsQuery.data ?? [],
channels,
deleteManagedAgent: deleteMutation.mutateAsync,
presenceLookup: managedPresenceQuery.data,
relayAgents: relayAgentsQuery.data ?? [],
@@ -36,7 +36,11 @@ type PersonaFeedbackSurface = "catalog" | "library";
export function usePersonaActions() {
const queryClient = useQueryClient();
const personasQuery = usePersonasQuery();
const acpRuntimesQuery = useAcpRuntimesQuery();
const [shouldLoadAcpRuntimes, setShouldLoadAcpRuntimes] =
React.useState(false);
const acpRuntimesQuery = useAcpRuntimesQuery({
enabled: shouldLoadAcpRuntimes,
});
const createPersonaMutation = useCreatePersonaMutation();
const updatePersonaMutation = useUpdatePersonaMutation();
const deletePersonaMutation = useDeletePersonaMutation();
@@ -141,6 +145,7 @@ export function usePersonaActions() {
try {
const result = await parsePersonaFiles(fileBytes, fileName);
if (isSingleItemFile(fileBytes) && result.personas.length === 1) {
setShouldLoadAcpRuntimes(true);
setPersonaDialogState(importPersonaDialogState(result.personas[0]));
} else if (result.personas.length > 0) {
setBatchImportResult(result);
@@ -182,16 +187,19 @@ export function usePersonaActions() {
function openCreate() {
clearFeedback("library");
setShouldLoadAcpRuntimes(true);
setPersonaDialogState(createPersonaDialogState());
}
function openEdit(persona: AgentPersona) {
clearFeedback("library");
setShouldLoadAcpRuntimes(true);
setPersonaDialogState(editPersonaDialogState(persona));
}
function openDuplicate(persona: AgentPersona) {
clearFeedback("library");
setShouldLoadAcpRuntimes(true);
setPersonaDialogState(duplicatePersonaDialogState(persona));
}
+2 -1
View File
@@ -101,8 +101,9 @@ function setChannelArchivedState(
);
}
export function useChannelsQuery() {
export function useChannelsQuery(options?: { enabled?: boolean }) {
return useQuery({
enabled: options?.enabled ?? true,
queryKey: channelsQueryKey,
queryFn: async () => sortChannels(await getChannels()),
staleTime: 60_000,