import React, { useMemo } from 'react'; import { Text, View } from 'react-native'; import { MessageBubble } from './conversationParts'; import { conversationStyles } from './conversationStyles'; import { formatDateLabel, getMessageFiles, getMessageToolCalls, shouldShowDateSeparator } from './messageUtils'; import type { SuperagentAgent, SuperagentConnector, SuperagentConnectorActionInput, SuperagentConversation, SuperagentMarkdownRenderer, SuperagentMessage, SuperagentToolRenderers, } from '../../types'; export function ConversationMessageList({ agent, availableConnectors, conversationId, currentUserAvatarUrl, messages, onConnectConnector, onReplyMessage, renderMarkdown, showDebugPayloads, submitToolCallInput, toolRenderers, }: { agent: SuperagentAgent; availableConnectors?: SuperagentConnector[]; conversationId: string | null; currentUserAvatarUrl?: string | null; messages: SuperagentMessage[]; onConnectConnector?: (input: SuperagentConnectorActionInput) => Promise | boolean | string | void; onReplyMessage?: (message: SuperagentMessage) => void; renderMarkdown?: SuperagentMarkdownRenderer; showDebugPayloads?: boolean; submitToolCallInput?: ( toolCallId: string, approve: boolean, extraUserInput?: unknown, originRequestId?: string, ) => Promise; toolRenderers?: SuperagentToolRenderers; }) { const displayMessages = useMemo(() => mergeConsecutiveToolOnlyMessages(messages), [messages]); const lastAssistantMessageIndex = useMemo(() => { for (let index = displayMessages.length - 1; index >= 0; index -= 1) { if (displayMessages[index].role === 'assistant') return index; } return -1; }, [displayMessages]); return ( <> {displayMessages.map((message, index) => { const previousMessage = displayMessages[index - 1]; const nextMessage = displayMessages[index + 1]; const showAvatar = !previousMessage || previousMessage.role !== message.role; const showTimestamp = !nextMessage || nextMessage.role !== message.role; return ( {shouldShowDateSeparator(message, previousMessage) ? ( ) : null} ); })} ); } function mergeConsecutiveToolOnlyMessages(messages: SuperagentMessage[]) { const merged: SuperagentMessage[] = []; messages.forEach((message) => { const toolCalls = getMessageToolCalls(message); const isToolOnly = message.role === 'assistant' && !message.content?.trim() && toolCalls.length > 0 && getMessageFiles(message).length === 0; const previous = merged[merged.length - 1]; if (isToolOnly && previous && isMergeableToolOnlyMessage(previous)) { const previousToolCalls = getMessageToolCalls(previous); merged[merged.length - 1] = { ...previous, id: previous.id ? `${previous.id}:${message.id ?? toolCalls.length}` : message.id, toolCalls: [...previousToolCalls, ...toolCalls], tool_calls: [...previousToolCalls, ...toolCalls], createdAt: message.createdAt ?? previous.createdAt, created_at: message.created_at ?? previous.created_at, }; return; } merged.push(message); }); return merged; } function isMergeableToolOnlyMessage(message: SuperagentMessage) { return message.role === 'assistant' && !message.content?.trim() && getMessageToolCalls(message).length > 0 && getMessageFiles(message).length === 0; } function DateSeparator({ label }: { label: string }) { return ( {label} ); }