feat: unified sessions — merge assistant chat into ai_sessions table

Add session_type ('guided'|'chat') and title columns to ai_sessions,
enabling both FlowPilot guided sessions and assistant chat sessions to
live in a single table. This is the foundation for a unified session
history and consistent UX across both interaction modes.

Backend:
- Migration 066: session_type + title columns
- unified_chat_service: chat sessions on ai_sessions with same AI/RAG
- POST /ai-sessions supports session_type='chat' creation
- POST /ai-sessions/{id}/chat for chat messages
- DELETE /ai-sessions/{id} for session deletion
- session_type filter on GET /ai-sessions

Frontend:
- AssistantChatPage rewired to aiSessionsApi (no more assistantChatApi)
- /assistant/:sessionId route for deep-linking
- Session history: type filter pills (All/Guided/Chat), type icons
- Dashboard: both types shown with correct routing and icons
- Fixed glass-border → border-default in dashboard components

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-23 17:29:25 +00:00
parent 72678e7f26
commit b414502062
15 changed files with 685 additions and 88 deletions

View File

@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import { CheckCircle, AlertTriangle, XCircle, ArrowRight } from 'lucide-react'
import { CheckCircle, AlertTriangle, XCircle, ArrowRight, MessageCircle } from 'lucide-react'
import { aiSessionsApi } from '@/api/aiSessions'
import type { AISessionSummary } from '@/types/ai-session'
@@ -44,7 +44,7 @@ export function RecentFlowPilotSessions() {
<div className="card-flat">
<div
className="flex items-center justify-between px-5 py-3"
style={{ borderBottom: '1px solid var(--glass-border)' }}
style={{ borderBottom: '1px solid var(--color-border-default)' }}
>
<h3 className="font-heading text-sm font-bold text-foreground">Recent Sessions</h3>
<Link
@@ -61,16 +61,22 @@ export function RecentFlowPilotSessions() {
return (
<button
key={session.id}
onClick={() => navigate(`/pilot/${session.id}`)}
onClick={() => navigate(session.session_type === 'chat' ? `/assistant/${session.id}` : `/pilot/${session.id}`)}
className="flex w-full items-center gap-3 px-5 py-3 text-left hover:bg-[rgba(255,255,255,0.02)] transition-colors"
style={{
borderBottom: i < sessions.length - 1 ? '1px solid var(--glass-border)' : undefined,
borderBottom: i < sessions.length - 1 ? '1px solid var(--color-border-default)' : undefined,
}}
>
<StatusIcon size={14} style={{ color: config.color }} className="shrink-0" />
{session.session_type === 'chat' ? (
<MessageCircle size={14} className="shrink-0 text-violet-400" />
) : (
<StatusIcon size={14} style={{ color: config.color }} className="shrink-0" />
)}
<div className="flex-1 min-w-0">
<p className="text-sm text-foreground truncate">
{session.problem_summary || 'Session'}
{session.session_type === 'chat'
? (session.title || session.problem_summary || 'Chat')
: (session.problem_summary || 'Session')}
</p>
</div>
<span className="shrink-0 font-sans text-xs text-muted-foreground">