Skip to content

sessions: fix model picker desyncing from the session - #330560

Merged
roblourens merged 2 commits into
mainfrom
roblou/agents/model-picker-sync-issue-investigation
Aug 13, 2026
Merged

sessions: fix model picker desyncing from the session#330560
roblourens merged 2 commits into
mainfrom
roblou/agents/model-picker-sync-issue-investigation

Conversation

@roblourens

Copy link
Copy Markdown
Member

Reverts the ChatView session-type delegate wiring from #329889, which desynchronized the model picker from the viewed session. Resuming an Opus 5 session would show — and then persist — gpt-5.6-sol.

Root cause

sessionTypePickerDelegate means "the user picks the session type here". It exists for the welcome view and the automations dialog, which have no chat model of their own. ChatInputPart listens to that delegate's onDidChangeActiveSessionProvider and reconciles the model and mode for the newly picked type:

this.checkModeInSessionPool(newSessionType);
this.revalidateModelForSessionType();   // "Reconcile the current model after an explicit session-type pick"

ChatView's delegate instead fired on every chat navigation, from _loadChat and before the widget rebinds:

this._currentChatResource = resource;
this._sessionTypeDelegate.setChatResource(resource);  // fires synchronously
...
if (previousChatResource) { this._clearCurrentChat(); }   // widget.setModel(undefined) — AFTER

So each navigation re-applied the remembered per-session-type model while the input was still bound to the outgoing chat. _applyModelruntime.applyModel_syncInputStateToModel() therefore wrote that model into the outgoing chat's persisted input state.

The corruption was silent while the remembered model happened to match, and became visible the moment an explicit pick made them differ.

Evidence

From an Agents window log bundle, [ChatModelSelection] diagnostics show the exact sequence. Note every affected switch was between chats of the same type (agent-host-copilotcliagent-host-copilotcli), where a session-type announcement is meaningless:

12:04:56.978  explicit-selection      conv=d43abfdb  model=gpt-5.6-sol      ← user picks
12:04:59.432  conversation-restore    conv=767d6151  desired=claude-opus-5  action=apply   ✅
12:05:02.325  initialize              conv=767d6151  result=gpt-5.6-sol  reason=remembered ❌ clobber
12:05:10.288  conversation-restore    conv=42ca61ac  desired=claude-opus-5  action=apply   ✅
12:05:12.784  initialize              conv=42ca61ac  result=gpt-5.6-sol  reason=remembered ❌ clobber
12:05:18+     initialize ... (no conversation-restore at all — state is now persisted wrong)

Confirmed by replaying the production call sequence against the real ChatInputModelSelectionController: restoring a conversation to Opus 5, then revalidateForSessionType(() => initialize('gpt-5.6-sol')) while bound to the outgoing conversation flips both the picker and the outgoing conversation's stored model to gpt-5.6-sol.

What changed

  • Removed ChatViewSessionTypeDelegate / getChatViewSessionType and stopped passing sessionTypePickerDelegate to the Agents-window ChatWidget.
  • Restored _updateWidgetLockState (and the IChatSessionsService injection it needs) so the widget still locks to the contributed chat session type when the model loads.
  • Removed the two tests covering the deleted delegate.
  • SESSIONS.md records why the delegate must not be reintroduced here.

The two incidental hunks from #329889 are left in place: getVisibleOptionGroupsModeAndUpdateContextKeys using getEffectiveSessionType (now falls back to the session resource for the Agents window, i.e. the pre-#329889 behavior) and the target picker's label re-render on delegate change (inert without a ChatView delegate, still correct for the welcome view and automations dialog).

Reviewer notes

  • ⚠️ This brings back the picker blink on session switch that sessions: Keep chat pickers stable while loading #329889 was fixing. That needs a solution which does not route through the explicit-pick pathway — e.g. a display-only notification that updates context keys / lock state / picker rendering without touching model reconciliation.
  • ⚠️ Already-corrupted sessions stay corrupted. Sessions whose input state was rewritten keep the wrong model persisted; this stops new damage but does not repair existing state.
  • git diff 0b8b8c6f3f5^ -- chatView.ts filtered for delegate/lock-state/session-type shows zero differences, so the file's behavior matches the pre-regression version exactly.

Validation

  • npm run typecheck-client — clean
  • npm run hygiene — clean
  • eslint on changed files — clean
  • 56 tests passing: Sessions - Chat View, ChatInputModelSelectionController, SessionTypePickerActionItem

(Written by Copilot)

Reverts the ChatView session-type delegate wiring from #329889, which
desynchronized the model picker from the viewed session.

`sessionTypePickerDelegate` means "the user picks the session type here" —
it exists for the welcome view and the automations dialog, which have no
chat model of their own. ChatInputPart listens to that delegate's
`onDidChangeActiveSessionProvider` and reconciles the model and mode for
the newly picked type via `revalidateModelForSessionType()`.

ChatView's delegate instead fired on every chat navigation, from
`_loadChat` and before the widget rebinds. So each navigation re-applied
the remembered per-session-type model while the input was still bound to
the *outgoing* chat, writing that model into the outgoing chat's
persisted input state. The corruption was silent until an explicit model
pick made the remembered model differ from the session's, after which
opening an Opus 5 session showed (and persisted) gpt-5.6-sol.

Restores `_updateWidgetLockState` so the widget still locks to the
contributed chat session type when the model loads.

Note this brings back the picker blink on session switch that #329889
was fixing; that needs a solution which does not route through the
explicit-pick pathway. SESSIONS.md records why the delegate must not be
reintroduced here.

(Written by Copilot)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings August 12, 2026 22:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Reverts incorrect session-type delegation that could overwrite persisted chat model selections during navigation.

Changes:

  • Removes ChatView session-type delegate wiring.
  • Restores model-derived widget lock state.
  • Updates documentation and removes obsolete delegate tests.
Show a summary per file
File Description
src/vs/sessions/SESSIONS.md Documents why ChatView must not use the picker delegate.
src/vs/sessions/contrib/chat/browser/chatView.ts Removes delegate wiring and restores widget locking.
src/vs/sessions/contrib/chat/test/browser/chatView.test.ts Removes tests for the deleted delegate.

Review details

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread src/vs/sessions/contrib/chat/browser/chatView.ts
…icker-sync-issue-investigation

# Conflicts:
#	src/vs/sessions/SESSIONS.md
@roblourens
roblourens marked this pull request as ready for review August 12, 2026 23:32
@roblourens
roblourens enabled auto-merge (squash) August 12, 2026 23:32
@roblourens
roblourens merged commit c792848 into main Aug 13, 2026
27 checks passed
@roblourens
roblourens deleted the roblou/agents/model-picker-sync-issue-investigation branch August 13, 2026 01:49
@vs-code-engineering vs-code-engineering Bot added this to the 1.134.0 milestone Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants