Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions desktop/src/features/channels/cleanupChannelAgents.ts
Original file line number Diff line number Diff line change
@@ -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)),
);
}
15 changes: 13 additions & 2 deletions desktop/src/features/channels/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
unarchiveChannel,
updateChannel,
} from "@/shared/api/tauri";
import { cleanupChannelAgents } from "@/features/channels/cleanupChannelAgents";
import type {
AddChannelMembersInput,
Channel,
Expand Down Expand Up @@ -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: () => {
Expand All @@ -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"] }),
]);
},
});
}
Expand Down Expand Up @@ -478,7 +490,6 @@ export function useSelectedChannel(
}

// ── Canvas ────────────────────────────────────────────────────────────────────

export function useCanvasQuery(channelId: string | null, enabled = true) {
return useQuery({
queryKey: ["channel-canvas", channelId],
Expand Down
Loading