@@ -208,6 +208,7 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
210 case pubsub.Event[message.Message]:
210 case pubsub.Event[message.Message]:
211+ // TODO: handle nested messages for agentic tools
211 if m.session == nil || msg.Payload.SessionID != m.session.ID {
212 if m.session == nil || msg.Payload.SessionID != m.session.ID {
@@ -217,8 +218,6 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
217 case pubsub.UpdatedEvent:
218 case pubsub.UpdatedEvent:
218 cmds = append(cmds, m.updateSessionMessage(msg.Payload))
219 cmds = append(cmds, m.updateSessionMessage(msg.Payload))
220− // TODO: Finish implementing me
221− // cmds = append(cmds, m.setMessageEvents(msg.Payload))
222 case pubsub.Event[history.File]:
221 case pubsub.Event[history.File]:
223 cmds = append(cmds, m.handleFileEvent(msg.Payload))
222 cmds = append(cmds, m.handleFileEvent(msg.Payload))
224 case pubsub.Event[app.LSPEvent]:
223 case pubsub.Event[app.LSPEvent]:
@@ -403,35 +402,81 @@ func (m *UI) setSessionMessages(msgs []message.Message) tea.Cmd {
405// appendSessionMessage appends a new message to the current session in the chat
404// appendSessionMessage appends a new message to the current session in the chat
405+// if the message is a tool result it will update the corresponding tool call message
406func (m *UI) appendSessionMessage(msg message.Message) tea.Cmd {
406func (m *UI) appendSessionMessage(msg message.Message) tea.Cmd {
407− items := chat.GetMessageItems(m.com.Styles, &msg, nil)
408 var cmds []tea.Cmd
407 var cmds []tea.Cmd
409+ case message.User, message.Assistant:
410+ items := chat.GetMessageItems(m.com.Styles, &msg, nil)
409− for _, item := range items {
411+ for _, item := range items {
410− if animatable, ok := item.(chat.Animatable); ok {
412+ if animatable, ok := item.(chat.Animatable); ok {
411− if cmd := animatable.StartAnimation(); cmd != nil {
413+ if cmd := animatable.StartAnimation(); cmd != nil {
414+ cmds = append(cmds, cmd)
418+ m.chat.AppendMessages(items...)
419+ if cmd := m.chat.ScrollToBottom(); cmd != nil {
412− cmds = append(cmds, cmd)
420+ cmds = append(cmds, cmd)
423+ for _, tr := range msg.ToolResults() {
424+ toolItem := m.chat.GetMessageItem(tr.ToolCallID)
425+ if toolItem == nil {
426+ // we should have an item!
429+ if toolMsgItem, ok := toolItem.(*chat.ToolMessageItem); ok {
430+ toolMsgItem.SetResult(&tr)
416− m.chat.AppendMessages(items...)
417− if cmd := m.chat.ScrollToBottom(); cmd != nil {
418− cmds = append(cmds, cmd)
420 return tea.Batch(cmds...)
434 return tea.Batch(cmds...)
423// updateSessionMessage updates an existing message in the current session in the chat
437// updateSessionMessage updates an existing message in the current session in the chat
424−// INFO: currently only updates the assistant when I add tools this will get a bit more complex
438+// when an assistant message is updated it may include updated tool calls as well
439+// that is why we need to handle creating/updating each tool call message too
425func (m *UI) updateSessionMessage(msg message.Message) tea.Cmd {
440func (m *UI) updateSessionMessage(msg message.Message) tea.Cmd {
426 existingItem := m.chat.GetMessageItem(msg.ID)
442 existingItem := m.chat.GetMessageItem(msg.ID)
428− case message.Assistant:
443+ if existingItem == nil || msg.Role != message.Assistant {
429− if assistantItem, ok := existingItem.(*chat.AssistantMessageItem); ok {
447+ if assistantItem, ok := existingItem.(*chat.AssistantMessageItem); ok {
430− assistantItem.SetMessage(&msg)
448+ assistantItem.SetMessage(&msg)
451+ var items []chat.MessageItem
452+ for _, tc := range msg.ToolCalls() {
453+ existingToolItem := m.chat.GetMessageItem(tc.ID)
454+ if toolItem, ok := existingToolItem.(*chat.ToolMessageItem); ok {
455+ existingToolCall := toolItem.ToolCall()
456+ // only update if finished state changed or input changed
457+ // to avoid clearing the cache
458+ if (tc.Finished && !existingToolCall.Finished) || tc.Input != existingToolCall.Input {
459+ toolItem.SetToolCall(tc)
462+ if existingToolItem == nil {
463+ items = append(items, chat.GetToolMessageItem(m.com.Styles, tc, nil, false))
467+ for _, item := range items {
468+ if animatable, ok := item.(chat.Animatable); ok {
469+ if cmd := animatable.StartAnimation(); cmd != nil {
470+ cmds = append(cmds, cmd)
474+ m.chat.AppendMessages(items...)
475+ if cmd := m.chat.ScrollToBottom(); cmd != nil {
476+ cmds = append(cmds, cmd)
434− return nil
479+ return tea.Batch(cmds...)
437func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) tea.Cmd {
482func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) tea.Cmd {
@@ -498,6 +543,13 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) tea.Cmd {
498 case dialog.SwitchSessionsMsg:
543 case dialog.SwitchSessionsMsg:
499 cmds = append(cmds, m.listSessions)
544 cmds = append(cmds, m.listSessions)
500 m.dialog.CloseDialog(dialog.CommandsID)
545 m.dialog.CloseDialog(dialog.CommandsID)
546+ case dialog.NewSessionsMsg:
547+ if m.com.App.AgentCoordinator != nil && m.com.App.AgentCoordinator.IsBusy() {
548+ cmds = append(cmds, uiutil.ReportWarn("Agent is busy, please wait before starting a new session..."))
552+ m.dialog.CloseDialog(dialog.CommandsID)
501 case dialog.CompactMsg:
553 case dialog.CompactMsg:
502 err := m.com.App.AgentCoordinator.Summarize(context.Background(), msg.SessionID)
554 err := m.com.App.AgentCoordinator.Summarize(context.Background(), msg.SessionID)
503 if err != nil {
555 if err != nil {
@@ -548,6 +600,15 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) tea.Cmd {
548 m.randomizePlaceholders()
600 m.randomizePlaceholders()
550 return m.sendMessage(value, attachments)
602 return m.sendMessage(value, attachments)
603+ case key.Matches(msg, m.keyMap.Chat.NewSession):
604+ if m.session == nil || m.session.ID == "" {
607+ if m.com.App.AgentCoordinator != nil && m.com.App.AgentCoordinator.IsBusy() {
608+ cmds = append(cmds, uiutil.ReportWarn("Agent is busy, please wait before starting a new session..."))
551 case key.Matches(msg, m.keyMap.Tab):
612 case key.Matches(msg, m.keyMap.Tab):
552 m.focus = uiFocusMain
613 m.focus = uiFocusMain
553 m.textarea.Blur()
614 m.textarea.Blur()
@@ -1362,6 +1423,22 @@ func (m *UI) listSessions() tea.Msg {
1362 return listSessionsMsg{sessions: allSessions}
1423 return listSessionsMsg{sessions: allSessions}
1426+// newSession clears the current session state and prepares for a new session.
1427+// The actual session creation happens when the user sends their first message.
1428+func (m *UI) newSession() {
1429+ if m.session == nil || m.session.ID == "" {
1434+ m.sessionFiles = nil
1435+ m.state = uiLanding
1436+ m.focus = uiFocusEditor
1439+ m.chat.ClearMessages()
1365// handlePasteMsg handles a paste message.
1442// handlePasteMsg handles a paste message.
1366func (m *UI) handlePasteMsg(msg tea.PasteMsg) tea.Cmd {
1443func (m *UI) handlePasteMsg(msg tea.PasteMsg) tea.Cmd {
1367 if m.focus != uiFocusEditor {
1444 if m.focus != uiFocusEditor {