Real-Time Updates for Insights Chat (#1511)

* auto-claude: subtask-1-1 - Add INSIGHTS_SESSION_UPDATED IPC channel constant

* auto-claude: subtask-1-2 - Add type definition for onInsightsSessionUpdated in ElectronAPI interface

* auto-claude: subtask-2-1 - Emit session-updated event in InsightsService after saving assistant message

- Add 'session-updated' event emission after assistant message is saved
- Enables real-time UI updates when insights chat receives AI response

* auto-claude: subtask-2-2 - Forward session-updated event to renderer via safeSendToRenderer

* auto-claude: subtask-3-1 - Add onInsightsSessionUpdated listener to insights-api.ts

Add new event listener onInsightsSessionUpdated to the preload API that listens
for INSIGHTS_SESSION_UPDATED IPC events from the main process. This enables
the renderer to receive real-time session updates when sessions are modified.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* auto-claude: subtask-4-1 - Add session-updated listener in setupInsightsListeners

- Add onInsightsSessionUpdated listener to setupInsightsListeners()
- Update current session if incoming session ID matches
- Refresh sessions list for sidebar to show updated titles/metadata
- Add cleanup function for proper listener removal

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add error handling for loadInsightsSessions promise in session-updated listener

Addresses PR review finding: unhandled promise rejection in fire-and-forget
loadInsightsSessions call. Added .catch() handler to log errors if the
sessions list refresh fails after a session update event.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Andy
2026-01-26 09:53:30 +01:00
committed by GitHub
parent 8d8306b8eb
commit d940b6adee
7 changed files with 39 additions and 2 deletions
@@ -182,6 +182,9 @@ export class InsightsService extends EventEmitter {
session.messages.push(assistantMessage);
session.updatedAt = new Date();
this.sessionManager.saveSession(projectPath, session);
// Emit session-updated event for real-time UI updates
this.emit('session-updated', projectId, session);
} catch (error) {
// Error already emitted by executor
console.error('[InsightsService] Error executing insights:', error);
@@ -371,4 +371,9 @@ export function registerInsightsHandlers(getMainWindow: () => BrowserWindow | nu
insightsService.on("sdk-rate-limit", (rateLimitInfo: unknown) => {
safeSendToRenderer(getMainWindow, IPC_CHANNELS.CLAUDE_SDK_RATE_LIMIT, rateLimitInfo);
});
// Forward session-updated events to renderer for real-time UI updates
insightsService.on("session-updated", (projectId: string, session: unknown) => {
safeSendToRenderer(getMainWindow, IPC_CHANNELS.INSIGHTS_SESSION_UPDATED, projectId, session);
});
}
@@ -42,6 +42,9 @@ export interface InsightsAPI {
onInsightsError: (
callback: (projectId: string, error: string) => void
) => IpcListenerCleanup;
onInsightsSessionUpdated: (
callback: (projectId: string, session: InsightsSession) => void
) => IpcListenerCleanup;
}
/**
@@ -98,5 +101,10 @@ export const createInsightsAPI = (): InsightsAPI => ({
onInsightsError: (
callback: (projectId: string, error: string) => void
): IpcListenerCleanup =>
createIpcListener(IPC_CHANNELS.INSIGHTS_ERROR, callback)
createIpcListener(IPC_CHANNELS.INSIGHTS_ERROR, callback),
onInsightsSessionUpdated: (
callback: (projectId: string, session: InsightsSession) => void
): IpcListenerCleanup =>
createIpcListener(IPC_CHANNELS.INSIGHTS_SESSION_UPDATED, callback)
});
@@ -107,5 +107,6 @@ export const insightsMock = {
onInsightsStreamChunk: () => () => {},
onInsightsStatus: () => () => {},
onInsightsError: () => () => {}
onInsightsError: () => () => {},
onInsightsSessionUpdated: () => () => {}
};
@@ -415,10 +415,26 @@ export function setupInsightsListeners(): () => void {
});
});
// Listen for session updates (e.g., after assistant message saved with auto-generated title)
const unsubSessionUpdated = window.electronAPI.onInsightsSessionUpdated(
(_projectId, session: InsightsSession) => {
// Update current session if it matches
const currentSession = store().session;
if (currentSession?.id === session.id) {
store().setSession(session);
}
// Also refresh sessions list for sidebar
loadInsightsSessions(session.projectId).catch((err) => {
console.error('Failed to refresh sessions list after update:', err);
});
}
);
// Return cleanup function
return () => {
unsubStreamChunk();
unsubStatus();
unsubError();
unsubSessionUpdated();
};
}
@@ -487,6 +487,7 @@ export const IPC_CHANNELS = {
INSIGHTS_STREAM_CHUNK: 'insights:streamChunk',
INSIGHTS_STATUS: 'insights:status',
INSIGHTS_ERROR: 'insights:error',
INSIGHTS_SESSION_UPDATED: 'insights:sessionUpdated', // Event: session updated (main -> renderer)
// File explorer operations
FILE_EXPLORER_LIST: 'fileExplorer:list',
+3
View File
@@ -747,6 +747,9 @@ export interface ElectronAPI {
onInsightsError: (
callback: (projectId: string, error: string) => void
) => () => void;
onInsightsSessionUpdated: (
callback: (projectId: string, session: InsightsSession) => void
) => () => void;
// Task logs operations
getTaskLogs: (projectId: string, specId: string) => Promise<IPCResult<TaskLogs | null>>;