feat: add conversation history feature to AI assistant

- Add ConversationHistory dropdown component with list of past conversations
- Add useConversations hook for fetching and managing conversations via React Query
- Implement conversation switching with proper state management
- Fix bug where reopening panel showed new greeting instead of resuming conversation
- Fix bug where selecting from history caused conversation ID to revert
- Add server-side history context loading for resumed conversations
- Add Playwright E2E tests for conversation history feature
- Add logging for debugging conversation flow

Key changes:
- AssistantPanel: manages conversation state with localStorage persistence
- AssistantChat: header with [+] New Chat and [History] buttons
- Server: skips greeting for resumed conversations, loads history context on first message
- Fixed race condition in onConversationCreated callback
This commit is contained in:
liri
2026-01-16 21:47:58 +00:00
parent 91cc00a9d0
commit 7d761cb8d0
12 changed files with 1291 additions and 44 deletions

View File

@@ -0,0 +1,47 @@
/**
* React Query hooks for assistant conversation management
*/
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import * as api from '../lib/api'
/**
* List all conversations for a project
*/
export function useConversations(projectName: string | null) {
return useQuery({
queryKey: ['conversations', projectName],
queryFn: () => api.listAssistantConversations(projectName!),
enabled: !!projectName,
staleTime: 30000, // Cache for 30 seconds
})
}
/**
* Get a single conversation with all its messages
*/
export function useConversation(projectName: string | null, conversationId: number | null) {
return useQuery({
queryKey: ['conversation', projectName, conversationId],
queryFn: () => api.getAssistantConversation(projectName!, conversationId!),
enabled: !!projectName && !!conversationId,
})
}
/**
* Delete a conversation
*/
export function useDeleteConversation(projectName: string) {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (conversationId: number) =>
api.deleteAssistantConversation(projectName, conversationId),
onSuccess: (_, deletedId) => {
// Invalidate conversations list
queryClient.invalidateQueries({ queryKey: ['conversations', projectName] })
// Remove the specific conversation from cache
queryClient.removeQueries({ queryKey: ['conversation', projectName, deletedId] })
},
})
}