fix(server): rebuild agent session when chat/agent mode changes (#1767)#1854
Merged
Dani Akash (DaniAkash) merged 4 commits intoJul 23, 2026
Merged
Conversation
Switching between Chat and Agent mode mid-conversation had no effect. The assistant kept answering that it was in Chat mode and kept refusing browser automation. The client sends the mode correctly on every turn. The problem is server side: an agent's toolset and system prompt are built once, when the session is created, and the session is cached per conversation. ChatService already rebuilds that session when MCP servers or the workspace change mid-conversation, but not when the mode changes. So a conversation that started in Chat mode kept its read-only toolset and its "you are in read-only chat mode" system prompt for the rest of the conversation, and the toggle did nothing. This is not new-tab specific despite the report. The sidepanel behaves the same way if you send a message before switching; the new tab page just makes it easy to hit, since its first turn is usually a plain question. Track chatMode on the session alongside the existing mcpServerKey and workingDir change-detection fields, and rebuild when it changes. chatMode is required rather than optional so the compiler enforces the stamp: an unstamped session would compare undefined !== false and fake a mode change on the first turn. Both directions rebuild. Only chat -> agent was reported and it is the safe one, since agent's toolset is a superset of chat's, but rebuilding only that direction would leave chat -> agent -> chat holding full write tools while the toggle reads chat. Known tradeoff: agent -> chat narrows the toolset, so sanitizeMessagesForToolset drops the agent's record of tool calls it already made and switching back does not restore them. The MCP and workspace rebuilds already behave this way, and it costs the agent context rather than the user's chat history, which is stored client-side. ACP-backed providers are excluded. A rebuild cannot deliver a mode change to them: their instructions live in the workspace instruction file, and ensureWorkspaceInstructionFile() skips with 'skipped-not-new-conversation' whenever isNewConversation is false, which it is on every rebuild since it comes from `!session` evaluated beforehand. Rebuilding would hand the agent a fresh in-band prompt contradicting the stale on-disk block. Mode switching for ACP needs that file refreshed, and is left for a separate change. Fixes browseros-ai#1767. Signed-off-by: ZhYGuoL <zguoliau@andrew.cmu.edu>
Contributor
|
All contributors have signed the CLA. Thank you! |
Contributor
Author
|
I have read the CLA Document and I hereby sign the CLA |
Resolve the chat-service test import conflict: keep beforeEach for the mode-switch tests alongside main's ai import. Source auto-merged; the mode-change rebuild logic applies unchanged against current main (typecheck clean, 15/15 chat-service tests pass, biome clean).
Contributor
Contributor
Greptile SummaryThe PR rebuilds cached non-ACP agent sessions when chat/agent mode changes.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains within the scope of the previous review thread. Important Files Changed
Reviews (2): Last reviewed commit: "refactor(server): replace rebuiltThisTur..." | Re-trigger Greptile |
…conciliation The three mid-conversation change detectors (MCP, workspace, mode) each rebuilt the session in place and relied on a mutable rebuiltThisTurn flag to avoid a second rebuild. Because rebuildSession restamps the session, later detectors read already-mutated state, which is why the mode block had to pre-compute its flag and why the workspace notice was silently dropped when MCP also changed in the same turn. Snapshot the session inputs once, derive mcpChanged/workspaceChanged/ modeChanged from that snapshot, rebuild at most once if any changed, then emit each notice independently. Removes the flag and makes every detector read consistent pre-rebuild state. Extracts the notice strings into pure describeMcpChange/describeWorkspaceChange/describeModeChange helpers with direct unit tests, plus integration tests asserting a single rebuild and all notices when several inputs change together.
Contributor
ACP conversations are always agent mode (read-only chat mode is not enforced for those providers). agentConfig.chatMode was still set from the current request mode, so a rebuild triggered by an unrelated change (an MCP server becoming ready, a workspace switch) adopted a mid-conversation chat toggle and handed the ACP agent a fresh in-band chat-mode prompt that contradicts its on-disk instruction file. The modeChanged exclusion only blocked a mode-triggered rebuild, not this adoption. Pin chatMode to false for ACP providers. Adds tests that an MCP-driven rebuild keeps the ACP session in agent mode, and that an ACP request asking for chat mode still builds in agent mode.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Switching between Chat and Agent mode mid-conversation had no effect. The assistant kept answering that it was in Chat mode and kept refusing browser automation.
The client sends the mode correctly on every turn. The problem is server side: an agent's toolset and system prompt are built once, when the session is created, and the session is cached per conversation.
ChatServicealready rebuilds that session when MCP servers or the workspace change mid-conversation, but not when the mode changes. So a conversation that started in Chat mode kept its read-only toolset and its "you are in read-only chat mode" system prompt (prompt.ts) for the rest of the conversation, and the toggle did nothing.This is not new-tab specific, despite the report. The sidepanel behaves the same way if you send a message before switching. The new tab page just makes it easy to hit, since its first turn is usually a plain question.
What changed
session-store.ts: trackchatModeonAgentSession, alongside the existingmcpServerKeyandworkingDirchange-detection fields. Declared required rather than optional so the compiler enforces the stamp: an unstamped session would compareundefined !== falseand fake a mode change on the first turn.chat-service.ts: rebuild the session whenchatModechanges mid-conversation, matching the existing MCP and workspace rebuild paths.Scope, and why it is drawn here
sanitizeMessagesForToolsetdrops the agent's record of tool calls it already made, and switching back does not restore them. The MCP and workspace rebuilds already behave this way. It costs the agent context, not the user's chat history, which is stored client-side.ensureWorkspaceInstructionFile()skips withskipped-not-new-conversationwheneverisNewConversationis false, which it is on every rebuild since it comes from!sessionevaluated beforehand. Rebuilding would hand the agent a fresh in-band prompt contradicting the stale on-disk block. Mode switching for ACP needs that file refreshed mid-conversation, which seems worth its own change and its own decision. Happy to take direction.How I verified
The regression test fails on
mainbefore the fix:and passes after:
Tests added:
rebuilds the session when the user switches from chat to agent,re-restricts the session when switching back to chat mode,leaves ACP sessions alone on a mode switch,does not rebuild the session when the mode is unchanged.Notes for reviewers
While tracing this I found two adjacent things that are out of scope here but may be worth separate issues:
buildAcpMcpServers/buildBrowserOsSelfMcpEntrysend no mode to/mcp, so that surface stays fully writable. An agent that ignores the instruction can still click and navigate while the user believes they have restricted it.autoAttachActiveTab(chat-actions.hooks.ts) is dead config:useChatActionshas one caller and it never passes the flag, and the sidepanel does not use the hook at all.Fixes #1767.