Duo Agentic Chat processes opening thread at least 2 times

Due to a combination of event handlers and watchers, loading a thread is not optimal in Duo Chat, initiating processing of the whole thread at least two times.

Discovery

The current implementation drives things in two parallel streams (can be read as "race conditions"):

Updates on the thread-selected event

  • while on the history view, user clicks one of the listed threads
  • the web-agentic-duo-chat component emits thread-selected that triggers the onThreadSelected() handler
  • the onThreadSelected handler calls hydrateActiveThread, among other things
  • the hydrateActiveThread calls loadActiveThread. That one, in its turn, fetches all the messages for the selected thread (acriveThread)
  • after the messages are fetched, they are currently stored in chatMessageHistory.

So far, so good.

However, the story doesn't end here. The action of selecting a thread from the history view also triggers another reaction chain.

Updates in the mode watcher

  • while on the history view, the mode prop is set to history
  • when user selects a thread, it also switches the mode to active, which, in its turn…
  • triggers the watcher for the mode prop
  • the watcher calls switchMode
  • the switchMode, when the mode is set to active branches into two options: hydrateActiveThread() (when this.shouldLoadThread === true) again, even though it was done in the algorithm above, or starts a new chat with onNewChat().
  • the only reason, this still works on master is that chatMessageHistory cover that duplicates this.messages in a lot of cases and is about to be removed in the course of Reduced Duo Agentic Chat messages processing (!212409 - merged)

This processing is not only fundamentally wrong, but also prone to producing a real bug where, in case we use state's messages as a SSOT:

  • in the course of reacting to the event (first algorithm) we store messages in the store
  • that updates the shouldLoadThread computed prop to return true (because of !this.messages?.length check)
  • that leads to switchMode picking the onNewChat, instead of hydrateActiveThread() in the second algorithm (triggered in the watcher), that wipes off all the messages and leaves the opened thread without any messages.
Edited by Denys Mishunov