/* Copyright 2026 Marimo. All rights reserved. */ import React from "react"; import { groupNotifications } from "use-acp"; import { ConnectionChangeBlock, ErrorBlock, ReadyToChatBlock, SessionNotificationsBlock, } from "./blocks"; type NotificationEvent = Awaited< ReturnType >[number][number]; interface AgentThreadProps { notifications: NotificationEvent[]; isConnected: boolean; onRetryConnection?: () => void; onRetryLastAction?: () => void; onDismissError?: (errorId: string) => void; } export const AgentThread = ({ notifications, isConnected, onRetryConnection, onRetryLastAction, onDismissError, }: AgentThreadProps) => { let combinedNotifications = groupNotifications(notifications); // Filter out all connection changes unless it is the last one // Filter out available_commands_update combinedNotifications = combinedNotifications.filter((group, index) => { if ( isSessionNotificationGroup(group) && group[0].data.update.sessionUpdate === "available_commands_update" ) { return false; } const isLast = index === combinedNotifications.length - 1; if (isLast) { return true; } if (isConnectionChangeGroup(group)) { return false; } return true; }); const renderNotification = ( group: NotificationEvent[], isLastBlock: boolean, ) => { if (group.length === 0) { return null; } if (isErrorGroup(group)) { const lastError = group[group.length - 1]; return ( onDismissError(lastError.id) : undefined } /> ); } if (isConnectionChangeGroup(group)) { const lastConnectionChange = group[group.length - 1]; return ( ); } if (isSessionNotificationGroup(group)) { const startTimestamp = group[0].timestamp; const endTimestamp = group[group.length - 1].timestamp; const data = group.map((item) => item.data.update); return ( ); } return "Unknown notification type"; }; return (
{combinedNotifications.map((notification, idx) => ( {renderNotification( notification, idx === combinedNotifications.length - 1, )} ))} {combinedNotifications.length === 0 && }
); }; function isErrorGroup( group: NotificationEvent[], ): group is Extract[] { // We only check the first since we know the group is the same type return group[0].type === "error"; } function isConnectionChangeGroup( group: NotificationEvent[], ): group is Extract[] { // We only check the first since we know the group is the same type return group[0].type === "connection_change"; } function isSessionNotificationGroup( group: NotificationEvent[], ): group is Extract[] { // We only check the first since we know the group is the same type return group[0].type === "session_notification"; }