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) {
Thinking
Verbose
Reasoning
+
Actions
${rows.length === 0 ? html`
No sessions found.
` - : rows.map((row) => renderRow(row, props.basePath, props.onPatch))} + : rows.map((row) => + renderRow(row, props.basePath, props.onPatch, props.onDelete, props.loading), + )} `; @@ -170,6 +174,8 @@ function renderRow( row: GatewaySessionRow, basePath: string, onPatch: SessionsProps["onPatch"], + onDelete: SessionsProps["onDelete"], + disabled: boolean, ) { const updated = row.updatedAt ? formatAgo(row.updatedAt) : "n/a"; const rawThinking = row.thinkingLevel ?? ""; @@ -196,6 +202,7 @@ function renderRow(
{ const value = (e.target as HTMLSelectElement).value; onPatch(row.key, { verboseLevel: value || null }); @@ -224,6 +232,7 @@ function renderRow(
+
+ +
`; }