Repository navigation
Description
📦 Deployment Method
Other (run from source: yarn install, next dev)
📌 Version
v2.16.1 (main at 1d3dc33)
💻 Operating System
macOS
📌 System Version
macOS 26.2
🌐 Browser
Other
📌 Browser Version
Chromium 153.0.8010.12 (Playwright, headless)
🐛 Bug Description
When a reply in chat A finishes while chat B is open, NextChat builds chat A's new memory prompt (the long-term summary shown as "Memory Prompt" in the chat's settings) from chat B's memory instead of chat A's own. Chat A's earlier summary is dropped, and from then on chat A sends the model a summary that was built from the other chat.
It needs a chat that is long enough to be summarized: with the defaults ("Send Memory" on, "History Compression Threshold" 1000), that is once more than 1000 tokens have accumulated since the last summary. One way to hit it is to send a message in such a chat and open another chat while the answer streams. If the chat you open has no memory prompt (a new chat, for example), chat A's earlier summary is simply lost.
📷 Recurrence Steps
- Default settings, OpenAI provider.
- In chat A, have an exchange long enough to be summarized. Chat A's settings show its "Memory Prompt".
- Start chat B and do the same, so chat B has its own memory prompt.
- Go back to chat A and ask something with a long answer. While the answer is still streaming, click chat B in the sidebar.
- Wait for the answer to finish and go back to chat A. Its memory prompt (shown in the chat's settings) is now a summary of chat B's memory plus chat A's last exchange, and chat A's first exchange is gone from it. The next question in chat A sends this summary to the model.
🚦 Expected Behavior
A chat's summary is built from that chat's own earlier memory, whichever chat is open when its reply finishes.
📝 Additional Information
How it was checked: Playwright against a local NextChat (next dev at 1d3dc33), with BASE_URL pointing at a local stub of the OpenAI API that logs every request. Each chat message carries a tag ([chat-A], [chat-B]), and each stub summary names the tags and earlier summaries it was given, so the stub's log shows which memory each summary request carried. The steps were as above: chat A and chat B have one long exchange each, then a second question in chat A whose answer streams for about 4 s.
- Clicking chat B 700 ms after chat A's answer started streaming: in 10 of 10 runs, chat A's summary request carried chat B's memory and not chat A's own. Chat A's memory prompt became that summary (built from chat B's memory plus chat A's last exchange), and chat A's next question sent it to the model.
- Control, the same steps but clicking chat B only after chat A's answer and its summary were done: 0 of 10.
Root cause: summarizeSession(refreshTitle, targetSession) summarizes targetSession, but it gets the memory prompt from get().getMemoryPrompt() (app/store/chat.ts:745). That function reads get().currentSession() (app/store/chat.ts:531), which is the chat on screen. summarizeSession runs from onFinish → onNewMessage(botMessage, session) (app/store/chat.ts:478, 404) when the reply ends, so the chat on screen is whichever one the user has opened by then. getMessagesWithMemory makes the same call after await isMcpEnabled() (app/store/chat.ts:591), so a chat switch during that await could do the same; that path was not measured.
AI assistance: Claude (Claude Code) investigated this, reproduced it with the measurements above, wrote a fix and a test, and drafted this report. I have not reproduced it by hand or reviewed the fix myself yet.
Activity