diff --git a/CHANGELOG.md b/CHANGELOG.md index 37612542f..53942f148 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ - TUI: show provider/model labels for the active session and default model. - Heartbeat: add per-agent heartbeat configuration and multi-agent docs example. - UI: show gateway auth guidance + doc link on unauthorized Control UI connections. +- UI: add session deletion action in Control UI sessions list. (#1017) — thanks @Szpadel. - Security: warn on weak model tiers (Haiku, below GPT-5, below Claude 4.5) in `clawdbot security audit`. - Apps: store node auth tokens encrypted (Keychain/SecurePrefs). - Cron: isolated cron jobs now start a fresh session id on every run to prevent context buildup. diff --git a/ui/src/styles/components.css b/ui/src/styles/components.css index 83bc13953..ee27aa0fb 100644 --- a/ui/src/styles/components.css +++ b/ui/src/styles/components.css @@ -537,7 +537,7 @@ .table-head, .table-row { display: grid; - grid-template-columns: 1.4fr 0.8fr 0.8fr 0.7fr 0.8fr 0.8fr; + grid-template-columns: 1.4fr 1fr 0.8fr 0.7fr 0.8fr 0.8fr 0.8fr 0.8fr 0.6fr; gap: 12px; align-items: center; } diff --git a/ui/src/ui/app-render.ts b/ui/src/ui/app-render.ts index 0d2276d30..08fa583ae 100644 --- a/ui/src/ui/app-render.ts +++ b/ui/src/ui/app-render.ts @@ -57,7 +57,7 @@ import { updateTelegramForm, } from "./controllers/connections"; import { loadPresence } from "./controllers/presence"; -import { loadSessions, patchSession } from "./controllers/sessions"; +import { deleteSession, loadSessions, patchSession } from "./controllers/sessions"; import { installSkill, loadSkills, @@ -277,11 +277,12 @@ export function renderApp(state: AppViewState) { state.sessionsFilterLimit = next.limit; state.sessionsIncludeGlobal = next.includeGlobal; state.sessionsIncludeUnknown = next.includeUnknown; - }, - onRefresh: () => loadSessions(state), - onPatch: (key, patch) => patchSession(state, key, patch), - }) - : nothing} + }, + onRefresh: () => loadSessions(state), + onPatch: (key, patch) => patchSession(state, key, patch), + onDelete: (key) => deleteSession(state, key), + }) + : nothing} ${state.tab === "cron" ? renderCron({ diff --git a/ui/src/ui/controllers/sessions.ts b/ui/src/ui/controllers/sessions.ts index faf61e5a9..71c532e64 100644 --- a/ui/src/ui/controllers/sessions.ts +++ b/ui/src/ui/controllers/sessions.ts @@ -60,3 +60,22 @@ export async function patchSession( state.sessionsError = String(err); } } + +export async function deleteSession(state: SessionsState, key: string) { + if (!state.client || !state.connected) return; + if (state.sessionsLoading) return; + const confirmed = window.confirm( + `Delete session "${key}"?\n\nDeletes the session entry and archives its transcript.`, + ); + if (!confirmed) return; + state.sessionsLoading = true; + state.sessionsError = null; + try { + await state.client.request("sessions.delete", { key, deleteTranscript: true }); + await loadSessions(state); + } catch (err) { + state.sessionsError = String(err); + } finally { + state.sessionsLoading = false; + } +} diff --git a/ui/src/ui/views/sessions.ts b/ui/src/ui/views/sessions.ts index 1c15a2555..f28abde44 100644 --- a/ui/src/ui/views/sessions.ts +++ b/ui/src/ui/views/sessions.ts @@ -29,6 +29,7 @@ export type SessionsProps = { reasoningLevel?: string | null; }, ) => void; + onDelete: (key: string) => void; }; const THINK_LEVELS = ["", "off", "minimal", "low", "medium", "high"] as const; @@ -157,10 +158,13 @@ export function renderSessions(props: SessionsProps) {