@@ -21,6 +21,49 @@ type ChatMessageHeaderProps = {
2121 sx ?: SxProps < Theme > ;
2222} ;
2323
24+ function sortMessages (
25+ messages : AiService . ChatMessage [ ]
26+ ) : AiService . ChatMessage [ ] {
27+ const timestampsById : Record < string , number > = { } ;
28+ for ( const message of messages ) {
29+ timestampsById [ message . id ] = message . time ;
30+ }
31+
32+ return [ ...messages ] . sort ( ( a , b ) => {
33+ /**
34+ * Use the *origin timestamp* as the primary sort key. This ensures that
35+ * each agent reply is grouped with the human message that triggered it.
36+ *
37+ * - If the message is from an agent, the origin timestamp is the timestamp
38+ * of the message it is replying to.
39+ *
40+ * - Otherwise, the origin timestamp is the *message timestamp*, i.e.
41+ * `message.time` itself.
42+ */
43+
44+ const aOriginTimestamp =
45+ a . type === 'agent' && a . reply_to in timestampsById
46+ ? timestampsById [ a . reply_to ]
47+ : a . time ;
48+ const bOriginTimestamp =
49+ b . type === 'agent' && b . reply_to in timestampsById
50+ ? timestampsById [ b . reply_to ]
51+ : b . time ;
52+
53+ /**
54+ * Use the message timestamp as a secondary sort key. This ensures that each
55+ * agent reply is shown after the human message that triggered it.
56+ */
57+ const aMessageTimestamp = a . time ;
58+ const bMessageTimestamp = b . time ;
59+
60+ return (
61+ aOriginTimestamp - bOriginTimestamp ||
62+ aMessageTimestamp - bMessageTimestamp
63+ ) ;
64+ } ) ;
65+ }
66+
2467export function ChatMessageHeader ( props : ChatMessageHeaderProps ) : JSX . Element {
2568 const collaborators = useCollaboratorsContext ( ) ;
2669
@@ -104,6 +147,9 @@ export function ChatMessageHeader(props: ChatMessageHeaderProps): JSX.Element {
104147
105148export function ChatMessages ( props : ChatMessagesProps ) : JSX . Element {
106149 const [ timestamps , setTimestamps ] = useState < Record < string , string > > ( { } ) ;
150+ const [ sortedMessages , setSortedMessages ] = useState < AiService . ChatMessage [ ] > (
151+ [ ]
152+ ) ;
107153
108154 /**
109155 * Effect: update cached timestamp strings upon receiving a new message.
@@ -129,6 +175,10 @@ export function ChatMessages(props: ChatMessagesProps): JSX.Element {
129175 }
130176 } , [ props . messages ] ) ;
131177
178+ useEffect ( ( ) => {
179+ setSortedMessages ( sortMessages ( props . messages ) ) ;
180+ } , [ props . messages ] ) ;
181+
132182 return (
133183 < Box
134184 sx = { {
@@ -137,15 +187,14 @@ export function ChatMessages(props: ChatMessagesProps): JSX.Element {
137187 }
138188 } }
139189 >
140- { props . messages . map ( ( message , i ) => {
190+ { sortedMessages . map ( message => {
141191 // render selection in HumanChatMessage, if any
142192 const markdownStr =
143193 message . type === 'human' && message . selection
144194 ? message . body + '\n\n```\n' + message . selection . source + '\n```\n'
145195 : message . body ;
146-
147196 return (
148- < Box key = { i } sx = { { padding : 4 } } >
197+ < Box key = { message . id } sx = { { padding : 4 } } >
149198 < ChatMessageHeader
150199 message = { message }
151200 timestamp = { timestamps [ message . id ] }
0 commit comments