/** * sessionStore.ts - Session Management Store * * Extracted from gatewayStore.ts for Store Refactoring. * Manages Gateway sessions and session messages. */ import { create } from 'zustand'; import type { GatewayClient } from '../lib/gateway-client'; // === Types === export interface Session { id: string; agentId: string; createdAt: string; updatedAt?: string; messageCount?: number; status?: 'active' | 'archived' | 'expired'; metadata?: Record; } export interface SessionMessage { id: string; role: 'user' | 'assistant' | 'system'; content: string; createdAt: string; tokens?: { input?: number; output?: number }; } // === Raw API Response Types === interface RawSession { id?: string; sessionId?: string; session_id?: string; agentId?: string; agent_id?: string; model?: string; status?: string; createdAt?: string; created_at?: string; updatedAt?: string; updated_at?: string; messageCount?: number; message_count?: number; metadata?: Record; } interface RawSessionMessage { id?: string; messageId?: string; message_id?: string; role?: string; content?: string; createdAt?: string; created_at?: string; metadata?: Record; tokens?: { input?: number; output?: number }; } // === Client Interface === interface SessionClient { listSessions(opts?: { limit?: number; offset?: number }): Promise<{ sessions?: RawSession[] } | null>; getSession(sessionId: string): Promise | null>; createSession(params: { agent_id: string; metadata?: Record }): Promise | null>; deleteSession(sessionId: string): Promise; getSessionMessages(sessionId: string, opts?: { limit?: number; offset?: number }): Promise<{ messages?: RawSessionMessage[] } | null>; } // === Store Interface === export interface SessionStateSlice { sessions: Session[]; sessionMessages: Record; isLoading: boolean; error: string | null; } export interface SessionActionsSlice { loadSessions: (opts?: { limit?: number; offset?: number }) => Promise; getSession: (sessionId: string) => Promise; createSession: (agentId: string, metadata?: Record) => Promise; deleteSession: (sessionId: string) => Promise; loadSessionMessages: (sessionId: string, opts?: { limit?: number; offset?: number }) => Promise; } export type SessionStore = SessionStateSlice & SessionActionsSlice & { client: SessionClient | null }; // === Store Implementation === export const useSessionStore = create((set, get) => ({ // Initial state sessions: [], sessionMessages: {}, isLoading: false, error: null, client: null, loadSessions: async (opts?: { limit?: number; offset?: number }) => { const client = get().client; if (!client) return; try { const result = await client.listSessions(opts); const sessions: Session[] = (result?.sessions || []) .filter((s: RawSession) => s.id || s.session_id) .map((s: RawSession) => ({ id: s.id || s.session_id || '', agentId: s.agent_id || s.agentId || '', createdAt: s.created_at || s.createdAt || new Date().toISOString(), updatedAt: s.updated_at || s.updatedAt, messageCount: s.message_count || s.messageCount, status: s.status as Session['status'], metadata: s.metadata, })); set({ sessions }); } catch { /* ignore if sessions API not available */ } }, getSession: async (sessionId: string) => { const client = get().client; if (!client) return undefined; try { const result = await client.getSession(sessionId); if (!result) return undefined; const session: Session = { id: result.id as string, agentId: result.agent_id as string, createdAt: result.created_at as string, updatedAt: result.updated_at as string | undefined, messageCount: result.message_count as number | undefined, status: result.status as Session['status'], metadata: result.metadata as Record | undefined, }; set(state => ({ sessions: state.sessions.some(s => s.id === sessionId) ? state.sessions.map(s => s.id === sessionId ? session : s) : [...state.sessions, session], })); return session; } catch { return undefined; } }, createSession: async (agentId: string, metadata?: Record) => { const client = get().client; if (!client) return undefined; try { const result = await client.createSession({ agent_id: agentId, metadata }); if (!result) return undefined; const session: Session = { id: result.id as string, agentId: result.agent_id as string, createdAt: result.created_at as string, status: 'active', metadata, }; set(state => ({ sessions: [...state.sessions, session] })); return session; } catch (err: unknown) { set({ error: err instanceof Error ? err.message : String(err) }); return undefined; } }, deleteSession: async (sessionId: string) => { const client = get().client; if (!client) return; try { await client.deleteSession(sessionId); set(state => ({ sessions: state.sessions.filter(s => s.id !== sessionId), sessionMessages: Object.fromEntries( Object.entries(state.sessionMessages).filter(([id]) => id !== sessionId) ), })); } catch (err: unknown) { set({ error: err instanceof Error ? err.message : String(err) }); throw err; } }, loadSessionMessages: async (sessionId: string, opts?: { limit?: number; offset?: number }) => { const client = get().client; if (!client) return []; try { const result = await client.getSessionMessages(sessionId, opts); const messages: SessionMessage[] = (result?.messages || []).map((m: RawSessionMessage) => ({ id: m.id || m.message_id || '', role: (m.role || 'user') as 'user' | 'assistant' | 'system', content: m.content || '', createdAt: m.created_at || m.createdAt || new Date().toISOString(), tokens: m.tokens, })); set(state => ({ sessionMessages: { ...state.sessionMessages, [sessionId]: messages }, })); return messages; } catch { return []; } }, })); // === Client Injection === function createSessionClientFromGateway(client: GatewayClient): SessionClient { return { listSessions: (opts) => client.listSessions(opts), getSession: (sessionId) => client.getSession(sessionId), createSession: (params) => client.createSession(params), deleteSession: async (sessionId) => { await client.deleteSession(sessionId); }, getSessionMessages: (sessionId, opts) => client.getSessionMessages(sessionId, opts), }; } export function setSessionStoreClient(client: unknown): void { const sessionClient = createSessionClientFromGateway(client as GatewayClient); useSessionStore.setState({ client: sessionClient }); }