import { useMutation, useQueryClient } from '@tanstack/react-query' import { isPermissionModeAllowedForFlavor } from '@hapi/protocol' import type { ApiClient } from '@/api/client' import type { CodexCollaborationMode, PermissionMode } from '@/types/api' import type { ReopenSessionResponse } from '@hapi/protocol/apiTypes' import { queryKeys } from '@/lib/query-keys' import { clearMessageWindow } from '@/lib/message-window-store' import { isKnownFlavor } from '@hapi/protocol' export function useSessionActions( api: ApiClient | null, sessionId: string | null, agentFlavor?: string | null, codexCollaborationModeSupported?: boolean ): { abortSession: () => Promise archiveSession: () => Promise reopenSession: () => Promise switchSession: () => Promise setPermissionMode: (mode: PermissionMode) => Promise setCollaborationMode: (mode: CodexCollaborationMode) => Promise setModel: (model: { provider: string; modelId: string } | string | null) => Promise setModelReasoningEffort: (modelReasoningEffort: string | null) => Promise setEffort: (effort: string | null) => Promise setServiceTier: (serviceTier: string | null) => Promise renameSession: (name: string) => Promise deleteSession: () => Promise isPending: boolean } { const queryClient = useQueryClient() const invalidateSession = async () => { if (!sessionId) return await queryClient.invalidateQueries({ queryKey: queryKeys.session(sessionId) }) await queryClient.invalidateQueries({ queryKey: queryKeys.sessions }) } const invalidateCursorModels = async () => { if (!sessionId || agentFlavor !== 'cursor') return await queryClient.invalidateQueries({ queryKey: queryKeys.sessionCursorModels(sessionId) }) await queryClient.invalidateQueries({ queryKey: ['machine-cursor-models'] }) } const abortMutation = useMutation({ mutationFn: async () => { if (!api || !sessionId) { throw new Error('Session unavailable') } await api.abortSession(sessionId) }, onSuccess: () => void invalidateSession(), }) const archiveMutation = useMutation({ mutationFn: async () => { if (!api || !sessionId) { throw new Error('Session unavailable') } await api.archiveSession(sessionId) }, onSuccess: () => void invalidateSession(), }) const reopenMutation = useMutation({ mutationFn: async () => { if (!api || !sessionId) { throw new Error('Session unavailable') } return await api.reopenSession(sessionId) }, onSuccess: () => void invalidateSession(), }) const switchMutation = useMutation({ mutationFn: async () => { if (!api || !sessionId) { throw new Error('Session unavailable') } await api.switchSession(sessionId) }, onSuccess: () => void invalidateSession(), }) const permissionMutation = useMutation({ mutationFn: async (mode: PermissionMode) => { if (!api || !sessionId) { throw new Error('Session unavailable') } if (isKnownFlavor(agentFlavor) && !isPermissionModeAllowedForFlavor(mode, agentFlavor)) { throw new Error('Invalid permission mode for session flavor') } await api.setPermissionMode(sessionId, mode) }, onSuccess: () => void invalidateSession(), }) const collaborationMutation = useMutation({ mutationFn: async (mode: CodexCollaborationMode) => { if (!api || !sessionId) { throw new Error('Session unavailable') } if (agentFlavor !== 'codex') { throw new Error('Collaboration mode is only supported for Codex sessions') } if (!codexCollaborationModeSupported) { throw new Error('Collaboration mode is only supported for remote Codex sessions') } await api.setCollaborationMode(sessionId, mode) }, onSuccess: () => void invalidateSession(), }) const modelMutation = useMutation({ mutationFn: async (model: { provider: string; modelId: string } | string | null) => { if (!api || !sessionId) { throw new Error('Session unavailable') } await api.setModel(sessionId, model) }, onSuccess: () => { void (async () => { await invalidateSession() await invalidateCursorModels() })() }, }) const modelReasoningEffortMutation = useMutation({ mutationFn: async (modelReasoningEffort: string | null) => { if (!api || !sessionId) { throw new Error('Session unavailable') } if (agentFlavor !== 'codex' && agentFlavor !== 'opencode') { throw new Error('Model reasoning effort is only supported for Codex and OpenCode sessions') } if (agentFlavor === 'codex' && !codexCollaborationModeSupported) { throw new Error('Model reasoning effort is only supported for remote sessions') } await api.setModelReasoningEffort(sessionId, modelReasoningEffort) }, onSuccess: () => void invalidateSession(), }) const effortMutation = useMutation({ mutationFn: async (effort: string | null) => { if (!api || !sessionId) { throw new Error('Session unavailable') } await api.setEffort(sessionId, effort) }, onSuccess: () => void invalidateSession(), }) const serviceTierMutation = useMutation({ mutationFn: async (serviceTier: string | null) => { if (!api || !sessionId) { throw new Error('Session unavailable') } if (agentFlavor !== 'codex') { throw new Error('Fast mode is only supported for Codex sessions') } if (!codexCollaborationModeSupported) { throw new Error('Fast mode is only supported for remote sessions') } await api.setServiceTier(sessionId, serviceTier) }, onSuccess: () => void invalidateSession(), }) const renameMutation = useMutation({ mutationFn: async (name: string) => { if (!api || !sessionId) { throw new Error('Session unavailable') } await api.renameSession(sessionId, name) }, onSuccess: () => void invalidateSession(), }) const deleteMutation = useMutation({ mutationFn: async () => { if (!api || !sessionId) { throw new Error('Session unavailable') } await api.deleteSession(sessionId) }, onSuccess: async () => { if (!sessionId) return queryClient.removeQueries({ queryKey: queryKeys.session(sessionId) }) clearMessageWindow(sessionId) await queryClient.invalidateQueries({ queryKey: queryKeys.sessions }) }, }) return { abortSession: abortMutation.mutateAsync, archiveSession: archiveMutation.mutateAsync, reopenSession: reopenMutation.mutateAsync, switchSession: switchMutation.mutateAsync, setPermissionMode: permissionMutation.mutateAsync, setCollaborationMode: collaborationMutation.mutateAsync, setModel: modelMutation.mutateAsync, setModelReasoningEffort: modelReasoningEffortMutation.mutateAsync, setEffort: effortMutation.mutateAsync, setServiceTier: serviceTierMutation.mutateAsync, renameSession: renameMutation.mutateAsync, deleteSession: deleteMutation.mutateAsync, isPending: abortMutation.isPending || archiveMutation.isPending || reopenMutation.isPending || switchMutation.isPending || permissionMutation.isPending || collaborationMutation.isPending || modelMutation.isPending || modelReasoningEffortMutation.isPending || effortMutation.isPending || serviceTierMutation.isPending || renameMutation.isPending || deleteMutation.isPending, } }