feat(ui): delete sessions from Control UI
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
<div>Thinking</div>
|
||||
<div>Verbose</div>
|
||||
<div>Reasoning</div>
|
||||
<div>Actions</div>
|
||||
</div>
|
||||
${rows.length === 0
|
||||
? html`<div class="muted">No sessions found.</div>`
|
||||
: rows.map((row) => renderRow(row, props.basePath, props.onPatch))}
|
||||
: rows.map((row) =>
|
||||
renderRow(row, props.basePath, props.onPatch, props.onDelete, props.loading),
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
`;
|
||||
@@ -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(
|
||||
<div>
|
||||
<select
|
||||
.value=${thinking}
|
||||
?disabled=${disabled}
|
||||
@change=${(e: Event) => {
|
||||
const value = (e.target as HTMLSelectElement).value;
|
||||
onPatch(row.key, {
|
||||
@@ -211,6 +218,7 @@ function renderRow(
|
||||
<div>
|
||||
<select
|
||||
.value=${verbose}
|
||||
?disabled=${disabled}
|
||||
@change=${(e: Event) => {
|
||||
const value = (e.target as HTMLSelectElement).value;
|
||||
onPatch(row.key, { verboseLevel: value || null });
|
||||
@@ -224,6 +232,7 @@ function renderRow(
|
||||
<div>
|
||||
<select
|
||||
.value=${reasoning}
|
||||
?disabled=${disabled}
|
||||
@change=${(e: Event) => {
|
||||
const value = (e.target as HTMLSelectElement).value;
|
||||
onPatch(row.key, { reasoningLevel: value || null });
|
||||
@@ -234,6 +243,11 @@ function renderRow(
|
||||
)}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn danger" ?disabled=${disabled} @click=${() => onDelete(row.key)}>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user