fix: chat console — message isolation, timestamps, day groups, delete, and RCA cards#40
Merged
Conversation
added 9 commits
March 25, 2026 08:52
…ete methods - listMessages(): when no investigationId, filter with WHERE investigation_id IS NULL and use latest-N subquery (DESC limit then ASC) instead of oldest-N - listRecentMessages(): add WHERE investigation_id IS NULL to exclude investigation follow-up messages from console context - Add deleteMessage(id): deletes a console message by id (guards against deleting investigation messages) - Add clearConsoleMessages(): bulk-deletes all console messages, returns count
Add DELETE /api/messages/:id (single console message) and DELETE /api/messages (clear all console messages) routes. Extend the chat:stream_end WebSocket type with id, createdAt, and investigationId fields so the client can display timestamps and target messages for deletion. Wire up the new fields in ws-handler for both conversational chat and deep-investigate flows.
… states - Extend ChatMessage with id/createdAt; populate from API and stream_end events - Filter out investigation-scoped chat/stream_end WS events from console feed - Show per-message timestamps (JetBrains Mono 10px, local time) - Insert day group separators (Today/Yesterday/Mar 22) between date boundaries - Add delete button (Trash2 icon) on hover for user messages with pessimistic delete - Add Clear All button in header with confirmation dialog - Show empty state with MessageSquare icon when no messages - Show spinner during initial history fetch (historyLoading state) - Force scroll to bottom after initial history load (initialScrollDone ref) - Add unread marker based on consoleFeed:lastVisitedAt localStorage timestamp - Remove dead investigation-enrichment code from history fetch
- Fix existing test: listMessages(50) now returns 1 (console only), not 2 - Add 7 new tests: investigation message exclusion, scoped queries, latest-N ordering, deleteMessage guards, clearConsoleMessages isolation - Fix listMessages/listRecentMessages queries to use rowid as tiebreaker for deterministic ordering within same-second timestamps
The investigationId filter on chat:stream_end was unconditionally skipping all investigation messages, including when in deep investigation mode. This caused deep investigation assistant replies to be silently dropped. Fixed by only filtering when NOT in deep mode.
The "Starting investigation of X..." message was only sent as a live WebSocket event but never persisted to the DB. After the investigation_id filter fix, the investigation summary (which WAS persisted) is correctly filtered from the console, leaving no visible response. Now the acknowledgment is saved without investigationId so it appears in the console as a permanent record of the investigation command.
The console showed "Starting investigation..." permanently with no completion status. Now persists a completion notice WITHOUT investigationId: "Investigation of X completed — 90% confidence. [View results →]". The link navigates to the investigation view. Also persists failure notices for failed investigations.
- Completion notice uses viewInvestigationId (not investigationId) so it passes the console filter but still links to the investigation - ChatPane chat handler now reads id/createdAt/viewInvestigationId from chat events for live rendering with timestamps - Failed investigations also persist a console-visible notice
…ries The investigation_id filter was too aggressive — it removed RCA completion summaries (which rendered as clickable cards) along with the Deep Investigation follow-up Q&A (the actual leak). Fix: DB query now includes messages where content starts with "**Root Cause:**" (completion summaries) even if they have an investigation_id. ChatPane WS handler allows chat events with report data through. Enrichment code restored to fetch investigation reports for RCA card rendering on history load. This preserves the original user experience: investigation commands show an acknowledgment, then an RCA card with root cause, confidence, severity — clickable to view the full investigation.
b7cfd10 to
f81aff5
Compare
WZ
added a commit
that referenced
this pull request
Apr 2, 2026
…, and RCA cards (#40) * fix(db): filter investigation messages from console queries + add delete methods - listMessages(): when no investigationId, filter with WHERE investigation_id IS NULL and use latest-N subquery (DESC limit then ASC) instead of oldest-N - listRecentMessages(): add WHERE investigation_id IS NULL to exclude investigation follow-up messages from console context - Add deleteMessage(id): deletes a console message by id (guards against deleting investigation messages) - Add clearConsoleMessages(): bulk-deletes all console messages, returns count * feat(server): DELETE message routes + stream_end metadata fields Add DELETE /api/messages/:id (single console message) and DELETE /api/messages (clear all console messages) routes. Extend the chat:stream_end WebSocket type with id, createdAt, and investigationId fields so the client can display timestamps and target messages for deletion. Wire up the new fields in ws-handler for both conversational chat and deep-investigate flows. * feat(web): timestamps, day groups, delete, unread marker, and loading states - Extend ChatMessage with id/createdAt; populate from API and stream_end events - Filter out investigation-scoped chat/stream_end WS events from console feed - Show per-message timestamps (JetBrains Mono 10px, local time) - Insert day group separators (Today/Yesterday/Mar 22) between date boundaries - Add delete button (Trash2 icon) on hover for user messages with pessimistic delete - Add Clear All button in header with confirmation dialog - Show empty state with MessageSquare icon when no messages - Show spinner during initial history fetch (historyLoading state) - Force scroll to bottom after initial history load (initialScrollDone ref) - Add unread marker based on consoleFeed:lastVisitedAt localStorage timestamp - Remove dead investigation-enrichment code from history fetch * test(db): add message isolation tests + fix rowid tiebreaker in queries - Fix existing test: listMessages(50) now returns 1 (console only), not 2 - Add 7 new tests: investigation message exclusion, scoped queries, latest-N ordering, deleteMessage guards, clearConsoleMessages isolation - Fix listMessages/listRecentMessages queries to use rowid as tiebreaker for deterministic ordering within same-second timestamps * fix(web): preserve deep investigation replies in stream_end filter The investigationId filter on chat:stream_end was unconditionally skipping all investigation messages, including when in deep investigation mode. This caused deep investigation assistant replies to be silently dropped. Fixed by only filtering when NOT in deep mode. * fix(server): persist investigation acknowledgment to messages table The "Starting investigation of X..." message was only sent as a live WebSocket event but never persisted to the DB. After the investigation_id filter fix, the investigation summary (which WAS persisted) is correctly filtered from the console, leaving no visible response. Now the acknowledgment is saved without investigationId so it appears in the console as a permanent record of the investigation command. * fix(server+web): show investigation completion in console with link The console showed "Starting investigation..." permanently with no completion status. Now persists a completion notice WITHOUT investigationId: "Investigation of X completed — 90% confidence. [View results →]". The link navigates to the investigation view. Also persists failure notices for failed investigations. * fix(server+web): investigation completion notice + viewInvestigationId - Completion notice uses viewInvestigationId (not investigationId) so it passes the console filter but still links to the investigation - ChatPane chat handler now reads id/createdAt/viewInvestigationId from chat events for live rendering with timestamps - Failed investigations also persist a console-visible notice * fix: restore RCA cards in console — filter follow-ups only, not summaries The investigation_id filter was too aggressive — it removed RCA completion summaries (which rendered as clickable cards) along with the Deep Investigation follow-up Q&A (the actual leak). Fix: DB query now includes messages where content starts with "**Root Cause:**" (completion summaries) even if they have an investigation_id. ChatPane WS handler allows chat events with report data through. Enrichment code restored to fetch investigation reports for RCA card rendering on history load. This preserves the original user experience: investigation commands show an acknowledgment, then an RCA card with root cause, confidence, severity — clickable to view the full investigation. --------- Co-authored-by: Wilson Li <wli02@fortinet.com>
This was referenced Apr 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
chat:stream_endwithid,createdAt,investigationIdfor live message managementlistMessages()to return latest-N instead of oldest-Nrowidtiebreaker for deterministic ordering within same-second timestampsKey Design Decision
The
investigation_idfilter distinguishes between:content LIKE '**Root Cause:**%', enriched with report data, rendered as clickable RCA cardsTest Coverage
Test plan
npx tsc --noEmit)🤖 Generated with Claude Code