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:
@@ -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',
|
||||
|
||||
@@ -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>>;
|
||||
|
||||
Reference in New Issue
Block a user