@@ -119,11 +119,11 @@ func (m *messageListCmp) handleMessageEvent(event pubsub.Event[message.Message])
119 m.handleChildSession(event)
119 m.handleChildSession(event)
120 return nil
120 return nil
123 if m.messageExists(event.Payload.ID) {
123 if m.messageExists(event.Payload.ID) {
124 return nil
124 return nil
127 return m.handleNewMessage(event.Payload)
127 return m.handleNewMessage(event.Payload)
128 case pubsub.UpdatedEvent:
128 case pubsub.UpdatedEvent:
129 return m.handleUpdateAssistantMessage(event.Payload)
129 return m.handleUpdateAssistantMessage(event.Payload)
@@ -192,22 +192,22 @@ func (m *messageListCmp) findToolCallByID(items []util.Model, toolCallID string)
192func (m *messageListCmp) handleUpdateAssistantMessage(msg message.Message) tea.Cmd {
192func (m *messageListCmp) handleUpdateAssistantMessage(msg message.Message) tea.Cmd {
193 var cmds []tea.Cmd
193 var cmds []tea.Cmd
194 items := m.listCmp.Items()
194 items := m.listCmp.Items()
196 // Find existing assistant message and tool calls for this message
196 // Find existing assistant message and tool calls for this message
197 assistantIndex, existingToolCalls := m.findAssistantMessageAndToolCalls(items, msg.ID)
197 assistantIndex, existingToolCalls := m.findAssistantMessageAndToolCalls(items, msg.ID)
199 logging.Info("Update Assistant Message", "msg", msg, "assistantMessageInx", assistantIndex, "toolCalls", existingToolCalls)
199 logging.Info("Update Assistant Message", "msg", msg, "assistantMessageInx", assistantIndex, "toolCalls", existingToolCalls)
201 // Handle assistant message content
201 // Handle assistant message content
202 if cmd := m.updateAssistantMessageContent(msg, assistantIndex); cmd != nil {
202 if cmd := m.updateAssistantMessageContent(msg, assistantIndex); cmd != nil {
203 cmds = append(cmds, cmd)
203 cmds = append(cmds, cmd)
206 // Handle tool calls
206 // Handle tool calls
207 if cmd := m.updateToolCalls(msg, existingToolCalls); cmd != nil {
207 if cmd := m.updateToolCalls(msg, existingToolCalls); cmd != nil {
208 cmds = append(cmds, cmd)
208 cmds = append(cmds, cmd)
211 return tea.Batch(cmds...)
211 return tea.Batch(cmds...)
@@ -215,7 +215,7 @@ func (m *messageListCmp) handleUpdateAssistantMessage(msg message.Message) tea.C
215func (m *messageListCmp) findAssistantMessageAndToolCalls(items []util.Model, messageID string) (int, map[int]messages.ToolCallCmp) {
215func (m *messageListCmp) findAssistantMessageAndToolCalls(items []util.Model, messageID string) (int, map[int]messages.ToolCallCmp) {
216 assistantIndex := NotFound
216 assistantIndex := NotFound
217 toolCalls := make(map[int]messages.ToolCallCmp)
217 toolCalls := make(map[int]messages.ToolCallCmp)
219 // Search backwards as messages are more likely to be at the end
219 // Search backwards as messages are more likely to be at the end
220 for i := len(items) - 1; i >= 0; i-- {
220 for i := len(items) - 1; i >= 0; i-- {
221 item := items[i]
221 item := items[i]
@@ -229,7 +229,7 @@ func (m *messageListCmp) findAssistantMessageAndToolCalls(items []util.Model, me
233 return assistantIndex, toolCalls
233 return assistantIndex, toolCalls
@@ -238,10 +238,10 @@ func (m *messageListCmp) updateAssistantMessageContent(msg message.Message, assi
238 if assistantIndex == NotFound {
238 if assistantIndex == NotFound {
239 return nil
239 return nil
242 shouldShowMessage := m.shouldShowAssistantMessage(msg)
242 shouldShowMessage := m.shouldShowAssistantMessage(msg)
243 hasToolCallsOnly := len(msg.ToolCalls()) > 0 && msg.Content().Text == ""
243 hasToolCallsOnly := len(msg.ToolCalls()) > 0 && msg.Content().Text == ""
245 if shouldShowMessage {
245 if shouldShowMessage {
246 m.listCmp.UpdateItem(
246 m.listCmp.UpdateItem(
247 assistantIndex,
247 assistantIndex,
@@ -253,7 +253,7 @@ func (m *messageListCmp) updateAssistantMessageContent(msg message.Message, assi
253 } else if hasToolCallsOnly {
253 } else if hasToolCallsOnly {
254 m.listCmp.DeleteItem(assistantIndex)
254 m.listCmp.DeleteItem(assistantIndex)
257 return nil
257 return nil
@@ -265,13 +265,13 @@ func (m *messageListCmp) shouldShowAssistantMessage(msg message.Message) bool {
265// updateToolCalls handles updates to tool calls, updating existing ones and adding new ones.
265// updateToolCalls handles updates to tool calls, updating existing ones and adding new ones.
266func (m *messageListCmp) updateToolCalls(msg message.Message, existingToolCalls map[int]messages.ToolCallCmp) tea.Cmd {
266func (m *messageListCmp) updateToolCalls(msg message.Message, existingToolCalls map[int]messages.ToolCallCmp) tea.Cmd {
267 var cmds []tea.Cmd
267 var cmds []tea.Cmd
269 for _, tc := range msg.ToolCalls() {
269 for _, tc := range msg.ToolCalls() {
270 if cmd := m.updateOrAddToolCall(tc, existingToolCalls, msg.ID); cmd != nil {
270 if cmd := m.updateOrAddToolCall(tc, existingToolCalls, msg.ID); cmd != nil {
271 cmds = append(cmds, cmd)
271 cmds = append(cmds, cmd)
275 return tea.Batch(cmds...)
275 return tea.Batch(cmds...)
@@ -285,7 +285,7 @@ func (m *messageListCmp) updateOrAddToolCall(tc message.ToolCall, existingToolCa
285 return nil
285 return nil
289 // Add new tool call if not found
289 // Add new tool call if not found
290 return m.listCmp.AppendItem(messages.NewToolCallCmp(messageID, tc))
290 return m.listCmp.AppendItem(messages.NewToolCallCmp(messageID, tc))
@@ -293,7 +293,7 @@ func (m *messageListCmp) updateOrAddToolCall(tc message.ToolCall, existingToolCa
293// handleNewAssistantMessage processes new assistant messages and their tool calls.
293// handleNewAssistantMessage processes new assistant messages and their tool calls.
294func (m *messageListCmp) handleNewAssistantMessage(msg message.Message) tea.Cmd {
294func (m *messageListCmp) handleNewAssistantMessage(msg message.Message) tea.Cmd {
295 var cmds []tea.Cmd
295 var cmds []tea.Cmd
297 // Add assistant message if it should be displayed
297 // Add assistant message if it should be displayed
298 if m.shouldShowAssistantMessage(msg) {
298 if m.shouldShowAssistantMessage(msg) {
299 cmd := m.listCmp.AppendItem(
299 cmd := m.listCmp.AppendItem(
@@ -304,13 +304,13 @@ func (m *messageListCmp) handleNewAssistantMessage(msg message.Message) tea.Cmd
305 cmds = append(cmds, cmd)
305 cmds = append(cmds, cmd)
308 // Add tool calls
308 // Add tool calls
309 for _, tc := range msg.ToolCalls() {
309 for _, tc := range msg.ToolCalls() {
310 cmd := m.listCmp.AppendItem(messages.NewToolCallCmp(msg.ID, tc))
310 cmd := m.listCmp.AppendItem(messages.NewToolCallCmp(msg.ID, tc))
311 cmds = append(cmds, cmd)
311 cmds = append(cmds, cmd)
314 return tea.Batch(cmds...)
314 return tea.Batch(cmds...)
@@ -319,26 +319,26 @@ func (m *messageListCmp) SetSession(session session.Session) tea.Cmd {
319 if m.session.ID == session.ID {
319 if m.session.ID == session.ID {
320 return nil
320 return nil
323 m.session = session
323 m.session = session
324 sessionMessages, err := m.app.Messages.List(context.Background(), session.ID)
324 sessionMessages, err := m.app.Messages.List(context.Background(), session.ID)
325 if err != nil {
325 if err != nil {
326 return util.ReportError(err)
326 return util.ReportError(err)
329 if len(sessionMessages) == 0 {
329 if len(sessionMessages) == 0 {
330 return m.listCmp.SetItems([]util.Model{})
330 return m.listCmp.SetItems([]util.Model{})
333 // Initialize with first message timestamp
333 // Initialize with first message timestamp
334 m.lastUserMessageTime = sessionMessages[0].CreatedAt
334 m.lastUserMessageTime = sessionMessages[0].CreatedAt
336 // Build tool result map for efficient lookup
336 // Build tool result map for efficient lookup
337 toolResultMap := m.buildToolResultMap(sessionMessages)
337 toolResultMap := m.buildToolResultMap(sessionMessages)
339 // Convert messages to UI components
339 // Convert messages to UI components
340 uiMessages := m.convertMessagesToUI(sessionMessages, toolResultMap)
340 uiMessages := m.convertMessagesToUI(sessionMessages, toolResultMap)
342 return m.listCmp.SetItems(uiMessages)
342 return m.listCmp.SetItems(uiMessages)
@@ -356,7 +356,7 @@ func (m *messageListCmp) buildToolResultMap(messages []message.Message) map[stri
356// convertMessagesToUI converts database messages to UI components.
356// convertMessagesToUI converts database messages to UI components.
357func (m *messageListCmp) convertMessagesToUI(sessionMessages []message.Message, toolResultMap map[string]message.ToolResult) []util.Model {
357func (m *messageListCmp) convertMessagesToUI(sessionMessages []message.Message, toolResultMap map[string]message.ToolResult) []util.Model {
358 uiMessages := make([]util.Model, 0)
358 uiMessages := make([]util.Model, 0)
360 for _, msg := range sessionMessages {
360 for _, msg := range sessionMessages {
361 switch msg.Role {
361 switch msg.Role {
362 case message.User:
362 case message.User:
@@ -366,14 +366,14 @@ func (m *messageListCmp) convertMessagesToUI(sessionMessages []message.Message,
366 uiMessages = append(uiMessages, m.convertAssistantMessage(msg, toolResultMap)...)
366 uiMessages = append(uiMessages, m.convertAssistantMessage(msg, toolResultMap)...)
370 return uiMessages
370 return uiMessages
373// convertAssistantMessage converts an assistant message and its tool calls to UI components.
373// convertAssistantMessage converts an assistant message and its tool calls to UI components.
374func (m *messageListCmp) convertAssistantMessage(msg message.Message, toolResultMap map[string]message.ToolResult) []util.Model {
374func (m *messageListCmp) convertAssistantMessage(msg message.Message, toolResultMap map[string]message.ToolResult) []util.Model {
375 var uiMessages []util.Model
375 var uiMessages []util.Model
377 // Add assistant message if it should be displayed
377 // Add assistant message if it should be displayed
378 if m.shouldShowAssistantMessage(msg) {
378 if m.shouldShowAssistantMessage(msg) {
379 uiMessages = append(
379 uiMessages = append(
@@ -384,30 +384,30 @@ func (m *messageListCmp) convertAssistantMessage(msg message.Message, toolResult
388 // Add tool calls with their results and status
388 // Add tool calls with their results and status
389 for _, tc := range msg.ToolCalls() {
389 for _, tc := range msg.ToolCalls() {
390 options := m.buildToolCallOptions(tc, msg, toolResultMap)
390 options := m.buildToolCallOptions(tc, msg, toolResultMap)
391 uiMessages = append(uiMessages, messages.NewToolCallCmp(msg.ID, tc, options...))
391 uiMessages = append(uiMessages, messages.NewToolCallCmp(msg.ID, tc, options...))
394 return uiMessages
394 return uiMessages
397// buildToolCallOptions creates options for tool call components based on results and status.
397// buildToolCallOptions creates options for tool call components based on results and status.
398func (m *messageListCmp) buildToolCallOptions(tc message.ToolCall, msg message.Message, toolResultMap map[string]message.ToolResult) []messages.ToolCallOption {
398func (m *messageListCmp) buildToolCallOptions(tc message.ToolCall, msg message.Message, toolResultMap map[string]message.ToolResult) []messages.ToolCallOption {
399 var options []messages.ToolCallOption
399 var options []messages.ToolCallOption
401 // Add tool result if available
401 // Add tool result if available
402 if tr, ok := toolResultMap[tc.ID]; ok {
402 if tr, ok := toolResultMap[tc.ID]; ok {
403 options = append(options, messages.WithToolCallResult(tr))
403 options = append(options, messages.WithToolCallResult(tr))
406 // Add cancelled status if applicable
406 // Add cancelled status if applicable
407 if msg.FinishPart() != nil && msg.FinishPart().Reason == message.FinishReasonCanceled {
407 if msg.FinishPart() != nil && msg.FinishPart().Reason == message.FinishReasonCanceled {
408 options = append(options, messages.WithToolCallCancelled())
408 options = append(options, messages.WithToolCallCancelled())
411 return options
411 return options