From c412e44f1045cb086ca527c28b12f8046ed9db7a Mon Sep 17 00:00:00 2001 From: Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com> Date: Wed, 4 Feb 2026 18:50:44 +0800 Subject: [PATCH] fix(desktop): update use-local-chat to use ContentBlock[] instead of string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Message.content changed from string to ContentBlock[] — update desktop's local chat hook to match: extractContentFromAgentEvent returns ContentBlock[], and history messages are normalized from string to ContentBlock[]. Co-Authored-By: Claude Opus 4.5 --- apps/desktop/src/hooks/use-local-chat.ts | 41 +++++++++++++----------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/apps/desktop/src/hooks/use-local-chat.ts b/apps/desktop/src/hooks/use-local-chat.ts index caacf681..ebe19d0c 100644 --- a/apps/desktop/src/hooks/use-local-chat.ts +++ b/apps/desktop/src/hooks/use-local-chat.ts @@ -6,6 +6,7 @@ */ import { useState, useEffect, useCallback, useRef } from 'react' import { useMessagesStore } from '@multica/store' +import type { ContentBlock } from '@multica/sdk' interface UseLocalChatOptions { agentId: string @@ -45,7 +46,15 @@ export function useLocalChat({ agentId }: UseLocalChatOptions): UseLocalChatRetu try { const result = await window.electronAPI.localChat.getHistory(agentId) if (result.messages && result.messages.length > 0) { - useMessagesStore.getState().loadMessages(result.messages) + // Normalize: IPC may return content as string, store expects ContentBlock[] + useMessagesStore.getState().loadMessages( + result.messages.map((m: Record) => ({ + ...m, + content: typeof m.content === 'string' + ? (m.content ? [{ type: 'text' as const, text: m.content }] : []) + : (m.content ?? []), + })) as import('@multica/store').Message[] + ) } } catch { // History load is best-effort @@ -74,18 +83,17 @@ export function useLocalChat({ agentId }: UseLocalChatOptions): UseLocalChatRetu if (agentEvent.type === 'message_start') { currentStreamRef.current = streamId store.startStream(streamId, agentId) - // Extract initial text if any - const text = extractTextFromAgentEvent(agentEvent) - if (text) store.appendStream(streamId, text) + const content = extractContentFromAgentEvent(agentEvent) + if (content.length) store.appendStream(streamId, content) } else if (agentEvent.type === 'message_update') { - const text = extractTextFromAgentEvent(agentEvent) - if (text && currentStreamRef.current) { - store.appendStream(currentStreamRef.current, text) + const content = extractContentFromAgentEvent(agentEvent) + if (content.length && currentStreamRef.current) { + store.appendStream(currentStreamRef.current, content) } } else if (agentEvent.type === 'message_end') { - const text = extractTextFromAgentEvent(agentEvent) + const content = extractContentFromAgentEvent(agentEvent) if (currentStreamRef.current) { - store.endStream(currentStreamRef.current, text) + store.endStream(currentStreamRef.current, content) currentStreamRef.current = null } setIsLoading(false) @@ -131,14 +139,9 @@ export function useLocalChat({ agentId }: UseLocalChatOptions): UseLocalChatRetu } } -/** - * Extract text content from AgentEvent message. - * Same logic as @multica/sdk extractTextFromEvent. - */ -function extractTextFromAgentEvent(event: { message?: { content?: Array<{ type: string; text?: string }> } }): string { - if (!event.message?.content) return '' - return event.message.content - .filter((c): c is { type: 'text'; text: string } => c.type === 'text' && !!c.text) - .map((c) => c.text) - .join('') +/** Extract content blocks from AgentEvent message */ +function extractContentFromAgentEvent(event: { message?: { content?: unknown } }): ContentBlock[] { + if (!event.message?.content) return [] + const content = event.message.content + return Array.isArray(content) ? content as ContentBlock[] : [] }