fix: stream session updates live

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-03-21 16:12:48 +01:00
co-authored by Copilot
parent 0b15af444d
commit 9ba0174f68
6 changed files with 327 additions and 49 deletions
+2
View File
@@ -10,6 +10,7 @@ import {
pruneSessionActivities,
type SessionActivityMap,
} from '@renderer/lib/sessionActivity';
import { applySessionEventWorkspace } from '@renderer/lib/sessionWorkspace';
import { WelcomePane } from '@renderer/components/WelcomePane';
import { getElectronApi } from '@renderer/lib/electronApi';
import type { PatternDefinition } from '@shared/domain/pattern';
@@ -70,6 +71,7 @@ export default function App() {
});
const offSessionEvent = api.onSessionEvent((event) => {
setWorkspace((current) => applySessionEventWorkspace(current, event));
setSessionActivities((current) => applySessionEventActivity(current, event));
});
+155
View File
@@ -0,0 +1,155 @@
import type { SessionEventRecord } from '@shared/domain/event';
import type { ChatMessageRecord, SessionRecord } from '@shared/domain/session';
import type { WorkspaceState } from '@shared/domain/workspace';
export function applySessionEventWorkspace(
current: WorkspaceState | undefined,
event: SessionEventRecord,
): WorkspaceState | undefined {
if (!current) {
return current;
}
const sessionIndex = current.sessions.findIndex((session) => session.id === event.sessionId);
if (sessionIndex < 0) {
return current;
}
const session = current.sessions[sessionIndex];
const nextSession = applySessionEvent(session, event);
if (nextSession === session) {
return current;
}
const nextSessions = current.sessions.slice();
nextSessions[sessionIndex] = nextSession;
return {
...current,
sessions: nextSessions,
};
}
function applySessionEvent(session: SessionRecord, event: SessionEventRecord): SessionRecord {
switch (event.kind) {
case 'status':
return applyStatusEvent(session, event);
case 'error':
return applyErrorEvent(session, event);
case 'message-delta':
return applyMessageDeltaEvent(session, event);
case 'message-complete':
return applyMessageCompleteEvent(session, event);
default:
return session;
}
}
function applyStatusEvent(session: SessionRecord, event: SessionEventRecord): SessionRecord {
if (!event.status) {
return session;
}
if (session.status === event.status && (event.status === 'error' || !session.lastError)) {
return session;
}
return {
...session,
status: event.status,
lastError: event.status === 'error' ? session.lastError : undefined,
updatedAt: event.occurredAt,
};
}
function applyErrorEvent(session: SessionRecord, event: SessionEventRecord): SessionRecord {
const error = event.error?.trim();
if (session.status === 'error' && session.lastError === error) {
return session;
}
return {
...session,
status: 'error',
lastError: error,
updatedAt: event.occurredAt,
};
}
function applyMessageDeltaEvent(session: SessionRecord, event: SessionEventRecord): SessionRecord {
if (!event.messageId || event.contentDelta === undefined) {
return session;
}
const messageIndex = session.messages.findIndex((message) => message.id === event.messageId);
if (messageIndex >= 0) {
const existing = session.messages[messageIndex];
const nextMessage: ChatMessageRecord = {
...existing,
authorName: event.authorName ?? existing.authorName,
content: `${existing.content}${event.contentDelta}`,
pending: true,
};
if (
nextMessage.authorName === existing.authorName
&& nextMessage.content === existing.content
&& existing.pending
) {
return session;
}
const nextMessages = session.messages.slice();
nextMessages[messageIndex] = nextMessage;
return {
...session,
messages: nextMessages,
updatedAt: event.occurredAt,
};
}
return {
...session,
messages: [
...session.messages,
{
id: event.messageId,
role: 'assistant',
authorName: event.authorName ?? 'assistant',
content: event.contentDelta,
createdAt: event.occurredAt,
pending: true,
},
],
updatedAt: event.occurredAt,
};
}
function applyMessageCompleteEvent(session: SessionRecord, event: SessionEventRecord): SessionRecord {
if (!event.messageId) {
return session;
}
const messageIndex = session.messages.findIndex((message) => message.id === event.messageId);
if (messageIndex < 0) {
return session;
}
const existing = session.messages[messageIndex];
const nextMessage: ChatMessageRecord = {
...existing,
authorName: event.authorName ?? existing.authorName,
pending: false,
};
if (nextMessage.authorName === existing.authorName && !existing.pending) {
return session;
}
const nextMessages = session.messages.slice();
nextMessages[messageIndex] = nextMessage;
return {
...session,
messages: nextMessages,
updatedAt: event.occurredAt,
};
}