fix(desktop): clean up managed agents when channel is deleted (#152)

This commit is contained in:
Wes
2026-03-21 10:21:19 -07:00
committed by GitHub
parent 3bdd86755a
commit 9b9a077737
2 changed files with 64 additions and 2 deletions

View File

@@ -0,0 +1,51 @@
/**
* Best-effort cleanup of managed agents when a channel is deleted.
*
* Each agent added via the "Add agents" dialog is a unique process scoped to
* the channel. When the channel is deleted these orphaned agents should be
* removed — but only if they are not members of any other channel.
*/
import {
deleteManagedAgent,
getChannelMembers,
listManagedAgents,
listRelayAgents,
} from "@/shared/api/tauri";
export async function cleanupChannelAgents(channelId: string): Promise<void> {
const [members, managedAgents, relayAgents] = await Promise.all([
getChannelMembers(channelId),
listManagedAgents(),
listRelayAgents(),
]);
const memberPubkeys = new Set(
members.map((member) => member.pubkey.toLowerCase()),
);
// Find managed agents that are members of this channel.
const agentsInChannel = managedAgents.filter((agent) =>
memberPubkeys.has(agent.pubkey.toLowerCase()),
);
// Only delete agents that are NOT members of any other channel.
const agentsToDelete = agentsInChannel.filter((agent) => {
const relayAgent = relayAgents.find(
(ra) => ra.pubkey.toLowerCase() === agent.pubkey.toLowerCase(),
);
if (!relayAgent) {
// Not found in relay — safe to delete.
return true;
}
// Only delete if this is the agent's only channel.
const otherChannels = relayAgent.channelIds.filter(
(id) => id !== channelId,
);
return otherChannels.length === 0;
});
// Delete orphaned agents (best-effort — don't block channel deletion).
await Promise.allSettled(
agentsToDelete.map((agent) => deleteManagedAgent(agent.pubkey)),
);
}

View File

@@ -25,6 +25,7 @@ import {
unarchiveChannel,
updateChannel,
} from "@/shared/api/tauri";
import { cleanupChannelAgents } from "@/features/channels/cleanupChannelAgents";
import type {
AddChannelMembersInput,
Channel,
@@ -343,6 +344,13 @@ export function useDeleteChannelMutation(channelId: string | null) {
throw new Error("No channel selected.");
}
// Best-effort cleanup of managed agents scoped to this channel.
try {
await cleanupChannelAgents(channelId);
} catch (error) {
console.warn("Failed to clean up managed agents:", error);
}
await deleteChannel(channelId);
},
onSuccess: () => {
@@ -361,7 +369,11 @@ export function useDeleteChannelMutation(channelId: string | null) {
});
},
onSettled: async () => {
await queryClient.invalidateQueries({ queryKey: channelsQueryKey });
await Promise.all([
queryClient.invalidateQueries({ queryKey: channelsQueryKey }),
queryClient.invalidateQueries({ queryKey: ["managed-agents"] }),
queryClient.invalidateQueries({ queryKey: ["relay-agents"] }),
]);
},
});
}
@@ -478,7 +490,6 @@ export function useSelectedChannel(
}
// ── Canvas ────────────────────────────────────────────────────────────────────
export function useCanvasQuery(channelId: string | null, enabled = true) {
return useQuery({
queryKey: ["channel-canvas", channelId],