Skip to content

History tab: typed sessions, real titles, working search

The History tab at /history lists every AI session a user has had. It derives a real, context-aware title per row, segments the list by session type (Presentations, Research, Lesson plans, Chats…), and makes search match across those titles and type labels. It serves any user of the AI surface, learner or trainer.

How it works

Before this change every row was titled "General chat" and search returned nothing, so users could not find or re-open past work. The fix needs no backend change: learning_mode already distinguishes the types and is returned on the Conversation payload.

Every row in /history is an ai_tutor Conversation. The user-meaningful kind is not conversation.type (always "ai_tutor"), it is conversation.learning_mode:

Conversation (type = "ai_tutor")
  └── learning_mode
        ├── "presentation"   → Presentations
        ├── "research"       → Research
        ├── "lesson_plan"    → Lesson plans
        ├── "question_bank"  → Question banks
        ├── "create_content" → Created content
        ├── …other Kwilo modes…
        └── null             → Chats   (deselected/default mode IS chat)

Sections render in priority order (Presentations, Research, Lesson plans, …, Chats last); empty sections are omitted. Labels reuse the aiTutor.modes.* i18n keys, so the vocabulary matches the chips users already see in the composer.

Title resolution (first non-empty wins): context.subjectcontext.topicsummary → mode-specific default ("Untitled presentation", "Research session") → "Chat". Most sessions have empty subject/summary, so the mode-aware default is what users actually see.

Search matches the derived title and the section label, not just raw subject/summary. Since the derived title always has a value, typing "presentation" or "revision" returns results.

Invariant: segmentation and labels derive purely from data already on the Conversation. Unknown learning_mode values fall back to the Chats section, so a new server-side mode without a frontend label degrades gracefully instead of crashing.

Edge cases

  • Loading: existing skeleton rows.
  • Empty (no history): existing EmptyState.
  • Search with no matches: EmptyState with the search illustration.
  • Single section: the section header still shows for consistency.
  • Unknown learning_mode: row drops into Chats.

Out of scope: row actions (rename/pin/delete), messaging (student_teacher/group) conversations, and mobile.

Where it lives

  • apps/web/src/pages/shared/HistoryPage/index.tsx: the page
  • apps/web/src/services/ai-tutor/index.ts: KWILO_MODES / MODE_IDS (mode ids + label keys)
  • apps/web/src/services/ai-tutor/types.ts: Conversation, ChatContext
  • GET /ai-tutor/conversations returns type, learning_mode, context, summary, last_message_at