import { toNumber } from "../format.ts"; import type { GatewayBrowserClient } from "../gateway.ts"; import type { SessionsListResult } from "../types.ts"; export type ArchivedSession = { sessionId: string; agentId: string; sessionKey: string; displayName?: string; createdAt: number; updatedAt: number; archivedAt: number; messageCount: number; filePath: string; firstMessage?: string; channel?: string; chatType?: string; totalTokens?: number; status?: string; }; export type ArchivedSessionsResult = { sessions: ArchivedSession[]; total: number; }; export type SessionsState = { client: GatewayBrowserClient | null; connected: boolean; sessionsLoading: boolean; sessionsResult: SessionsListResult | null; sessionsError: string | null; sessionsFilterActive: string; sessionsFilterLimit: string; sessionsIncludeGlobal: boolean; sessionsIncludeUnknown: boolean; // Archived sessions state archivedSessionsLoading: boolean; archivedSessionsResult: ArchivedSessionsResult | null; archivedSessionsError: string | null; archivedSessionsSearch: string; archivedSessionsPageSize: number; archivedSessionsPage: number; }; export async function loadSessions( state: SessionsState, overrides?: { activeMinutes?: number; limit?: number; includeGlobal?: boolean; includeUnknown?: boolean; }, ) { if (!state.client || !state.connected) { return; } if (state.sessionsLoading) { return; } state.sessionsLoading = true; state.sessionsError = null; try { const includeGlobal = overrides?.includeGlobal ?? state.sessionsIncludeGlobal; const includeUnknown = overrides?.includeUnknown ?? state.sessionsIncludeUnknown; const activeMinutes = overrides?.activeMinutes ?? toNumber(state.sessionsFilterActive, 0); const limit = overrides?.limit ?? toNumber(state.sessionsFilterLimit, 0); const params: Record = { includeGlobal, includeUnknown, }; if (activeMinutes > 0) { params.activeMinutes = activeMinutes; } if (limit > 0) { params.limit = limit; } const res = await state.client.request("sessions.list", params); if (res) { state.sessionsResult = res; } } catch (err) { state.sessionsError = String(err); } finally { state.sessionsLoading = false; } } export async function patchSession( state: SessionsState, key: string, patch: { label?: string | null; thinkingLevel?: string | null; verboseLevel?: string | null; reasoningLevel?: string | null; }, ) { if (!state.client || !state.connected) { return; } const params: Record = { key }; if ("label" in patch) { params.label = patch.label; } if ("thinkingLevel" in patch) { params.thinkingLevel = patch.thinkingLevel; } if ("verboseLevel" in patch) { params.verboseLevel = patch.verboseLevel; } if ("reasoningLevel" in patch) { params.reasoningLevel = patch.reasoningLevel; } try { await state.client.request("sessions.patch", params); await loadSessions(state); } catch (err) { state.sessionsError = String(err); } } export async function deleteSession(state: SessionsState, key: string): Promise { if (!state.client || !state.connected) { return false; } if (state.sessionsLoading) { return false; } const confirmed = window.confirm( `Delete session "${key}"?\n\nDeletes the session entry and archives its transcript.`, ); if (!confirmed) { return false; } state.sessionsLoading = true; state.sessionsError = null; try { await state.client.request("sessions.delete", { key, deleteTranscript: true }); return true; } catch (err) { state.sessionsError = String(err); return false; } finally { state.sessionsLoading = false; } } export async function deleteSessionAndRefresh(state: SessionsState, key: string): Promise { const deleted = await deleteSession(state, key); if (!deleted) { return false; } await loadSessions(state); return true; } // ── Recent Archived Sessions (for chat dropdown) ──────────────── export async function loadRecentArchivedSessions( state: SessionsState & { recentArchivedSessions: ArchivedSessionsResult["sessions"] | null }, ) { if (!state.client || !state.connected) { return; } try { const res = await state.client.request( "sessions.archived", { limit: 10 }, ); if (res) { state.recentArchivedSessions = res.sessions; } } catch { // Silently fail — dropdown will just show active sessions } } // ── Archived Sessions ──────────────────────────────────────────── export async function loadArchivedSessions( state: SessionsState, agentId?: string, search?: string, limit?: number, offset?: number, ) { if (!state.client || !state.connected) { return; } if (state.archivedSessionsLoading) { return; } state.archivedSessionsLoading = true; state.archivedSessionsError = null; try { const params: Record = {}; if (agentId) { params.agentId = agentId; } if (search?.trim()) { params.search = search.trim(); } if (limit != null) { params.limit = limit; } if (offset != null) { params.offset = offset; } const res = await state.client.request( "sessions.archived", params, ); if (res) { state.archivedSessionsResult = res; } } catch (err) { state.archivedSessionsError = String(err); } finally { state.archivedSessionsLoading = false; } } export async function resumeSession(state: SessionsState, sessionId: string): Promise { if (!state.client || !state.connected) { return false; } try { await state.client.request("sessions.resume", { sessionId }); return true; } catch (err) { state.archivedSessionsError = String(err); return false; } } export async function renameSession( state: SessionsState, sessionId: string, name: string, ): Promise { if (!state.client || !state.connected) { return false; } try { await state.client.request("sessions.rename", { sessionId, name }); // Reload archived sessions to reflect the change await loadArchivedSessions(state, undefined, state.archivedSessionsSearch || undefined, state.archivedSessionsPageSize, (state.archivedSessionsPage - 1) * state.archivedSessionsPageSize); return true; } catch (err) { state.archivedSessionsError = String(err); return false; } } export async function deleteArchivedSession( state: SessionsState, sessionId: string, ): Promise { if (!state.client || !state.connected) { return false; } const confirmed = window.confirm( `Delete archived session?\n\nThis will permanently delete the session and its transcript.`, ); if (!confirmed) { return false; } try { await state.client.request("sessions.deleteArchived", { sessionId }); // Reload archived sessions to reflect the change await loadArchivedSessions(state, undefined, state.archivedSessionsSearch || undefined, state.archivedSessionsPageSize, (state.archivedSessionsPage - 1) * state.archivedSessionsPageSize); return true; } catch (err) { state.archivedSessionsError = String(err); return false; } }