Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
293d7f2
Support non-default 'debug.toolBarLocation' settings in agentsControl
joshspicer Jan 16, 2026
68f8ad5
show filtered agents view when clicking on notification
joshspicer Jan 16, 2026
884c14e
prettier vertical divider in agent-status-badge-section
joshspicer Jan 16, 2026
a9d8559
auto enable command center when toggling 'Agent Status'
joshspicer Jan 16, 2026
5e2f44d
Refactor sessions picker visibility
osortega Jan 16, 2026
e97df5b
tidy up commands (fix https://github.com/microsoft/vscode/issues/288082)
joshspicer Jan 16, 2026
8f3cef1
handle layout with chat control
joshspicer Jan 16, 2026
4bd05d1
clear agent session filter when filtered category is completed
joshspicer Jan 16, 2026
dcd93ff
fix how to detect if an agent's artifacts count as 'projectable'
joshspicer Jan 16, 2026
6334cf0
chat: feat: allow rendering links as links (#288142)
ulugbekna Jan 16, 2026
2dcb467
optimize how we store/handle commandCenterMenu
joshspicer Jan 16, 2026
c13eb90
Load instructions on demand and don't add images (#288503)
aeschli Jan 16, 2026
37d9d32
Open chats from welcome view in maximized chat
osortega Jan 16, 2026
650dc9d
Merge pull request #288504 from microsoft/osortega/damp-stingray
osortega Jan 16, 2026
4a61476
Merge pull request #288506 from microsoft/osortega/invisible-bedbug
osortega Jan 16, 2026
b3f52fe
agents control and agent projection tweaks (#288491)
joshspicer Jan 16, 2026
7e01c14
fix mermaid output (#288510)
justschen Jan 17, 2026
21bf05b
Do not disable target picker in welcome view
osortega Jan 17, 2026
f5eddbc
mirror default icon display behavior for chatSession optionGroups
joshspicer Jan 17, 2026
fee4635
Agents welcome page layout fix
osortega Jan 17, 2026
a465d26
Open Agent Sessions button should maximize chat
osortega Jan 17, 2026
05e6870
Merge pull request #288519 from microsoft/osortega/live-parakeet
osortega Jan 17, 2026
fe6b1ad
Merge pull request #288520 from microsoft/joshspicer/optiongroups-ico…
joshspicer Jan 17, 2026
654d7d5
Merge pull request #288521 from microsoft/osortega/registered-rat
osortega Jan 17, 2026
1e90b6c
Merge pull request #288523 from microsoft/osortega/irrelevant-monkey
osortega Jan 17, 2026
c945bef
Increase max number of persisted sessions (#288528)
roblourens Jan 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/instructions/chat.instructions.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
---
applyTo: '**/chat/**'
description: Chat feature area coding guidelines
---

Expand Down
3 changes: 1 addition & 2 deletions .github/instructions/interactive.instructions.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
---
applyTo: '**/interactive/**'
description: Architecture documentation for VS Code interactive window component
description: Architecture documentation for VS Code interactive window component. Use when working in folder
---

# Interactive Window
Expand Down
1 change: 0 additions & 1 deletion .github/instructions/learnings.instructions.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
---
applyTo: **
description: This document describes how to deal with learnings that you make. (meta instruction)
---

Expand Down
1 change: 0 additions & 1 deletion .github/instructions/notebook.instructions.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
---
applyTo: '**/notebook/**'
description: Architecture documentation for VS Code notebook and interactive window components
---

Expand Down
7 changes: 5 additions & 2 deletions src/vs/workbench/contrib/chat/browser/actions/chatActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -948,9 +948,12 @@ MenuRegistry.appendMenuItem(MenuId.CommandCenter, {
ChatContextKeys.Setup.disabled.negate()
),
ContextKeyExpr.has('config.chat.commandCenter.enabled'),
ContextKeyExpr.has(`config.${ChatConfiguration.AgentStatusEnabled}`).negate() // Hide when agent status is shown
ContextKeyExpr.or(
ContextKeyExpr.has(`config.${ChatConfiguration.AgentStatusEnabled}`).negate(), // Show when agent status is disabled
ChatContextKeys.agentStatusHasNotifications.negate() // Or when agent status has no notifications
)
),
order: 10001 // to the right of command center
order: 10003 // to the right of agent controls
});

// Add to the global title bar if command center is disabled
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ export class OpenSessionTargetPickerAction extends Action2 {
tooltip: localize('setSessionTarget', "Set Session Target"),
category: CHAT_CATEGORY,
f1: false,
precondition: ContextKeyExpr.and(ChatContextKeys.enabled, ChatContextKeys.hasCanDelegateProviders, ChatContextKeys.chatSessionIsEmpty),
precondition: ContextKeyExpr.and(ChatContextKeys.enabled, ChatContextKeys.hasCanDelegateProviders, ContextKeyExpr.or(ChatContextKeys.chatSessionIsEmpty, ChatContextKeys.inAgentSessionsWelcome)),
menu: [
{
id: MenuId.ChatInput,
Expand Down Expand Up @@ -479,12 +479,14 @@ export class ChatSessionPrimaryPickerAction extends Action2 {
order: 4,
group: 'navigation',
when:
ContextKeyExpr.or(
ContextKeyExpr.and(
ChatContextKeys.chatSessionHasModels,
ChatContextKeys.lockedToCodingAgent,
ContextKeyExpr.and(
ChatContextKeys.inAgentSessionsWelcome,
ChatContextKeys.chatSessionType.notEqualsTo('local')
ContextKeyExpr.or(
ChatContextKeys.lockedToCodingAgent,
ContextKeyExpr.and(
ChatContextKeys.inAgentSessionsWelcome,
ChatContextKeys.chatSessionType.notEqualsTo('local')
)
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { localize, localize2 } from '../../../../../nls.js';
import { Action2, MenuId } from '../../../../../platform/actions/common/actions.js';
import { Action2 } from '../../../../../platform/actions/common/actions.js';
import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js';
import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js';
import { KeyCode } from '../../../../../base/common/keyCodes.js';
Expand All @@ -15,7 +15,6 @@ import { IAgentSessionProjectionService } from './agentSessionProjectionService.
import { IAgentSession, isMarshalledAgentSessionContext, IMarshalledAgentSessionContext } from './agentSessionsModel.js';
import { IAgentSessionsService } from './agentSessionsService.js';
import { CHAT_CATEGORY } from '../actions/chatActions.js';
import { openSessionInChatWidget } from './agentSessionsOpener.js';
import { ToggleTitleBarConfigAction } from '../../../../browser/parts/titlebar/titlebarActions.js';
import { IsCompactTitleBarContext } from '../../../../common/contextkeys.js';

Expand Down Expand Up @@ -90,45 +89,6 @@ export class ExitAgentSessionProjectionAction extends Action2 {

//#endregion

//#region Open in Chat Panel

export class OpenInChatPanelAction extends Action2 {
static readonly ID = 'agentSession.openInChatPanel';

constructor() {
super({
id: OpenInChatPanelAction.ID,
title: localize2('openInChatPanel', "Open in Chat Panel"),
category: CHAT_CATEGORY,
precondition: ChatContextKeys.enabled,
menu: [{
id: MenuId.AgentSessionsContext,
group: '1_open',
order: 1,
}]
});
}

override async run(accessor: ServicesAccessor, context?: IAgentSession | IMarshalledAgentSessionContext): Promise<void> {
const agentSessionsService = accessor.get(IAgentSessionsService);

let session: IAgentSession | undefined;
if (context) {
if (isMarshalledAgentSessionContext(context)) {
session = agentSessionsService.getSession(context.session.resource);
} else {
session = context;
}
}

if (session) {
await openSessionInChatWidget(accessor, session);
}
}
}

//#endregion

//#region Toggle Agent Status

export class ToggleAgentStatusAction extends ToggleTitleBarConfigAction {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,11 @@ export class AgentSessionProjectionService extends Disposable implements IAgentS
}
}

private async _openSessionFiles(session: IAgentSession): Promise<void> {
/**
* Open the session's files in a multi-diff editor.
* @returns true if any files were opened, false if nothing to display
*/
private async _openSessionFiles(session: IAgentSession): Promise<boolean> {
// Clear editors first
await this.editorGroupsService.applyWorkingSet('empty', { preserveFocus: true });

Expand Down Expand Up @@ -178,11 +182,14 @@ export class AgentSessionProjectionService extends Disposable implements IAgentS
const sessionKey = session.resource.toString();
const newWorkingSet = this.editorGroupsService.saveWorkingSet(`agent-session-projection-${sessionKey}`);
this._sessionWorkingSets.set(sessionKey, newWorkingSet);
return true;
} else {
this.logService.trace(`[AgentSessionProjection] No files with diffs to display (all changes missing originalUri)`);
return false;
}
} else {
this.logService.trace(`[AgentSessionProjection] Session has no changes to display`);
return false;
}
}

Expand Down Expand Up @@ -222,24 +229,45 @@ export class AgentSessionProjectionService extends Disposable implements IAgentS
this._sessionWorkingSets.set(previousSessionKey, previousWorkingSet);
}

// Always open session files to ensure they're displayed
await this._openSessionFiles(session);

// Set active state
const wasActive = this._isActive;
this._isActive = true;
this._activeSession = session;
this._inProjectionModeContextKey.set(true);
this.layoutService.mainContainer.classList.add('agent-session-projection-active');

// Update the agent status to show session mode
this.agentStatusService.enterSessionMode(session.resource.toString(), session.label);
// For local sessions, changes are shown via chatEditing.viewChanges, not _openSessionFiles
// For other providers, try to open session files from session.changes
let filesOpened = false;
if (session.providerType === AgentSessionProviders.Local) {
// Local sessions use editing session for changes - we already verified hasUndecidedChanges above
// Clear editors to prepare for the changes view
await this.editorGroupsService.applyWorkingSet('empty', { preserveFocus: true });
filesOpened = true;
} else {
// Try to open session files - only continue with projection if files were displayed
filesOpened = await this._openSessionFiles(session);
}

if (!wasActive) {
this._onDidChangeProjectionMode.fire(true);
if (!filesOpened) {
this.logService.trace('[AgentSessionProjection] No files to display, opening chat without projection mode');
// Restore the working set we just saved if this was our first attempt
if (!this._isActive && this._preProjectionWorkingSet) {
await this.editorGroupsService.applyWorkingSet(this._preProjectionWorkingSet);
this.editorGroupsService.deleteWorkingSet(this._preProjectionWorkingSet);
this._preProjectionWorkingSet = undefined;
}
// Fall through to just open the chat panel
} else {
// Set active state
const wasActive = this._isActive;
this._isActive = true;
this._activeSession = session;
this._inProjectionModeContextKey.set(true);
this.layoutService.mainContainer.classList.add('agent-session-projection-active');

// Update the agent status to show session mode
this.agentStatusService.enterSessionMode(session.resource.toString(), session.label);

if (!wasActive) {
this._onDidChangeProjectionMode.fire(true);
}
// Always fire session change event (for title updates when switching sessions)
this._onDidChangeActiveSession.fire(session);
}
// Always fire session change event (for title updates when switching sessions)
this._onDidChangeActiveSession.fire(session);
}

// Open the session in the chat panel (always, even without changes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { IInstantiationService } from '../../../../../platform/instantiation/com
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
import { ChatConfiguration } from '../../common/constants.js';
import { AuxiliaryBarMaximizedContext } from '../../../../common/contextkeys.js';
import { LayoutSettings } from '../../../../services/layout/browser/layoutService.js';

//#region Actions and Menus

Expand Down Expand Up @@ -61,7 +62,6 @@ registerAction2(SetAgentSessionsOrientationSideBySideAction);
// Agent Session Projection
registerAction2(EnterAgentSessionProjectionAction);
registerAction2(ExitAgentSessionProjectionAction);
// registerAction2(OpenInChatPanelAction); // TODO@joshspicer https://github.com/microsoft/vscode/issues/288082
registerAction2(ToggleAgentStatusAction);
registerAction2(ToggleAgentSessionProjectionAction);

Expand Down Expand Up @@ -240,9 +240,15 @@ class AgentStatusRendering extends Disposable implements IWorkbenchContribution
}, undefined));

// Add/remove CSS class on workbench based on setting
// Also force enable command center when agent status is enabled
const updateClass = () => {
const enabled = configurationService.getValue<boolean>(ChatConfiguration.AgentStatusEnabled) === true;
mainWindow.document.body.classList.toggle('agent-status-enabled', enabled);

// Force enable command center when agent status is enabled
if (enabled && configurationService.getValue<boolean>(LayoutSettings.COMMAND_CENTER) !== true) {
configurationService.updateValue(LayoutSettings.COMMAND_CENTER, true);
}
};
updateClass();
this._register(configurationService.onDidChangeConfiguration(e => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,15 +196,6 @@ export class PickAgentSessionAction extends Action2 {
group: 'navigation',
order: 2
},
{
id: MenuId.ViewTitle,
when: ContextKeyExpr.and(
ContextKeyExpr.equals('view', ChatViewId),
ContextKeyExpr.equals(`config.${ChatConfiguration.ChatViewSessionsEnabled}`, true)
),
group: '2_history',
order: 1
},
{
id: MenuId.EditorTitle,
when: ActiveEditorContext.isEqualTo(ChatEditorInput.EditorID),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,27 @@ import { IMouseEvent } from '../../../../../base/browser/mouseEvent.js';
export interface IAgentSessionsControlOptions extends IAgentSessionsSorterOptions {
readonly overrideStyles: IStyleOverride<IListStyles>;
readonly filter: IAgentSessionsFilter;
readonly source: AgentSessionsControlSource;

getHoverPosition(): HoverPosition;
trackActiveEditorSession(): boolean;
}

export const enum AgentSessionsControlSource {
ChatViewPane = 'chatViewPane',
WelcomeView = 'welcomeView'
}

type AgentSessionOpenedClassification = {
owner: 'bpasero';
providerType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The provider type of the opened agent session.' };
source: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The source of the opened agent session.' };
comment: 'Event fired when a agent session is opened from the agent sessions control.';
};

type AgentSessionOpenedEvent = {
providerType: string;
source: AgentSessionsControlSource;
};

export class AgentSessionsControl extends Disposable implements IAgentSessionsControl {
Expand Down Expand Up @@ -196,10 +204,11 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo
}

this.telemetryService.publicLog2<AgentSessionOpenedEvent, AgentSessionOpenedClassification>('agentSessionOpened', {
providerType: element.providerType
providerType: element.providerType,
source: this.options.source
});

await this.instantiationService.invokeFunction(openSession, element, e);
await this.instantiationService.invokeFunction(openSession, element, { ...e, expanded: this.options.source === AgentSessionsControlSource.WelcomeView });
}

private async showContextMenu({ element, anchor, browserEvent }: ITreeContextMenuEvent<AgentSessionListItem>): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { IAgentSessionProjectionService } from './agentSessionProjectionService.
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
import { ChatConfiguration } from '../../common/constants.js';

export async function openSession(accessor: ServicesAccessor, session: IAgentSession, openOptions?: { sideBySide?: boolean; editorOptions?: IEditorOptions }): Promise<void> {
export async function openSession(accessor: ServicesAccessor, session: IAgentSession, openOptions?: { sideBySide?: boolean; editorOptions?: IEditorOptions; expanded?: boolean }): Promise<void> {
const configurationService = accessor.get(IConfigurationService);
const projectionService = accessor.get(IAgentSessionProjectionService);

Expand All @@ -35,7 +35,7 @@ export async function openSession(accessor: ServicesAccessor, session: IAgentSes
* Opens a session in the traditional chat widget (side panel or editor).
* Use this when you explicitly want to open in the chat widget rather than agent session projection mode.
*/
export async function openSessionInChatWidget(accessor: ServicesAccessor, session: IAgentSession, openOptions?: { sideBySide?: boolean; editorOptions?: IEditorOptions }): Promise<void> {
export async function openSessionInChatWidget(accessor: ServicesAccessor, session: IAgentSession, openOptions?: { sideBySide?: boolean; editorOptions?: IEditorOptions; expanded?: boolean }): Promise<void> {
const chatSessionsService = accessor.get(IChatSessionsService);
const chatWidgetService = accessor.get(IChatWidgetService);

Expand All @@ -51,7 +51,8 @@ export async function openSessionInChatWidget(accessor: ServicesAccessor, sessio
let options: IChatEditorOptions = {
...sessionOptions,
...openOptions?.editorOptions,
revealIfOpened: true // always try to reveal if already opened
revealIfOpened: true, // always try to reveal if already opened
expanded: openOptions?.expanded
};

await chatSessionsService.activateChatSessionItemProvider(session.providerType); // ensure provider is activated before trying to open
Expand Down
Loading
Loading