refactor: consolidate streaming text ownership and forward intent events

- Main process now always emits resolved content in message-delta events,
  making it the single authoritative text assembly point
- Renderer no longer imports or runs mergeStreamingText; uses main-process
  resolved content directly with simple append as defensive fallback
- Forward assistant-intent events through the session event pipeline; track
  currentIntent on SessionRecord, cleared on turn completion/cancellation
- Forward reasoning-delta events through the session event pipeline so
  renderers can consume incremental reasoning text
- Add SessionEventKind entries: assistant-intent, reasoning-delta
- Add SessionEventRecord fields: intent, reasoningId, reasoningDelta
- Remove dead applyTurnDelta, applyMessageReclassified, applyAgentActivity
  methods from AryxAppService (superseded by SessionTurnExecutor)
- Remove stale mergeStreamingText and appendRunActivityEvent imports from
  AryxAppService
- Add 5 regression tests: resolved content path, simple append fallback,
  intent set/clear lifecycle, duplicate suppression, intent preservation
  during status transitions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-04-13 16:12:41 +02:00
co-authored by Copilot
parent 1fdb1c27d5
commit 08886f9078
6 changed files with 175 additions and 158 deletions
+24 -3
View File
@@ -2,7 +2,6 @@ import type { SessionEventRecord } from '@shared/domain/event';
import { upsertSessionRunRecord } from '@shared/domain/runTimeline';
import type { ChatMessageRecord, SessionRecord } from '@shared/domain/session';
import type { WorkspaceState } from '@shared/domain/workspace';
import { mergeStreamingText } from '@shared/utils/streamingText';
export function applySessionEventWorkspace(
current: WorkspaceState | undefined,
@@ -45,6 +44,8 @@ function applySessionEvent(session: SessionRecord, event: SessionEventRecord): S
return applyMessageReclassifiedEvent(session, event);
case 'run-updated':
return applyRunUpdatedEvent(session, event);
case 'assistant-intent':
return applyAssistantIntentEvent(session, event);
default:
return session;
}
@@ -63,6 +64,7 @@ function applyStatusEvent(session: SessionRecord, event: SessionEventRecord): Se
...session,
status: event.status,
lastError: event.status === 'error' ? session.lastError : undefined,
currentIntent: event.status === 'idle' ? undefined : session.currentIntent,
updatedAt: event.occurredAt,
};
}
@@ -86,14 +88,19 @@ function applyMessageDeltaEvent(session: SessionRecord, event: SessionEventRecor
return session;
}
const resolvedContent = event.content ?? event.contentDelta ?? '';
const messageIndex = session.messages.findIndex((message) => message.id === event.messageId);
if (messageIndex >= 0) {
const existing = session.messages[messageIndex];
// The main process resolves content authoritatively; prefer event.content
// (full assembled text) and fall back to simple append for backward compat.
const nextContent =
event.content !== undefined
? event.content
: existing.content + (event.contentDelta ?? '');
const nextMessage: ChatMessageRecord = {
...existing,
authorName: event.authorName ?? existing.authorName,
content: event.content ?? mergeStreamingText(existing.content, resolvedContent),
content: nextContent,
pending: true,
};
@@ -114,6 +121,8 @@ function applyMessageDeltaEvent(session: SessionRecord, event: SessionEventRecor
};
}
const resolvedContent = event.content ?? event.contentDelta ?? '';
// Auto-complete any previously pending assistant messages so only
// the new message shows the "Thinking" indicator.
const completedMessages = session.messages.map((message) =>
@@ -217,3 +226,15 @@ function applyRunUpdatedEvent(session: SessionRecord, event: SessionEventRecord)
updatedAt: event.occurredAt,
};
}
function applyAssistantIntentEvent(session: SessionRecord, event: SessionEventRecord): SessionRecord {
if (!event.intent || session.currentIntent === event.intent) {
return session;
}
return {
...session,
currentIntent: event.intent,
updatedAt: event.occurredAt,
};
}