/** * @file stopSession — the one way the UI ends live panes: **Stop** kills the * pane and ARCHIVES the session it held (`DELETE /api/run/:id?archive=1`), so * a session the user closed leaves its workspace's lists; **Sleep** kills the * pane and KEEPS the session listed, to be resumed later by sending it a * message. Both ask first when any pane holds an AI agent (it may be * mid-turn); a plain shell holds no session, so it neither asks nor sleeps. * Used by the tab bar (`ConsoleTabs`), the split panes (`Workspace`) and a * session's right-click menu (`SessionMenu`). * @author Nguyễn Ngọc Trí Vĩ */ import type { TFunction } from "i18next"; import { api, type RunHandle } from "./api"; import { confirmDialog } from "./confirm"; import { toast } from "./toast"; /** Whether a pane can sleep: an AI pane (a shell has no session to keep). */ export const canSleep = (run: RunHandle | null | undefined): boolean => !!run && run.tool !== "shell"; /** * Stop (archive) or sleep (keep) `runs` after a confirm, and return the ids * that were ended. `name` titles a one-pane confirm; `t` is the `lanes` * namespace. */ export async function endRuns( runs: RunHandle[], { sleep = false, name, t }: { sleep?: boolean; name: string; t: TFunction } ): Promise { const list = sleep ? runs.filter(canSleep) : runs; if (!list.length) return []; const kind = sleep ? "sleep" : "stop"; if ( list.some((r) => r.tool !== "shell") && !(await confirmDialog({ title: list.length > 1 ? t(`tabs.${kind}GroupTitle`, { count: list.length }) : t(`tabs.${kind}Title`, { name }), message: t(`tabs.${kind}Body`), confirmLabel: t(`tabs.${kind}Confirm`), destructive: !sleep, })) ) return []; const ended: string[] = []; for (const r of list) { try { await api.run.kill(r.id, { archive: !sleep }); ended.push(r.id); } catch (err) { toast.error(err); } } return ended; }