mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
214 lines
5.8 KiB
TypeScript
214 lines
5.8 KiB
TypeScript
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<unknown>;
|
|
type StopManagedAgent = (pubkey: string) => Promise<unknown>;
|
|
type DeleteManagedAgent = (input: DeleteManagedAgentInput) => Promise<unknown>;
|
|
|
|
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<ManagedAgent, "status">) {
|
|
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<ManagedAgent, "pubkey">,
|
|
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<ManagedAgentActionResult> {
|
|
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<ManagedAgentActionResult> {
|
|
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 {};
|
|
}
|