Skip to content

Mode picker in Sessions app#298199

Draft
sandy081 wants to merge 9 commits intomainfrom
copilot-worktree-2026-02-26T22-26-52
Draft

Mode picker in Sessions app#298199
sandy081 wants to merge 9 commits intomainfrom
copilot-worktree-2026-02-26T22-26-52

Conversation

@sandy081
Copy link
Member

Mode picker in Sessions app

@sandy081 sandy081 self-assigned this Feb 27, 2026
Copilot AI review requested due to automatic review settings February 27, 2026 08:55
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a mode picker to the Sessions app, allowing users to select between the default "Agent" mode and any custom agents when starting a new local/Background session. The mode picker filters custom agents based on the session type's target and integrates with the existing chat mode infrastructure.

Changes:

  • Adds a new ModePicker widget for selecting chat modes in the Sessions app
  • Extends INewSession interface to track the selected mode via modeId property
  • Updates SessionsManagementService to resolve mode information and pass it to chat requests

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

File Description
src/vs/sessions/contrib/chat/browser/modePicker.ts New self-contained mode picker widget that displays Agent and filtered custom modes for local sessions
src/vs/sessions/contrib/chat/browser/newChatViewPane.ts Integrates ModePicker into the new chat widget, shows/hides based on session type, syncs mode changes to session
src/vs/sessions/contrib/chat/browser/newSession.ts Adds modeId property to INewSession interface with implementations in LocalNewSession (stores) and RemoteNewSession (no-op)
src/vs/sessions/contrib/sessions/browser/sessionsManagementService.ts Resolves mode from session's modeId, constructs modeInstructions with tool references, sets mode on input model for UI consistency
Comments suppressed due to low confidence (1)

src/vs/sessions/contrib/sessions/browser/sessionsManagementService.ts:309

  • Non-null assertion operator (!) is used on resolvedMode at line 309 without a null check. While rawModeInstructions being truthy implies resolvedMode exists, this assumption is fragile. If the conditional logic changes, this could cause a runtime error. Consider using optional chaining instead: resolvedMode?.name.get().
			name: resolvedMode!.name.get(),

Comment on lines +206 to +221
private _updateTriggerLabel(): void {
if (!this._triggerElement) {
return;
}

dom.clearNode(this._triggerElement);

const icon = this._selectedMode.icon.get();
if (icon) {
dom.append(this._triggerElement, renderIcon(icon));
}

const labelSpan = dom.append(this._triggerElement, dom.$('span.sessions-chat-dropdown-label'));
labelSpan.textContent = this._selectedMode.label.get();
dom.append(this._triggerElement, renderIcon(Codicon.chevronDown));
}
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The ModePicker does not visually indicate when no custom modes are available, unlike the CloudModelPicker which adds a 'disabled' class (see modelPicker.ts line 202). While the picker will show only the default Agent mode, it would be better UX to add visual feedback when the picker is effectively showing only one option. Consider adding disabled styling when modes.length is 1.

Copilot uses AI. Check for mistakes.
Comment on lines +128 to +130
if (modes.length === 0) {
return;
}
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The early return when modes.length is 0 prevents the picker from being shown, but this condition should never be true since _getAvailableModes always includes at least ChatMode.Agent (line 109). This check is defensive but may hide bugs where the default Agent mode is unexpectedly missing. Consider removing this check or adding logging to detect if this condition ever occurs.

Suggested change
if (modes.length === 0) {
return;
}

Copilot uses AI. Check for mistakes.
}

setModeId(_modeId: string | undefined): void {
// No-op for remote sessions
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The setModeId method is a no-op for RemoteNewSession (line 241-243), but there's no documentation explaining why remote sessions don't support mode selection. Consider adding a comment explaining the reasoning, such as whether mode selection is not supported by the remote session provider or if modes are determined server-side.

Suggested change
// No-op for remote sessions
// Intentionally a no-op: remote sessions do not support client-side mode selection.
// Any mode or behavior differences are determined by the remote session provider/server.

Copilot uses AI. Check for mistakes.

const trigger = dom.append(slot, dom.$('a.action-label'));
trigger.tabIndex = 0;
trigger.role = 'button';
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The trigger element has tabIndex and role set for keyboard accessibility (lines 73-74), but there's no aria-label or aria-describedby attribute to provide context for screen reader users. Consider adding an aria-label such as "Select chat mode" to improve accessibility, similar to how other pickers in the codebase provide accessible labels.

Suggested change
trigger.role = 'button';
trigger.role = 'button';
trigger.setAttribute('aria-label', localize('sessions.modePicker.ariaLabel', "Select chat mode"));

Copilot uses AI. Check for mistakes.
@IChatSessionsService private readonly chatSessionsService: IChatSessionsService,
@ICommandService private readonly commandService: ICommandService,
) {
super();
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The ModePicker does not listen to changes in available modes. If custom agents are added, removed, or modified after the picker is created, the picker will not update its list of available modes until the user reopens the picker. Consider adding a listener to chatModeService.onDidChangeChatModes to refresh the available modes dynamically.

Suggested change
super();
super();
this._register(this.chatModeService.onDidChangeChatModes(() => {
// Refresh the trigger label when available chat modes change
if (this._triggerElement) {
this._updateTriggerLabel();
}
}));

Copilot uses AI. Check for mistakes.
Comment on lines +358 to +365
// Set the selected mode on the input model so the mode picker reflects it
if (session.modeId) {
const resolvedMode = this.chatModeService.findModeById(session.modeId);
if (resolvedMode) {
model.inputModel.setState({
mode: { id: resolvedMode.id, kind: resolvedMode.kind }
});
}
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The mode resolution logic is duplicated: it's first resolved at lines 302-313 to build sendOptions, and then resolved again at lines 360-365 to set the input model state. This could lead to inconsistencies if the mode is deleted between the two calls. Consider consolidating this logic by reusing the resolvedMode variable from the first resolution, or move the input model state setting earlier to avoid redundant lookups.

This issue also appears on line 309 of the same file.

Copilot uses AI. Check for mistakes.
Comment on lines +103 to +120
private _getAvailableModes(): IChatMode[] {
const customAgentTarget = this.chatSessionsService.getCustomAgentTargetForSessionType(AgentSessionProviders.Background);
const effectiveTarget = customAgentTarget && customAgentTarget !== Target.Undefined ? customAgentTarget : Target.GitHubCopilot;
const modes = this.chatModeService.getModes();

// Always include the default Agent mode
const result: IChatMode[] = [ChatMode.Agent];

// Add custom modes matching the target
for (const mode of modes.custom) {
const target = mode.target.get();
if (target === effectiveTarget || target === Target.Undefined) {
result.push(mode);
}
}

return result;
}
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The mode filtering logic does not check the visibility.userInvocable property of custom modes. According to the codebase conventions (see chatModes.ts line 116 and line 162), modes with userInvocable set to false should not be shown in user-facing UI. The picker should filter out modes where visibility.userInvocable is false to prevent users from selecting modes that are only intended for agent invocation.

Copilot uses AI. Check for mistakes.
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.

2 participants