import { sendChannelMessage } from "@/shared/api/tauri"; import type { Channel, ManagedAgent, PresenceLookup, RelayAgent, } from "@/shared/api/types"; import { normalizePubkey } from "@/shared/lib/pubkey"; type DeleteManagedAgentInput = { pubkey: string; forceRemoteDelete?: boolean; }; type StartManagedAgent = (pubkey: string) => Promise; type StopManagedAgent = (pubkey: string) => Promise; type DeleteManagedAgent = (input: DeleteManagedAgentInput) => Promise; type ManagedAgentChannelContext = { channels: readonly Channel[]; preferredChannelId?: string | null; relayAgents: readonly RelayAgent[]; }; type ManagedAgentActionContext = ManagedAgentChannelContext & { presenceLookup?: PresenceLookup | null; }; export type ManagedAgentActionResult = { cancelled?: boolean; noticeMessage?: string; }; export function isManagedAgentActive(agent: Pick) { return agent.status === "running" || agent.status === "deployed"; } export function getManagedAgentPrimaryActionLabel(agent: ManagedAgent) { if (agent.backend.type === "provider") { return isManagedAgentActive(agent) ? "Shutdown" : "Deploy"; } if (isManagedAgentActive(agent)) { return "Stop"; } return agent.status === "stopped" ? "Respawn" : "Spawn"; } export function resolveManagedAgentChannelId( agent: Pick, context: ManagedAgentChannelContext, ) { if (context.preferredChannelId) { return context.preferredChannelId; } const relayAgent = context.relayAgents.find( (candidate) => normalizePubkey(candidate.pubkey) === normalizePubkey(agent.pubkey), ); if (relayAgent?.channelIds?.length) { return relayAgent.channelIds[0]; } const channelName = relayAgent?.channels?.[0]; if (!channelName) { return null; } const matches = context.channels.filter( (channel) => channel.name === channelName, ); return matches.length === 1 ? matches[0].id : null; } function relayMeshAgentError(agent: ManagedAgent): string | null { if (agent.backend.type !== "local") return null; if (agent.envVars.SPROUT_AGENT_PROVIDER !== "openai") return null; if ( agent.envVars.OPENAI_COMPAT_BASE_URL?.replace(/\/+$/, "") !== "http://127.0.0.1:9337/v1" ) { return null; } return "Relay-mesh agents need a fresh serve target before start. Create a new agent with Run on relay mesh selected."; } export async function startManagedAgentWithRules({ agent, startManagedAgent, }: { agent: ManagedAgent; startManagedAgent: StartManagedAgent; }) { const relayMeshError = relayMeshAgentError(agent); if (relayMeshError) throw new Error(relayMeshError); await startManagedAgent(agent.pubkey); } export async function respawnManagedAgentWithRules({ agent, startManagedAgent, stopManagedAgent, }: { agent: ManagedAgent; startManagedAgent: StartManagedAgent; stopManagedAgent: StopManagedAgent; }) { const relayMeshError = relayMeshAgentError(agent); if (relayMeshError) throw new Error(relayMeshError); if (agent.backend.type === "local" && isManagedAgentActive(agent)) { await stopManagedAgent(agent.pubkey); } await startManagedAgent(agent.pubkey); } export async function stopManagedAgentWithRules({ agent, channels, preferredChannelId, relayAgents, stopManagedAgent, }: { agent: ManagedAgent; stopManagedAgent: StopManagedAgent; } & ManagedAgentChannelContext): Promise { if (agent.backend.type === "provider") { const channelId = resolveManagedAgentChannelId(agent, { channels, preferredChannelId, relayAgents, }); if (!channelId) { throw new Error("Cannot stop: agent is not in any channel"); } await sendChannelMessage(channelId, "!shutdown", undefined, undefined, [ agent.pubkey, ]); return { noticeMessage: "Shutdown command sent. Agent will stop shortly.", }; } await stopManagedAgent(agent.pubkey); return {}; } export async function deleteManagedAgentWithRules({ agent, channels, deleteManagedAgent, preferredChannelId, presenceLookup, relayAgents, }: { agent: ManagedAgent; deleteManagedAgent: DeleteManagedAgent; } & ManagedAgentActionContext): Promise { if (agent.backend.type === "provider" && agent.backendAgentId) { const presence = presenceLookup?.[normalizePubkey(agent.pubkey)]; const channelId = resolveManagedAgentChannelId(agent, { channels, preferredChannelId, relayAgents, }); if (channelId) { if (presence === "online" || presence === "away") { await sendChannelMessage(channelId, "!shutdown", undefined, undefined, [ agent.pubkey, ]); const confirmed = window.confirm( "Shutdown command sent, but the agent may still be running. " + "Deleting now removes the local record — the remote deployment " + "will be orphaned if shutdown hasn't completed. Continue?", ); if (!confirmed) { return { cancelled: true }; } } else { const confirmed = window.confirm( "This agent is offline but the remote deployment may still exist. " + "Deleting removes the local management record. Continue?", ); if (!confirmed) { return { cancelled: true }; } } } else { const confirmed = window.confirm( "This agent is deployed but not in any channel. " + "Deleting will orphan the remote deployment (it will keep running). Continue?", ); if (!confirmed) { return { cancelled: true }; } } } const isDeployedRemote = agent.backend.type === "provider" && agent.backendAgentId; await deleteManagedAgent({ pubkey: agent.pubkey, forceRemoteDelete: isDeployedRemote ? true : undefined, }); return {}; }