Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
fc884f0
vscode-dts: Fix typedoc for WebviewPanel.dispose()
hkleungai Jan 20, 2026
ac24b54
Tracing for sessions timing
osortega Jan 20, 2026
2002893
Use session resource for edit file tool
mjbvz Jan 20, 2026
0572e9f
Tracing chat session status
osortega Jan 20, 2026
e861c3c
Merge pull request #289216 from microsoft/osortega/symbolic-moose
osortega Jan 20, 2026
2fd8c70
mcp: expose MCP server definitions to ext host (#288798)
DonJayamanne Jan 20, 2026
67fcc5f
Merge pull request #289215 from mjbvz/dev/mjbvz/unhappy-angelfish
mjbvz Jan 20, 2026
d0818b2
Adopt hover for session type picker and mode picker
benibenj Jan 20, 2026
f42b4b1
chat: remove fragile timeout in revealSessionIfAlreadyOpen (#289232)
connor4312 Jan 20, 2026
7d39757
switch edit icon to pencil (#289231)
justschen Jan 20, 2026
ac06186
Add `sessionResource` to proposed apis
mjbvz Jan 20, 2026
41c7fd6
Merge pull request #289242 from microsoft/benibenj/radical-wolf
benibenj Jan 20, 2026
df38c17
Merge pull request #289246 from mjbvz/dev/mjbvz/potential-gamefowl
mjbvz Jan 20, 2026
820726c
make thinking scrollable (#289227)
justschen Jan 20, 2026
c1acc69
Workbench - update editor content menu design (#289245)
lszomoru Jan 20, 2026
63c5c4c
mcp: eager push server definitions to ext hosts (#289248)
connor4312 Jan 20, 2026
7dc2938
Merge pull request #289071 from hkleungai/update-typedoc-for-WebviewP…
mjbvz Jan 20, 2026
5e019f2
Fix navigation to `localhost:<port>` in integrated browser (#289253)
kycutler Jan 21, 2026
b00da6f
Privacy footer for agents welcome page
osortega Jan 21, 2026
dbe5ad8
Review comment
osortega Jan 21, 2026
2c85d17
Obey sendElementsToChat feature flag (#289255)
kycutler Jan 21, 2026
d58526e
Merge pull request #289251 from microsoft/joshspicer/agent-status-wid…
joshspicer Jan 21, 2026
4a8af5d
Apply suggestion from @Copilot
osortega Jan 21, 2026
29ddeaa
Apply suggestion from @Copilot
osortega Jan 21, 2026
a5d6f54
Test update
osortega Jan 21, 2026
b6768f0
Initial plan
Copilot Jan 21, 2026
5b84902
Do not open the chat widget if welcome page is agent sessions
osortega Jan 21, 2026
0ab69cd
Use proper virtual file system for prompt files provider extension AP…
pwang347 Jan 21, 2026
c6d8982
Use MarkdownString for localization with links following VS Code patt…
Copilot Jan 21, 2026
1d08808
Remove duplicate provider name parameter
Copilot Jan 21, 2026
2657b9c
Merge pull request #289271 from microsoft/copilot/sub-pr-289266
osortega Jan 21, 2026
fc5012a
Merge branch 'main' into osortega/supporting-newt
osortega Jan 21, 2026
58db9f8
chat: combine adjacent code blocks in MCP tool output (#289272)
connor4312 Jan 21, 2026
749a253
Missing stub
osortega Jan 21, 2026
2b033f7
So silly
osortega Jan 21, 2026
072c5e2
Merge pull request #289273 from microsoft/osortega/sore-caterpillar
osortega Jan 21, 2026
980d45a
Hash custom agent names (#289279)
digitarald Jan 21, 2026
3fd243d
Merge pull request #289266 from microsoft/osortega/planned-halibut
osortega Jan 21, 2026
51a7a0f
fix: memory leak in folder configuration (#279230)
SimonSiefke Jan 21, 2026
729e9d1
better instructions for ai headers (#289259)
justschen Jan 21, 2026
ae3402f
Merge pull request #289210 from microsoft/osortega/supporting-newt
osortega Jan 21, 2026
6912960
Clean up chat row layout (#289238)
roblourens Jan 21, 2026
995b626
Remove chat response model onDidChange listener (#289297)
roblourens Jan 21, 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: 1 addition & 0 deletions extensions/vscode-api-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"fsChunks",
"interactive",
"languageStatusText",
"mcpServerDefinitions",
"nativeWindowHandle",
"notebookDeprecated",
"notebookLiveShare",
Expand Down
7 changes: 2 additions & 5 deletions src/vs/base/browser/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2064,12 +2064,9 @@ export class DisposableResizeObserver extends Disposable {
this._register(toDisposable(() => this.observer.disconnect()));
}

observe(target: Element, options?: ResizeObserverOptions): void {
observe(target: Element, options?: ResizeObserverOptions): IDisposable {
this.observer.observe(target, options);
}

unobserve(target: Element): void {
this.observer.unobserve(target);
return toDisposable(() => this.observer.unobserve(target));
}
}

Expand Down
1 change: 1 addition & 0 deletions src/vs/base/browser/markdownRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,7 @@ function getDomSanitizerConfig(mdStrConfig: MdStrConfig, options: MarkdownSaniti
Schemas.vscodeRemote,
Schemas.vscodeRemoteResource,
Schemas.vscodeNotebookCell,
Schemas.vscodeChatPrompt,
// For links that are handled entirely by the action handler
Schemas.internal,
];
Expand Down
47 changes: 27 additions & 20 deletions src/vs/editor/contrib/floatingMenu/browser/floatingMenu.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,48 @@
*--------------------------------------------------------------------------------------------*/

.floating-menu-overlay-widget {
padding: 0px;
color: var(--vscode-button-foreground);
background-color: var(--vscode-button-background);
border-radius: 2px;
padding: 2px 4px;
color: var(--vscode-foreground);
background-color: var(--vscode-editorWidget-background);
border-radius: 6px;
border: 1px solid var(--vscode-contrastBorder);
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
z-index: 10;
box-shadow: 0 2px 8px var(--vscode-widget-shadow);
overflow: hidden;

.action-item > .action-label {
padding: 5px;
font-size: 12px;
border-radius: 2px;
.actions-container {
gap: 4px;
}

.action-item > .action-label.codicon, .action-item .codicon {
color: var(--vscode-button-foreground);
.action-item > .action-label {
padding: 4px 6px;
font-size: 11px;
line-height: 14px;
border-radius: 4px;
}

.action-item > .action-label.codicon:not(.separator) {
padding-top: 6px;
padding-bottom: 6px;
color: var(--vscode-foreground);
width: 22px;
height: 22px;
padding: 0;
font-size: 16px;
line-height: 22px;
display: flex;
align-items: center;
justify-content: center;
}

.action-item:first-child > .action-label {
padding-left: 7px;
}

.action-item:last-child > .action-label {
padding-right: 7px;
.action-item.primary > .action-label {
background-color: var(--vscode-button-background);
color: var(--vscode-button-foreground);
}

.action-item .action-label.separator {
background-color: var(--vscode-button-separator);
.action-item.primary > .action-label:hover {
background-color: var(--vscode-button-hoverBackground) !important;
}
}
55 changes: 44 additions & 11 deletions src/vs/editor/contrib/floatingMenu/browser/floatingMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { h } from '../../../../base/browser/dom.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
import { autorun, constObservable, observableFromEvent } from '../../../../base/common/observable.js';
import { autorun, constObservable, derived, observableFromEvent } from '../../../../base/common/observable.js';
import { MenuEntryActionViewItem } from '../../../../platform/actions/browser/menuEntryActionViewItem.js';
import { HiddenItemStrategy, MenuWorkbenchToolBar } from '../../../../platform/actions/browser/toolbar.js';
import { IMenuService, MenuId, MenuItemAction } from '../../../../platform/actions/common/actions.js';
Expand All @@ -29,19 +29,43 @@ export class FloatingEditorToolbar extends Disposable implements IEditorContribu
const editorObs = this._register(observableCodeEditor(editor));

const menu = this._register(menuService.createMenu(MenuId.EditorContent, editor.contextKeyService));
const menuIsEmptyObs = observableFromEvent(this, menu.onDidChange, () => menu.getActions().length === 0);
const menuActionsObs = observableFromEvent(this, menu.onDidChange, () => menu.getActions());
const menuPrimaryActionIdObs = derived(reader => {
const menuActions = menuActionsObs.read(reader);
if (menuActions.length === 0) {
return undefined;
}

// Navigation group
const navigationGroup = menuActions
.find((group) => group[0] === 'navigation');

// First action in navigation group
if (navigationGroup && navigationGroup[1].length > 0) {
return navigationGroup[1][0].id;
}

// Fallback to first group/action
for (const [, actions] of menuActions) {
if (actions.length > 0) {
return actions[0].id;
}
}

return undefined;
});

this._register(autorun(reader => {
const menuIsEmpty = menuIsEmptyObs.read(reader);
if (menuIsEmpty) {
const menuPrimaryActionId = menuPrimaryActionIdObs.read(reader);
if (!menuPrimaryActionId) {
return;
}

const container = h('div.floating-menu-overlay-widget');

// Set height explicitly to ensure that the floating menu element
// is rendered in the lower right corner at the correct position.
container.root.style.height = '28px';
container.root.style.height = '26px';

// Toolbar
const toolbar = instantiationService.createInstance(MenuWorkbenchToolBar, container.root, MenuId.EditorContent, {
Expand All @@ -50,15 +74,24 @@ export class FloatingEditorToolbar extends Disposable implements IEditorContribu
return undefined;
}

const keybinding = keybindingService.lookupKeybinding(action.id);
if (!keybinding) {
return undefined;
}

return instantiationService.createInstance(class extends MenuEntryActionViewItem {
override render(container: HTMLElement): void {
super.render(container);

// Highlight primary action
if (action.id === menuPrimaryActionId) {
this.element?.classList.add('primary');
}
}

protected override updateLabel(): void {
const keybinding = keybindingService.lookupKeybinding(action.id);
const keybindingLabel = keybinding ? keybinding.getLabel() : undefined;

if (this.options.label && this.label) {
this.label.textContent = `${this._commandAction.label} (${keybinding.getLabel()})`;
this.label.textContent = keybindingLabel
? `${this._commandAction.label} (${keybindingLabel})`
: this._commandAction.label;
}
}
}, action, { ...options, keybindingNotRenderedWithLabel: true });
Expand Down
33 changes: 8 additions & 25 deletions src/vs/platform/actionWidget/browser/actionList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { IListAccessibilityProvider, List } from '../../../base/browser/ui/list/
import { CancellationToken, CancellationTokenSource } from '../../../base/common/cancellation.js';
import { Codicon } from '../../../base/common/codicons.js';
import { ResolvedKeybinding } from '../../../base/common/keybindings.js';
import { Disposable, toDisposable } from '../../../base/common/lifecycle.js';
import { Disposable, MutableDisposable } from '../../../base/common/lifecycle.js';
import { OS } from '../../../base/common/platform.js';
import { ThemeIcon } from '../../../base/common/themables.js';
import './actionWidget.css';
Expand Down Expand Up @@ -246,7 +246,7 @@ export class ActionList<T> extends Disposable {

private readonly cts = this._register(new CancellationTokenSource());

private hover: { index: number; hover: IHoverWidget } | undefined;
private _hover = this._register(new MutableDisposable<IHoverWidget>());

constructor(
user: string,
Expand Down Expand Up @@ -322,9 +322,6 @@ export class ActionList<T> extends Disposable {
this._register(this._list.onDidChangeFocus(() => this.onFocus()));
this._register(this._list.onDidChangeSelection(e => this.onListSelection(e)));

// Ensure hover is hidden when ActionList is disposed
this._register(toDisposable(() => this.hideHover()));

this._allMenuItems = items;
this._list.splice(0, this._list.length, this._allMenuItems);

Expand All @@ -340,7 +337,7 @@ export class ActionList<T> extends Disposable {
hide(didCancel?: boolean): void {
this._delegate.onHide(didCancel);
this.cts.cancel();
this.hideHover();
this._hover.clear();
this._contextViewService.hideContextView();
}

Expand Down Expand Up @@ -420,15 +417,6 @@ export class ActionList<T> extends Disposable {
}
}

private hideHover() {
if (this.hover) {
if (!this.hover.hover.isDisposed) {
this.hover.hover.dispose();
}
this.hover = undefined;
}
}

private onFocus() {
const focused = this._list.getFocus();
if (focused.length === 0) {
Expand All @@ -448,13 +436,7 @@ export class ActionList<T> extends Disposable {
}

private _showHoverForElement(element: IActionListItem<T>, index: number): void {
// Hide any existing hover when moving to a different item
if (this.hover) {
if (this.hover.index === index && !this.hover.hover.isDisposed) {
return;
}
this.hideHover();
}
let newHover: IHoverWidget | undefined;

// Show hover if the element has hover content
if (element.hover?.content && this.focusCondition(element)) {
Expand All @@ -463,7 +445,7 @@ export class ActionList<T> extends Disposable {
const rowElement = this._getRowElement(index);
if (rowElement) {
const markdown = element.hover.content ? new MarkdownString(element.hover.content) : undefined;
const hover = this._hoverService.showInstantHover({
newHover = this._hoverService.showDelayedHover({
content: markdown ?? '',
target: rowElement,
additionalClasses: ['action-widget-hover'],
Expand All @@ -474,10 +456,11 @@ export class ActionList<T> extends Disposable {
appearance: {
showPointer: true,
},
});
this.hover = hover ? { index, hover } : undefined;
}, { groupId: `actionListHover` });
}
}

this._hover.value = newHover;
}

private async onListHover(e: IListMouseEvent<IActionListItem<T>>) {
Expand Down
3 changes: 3 additions & 0 deletions src/vs/platform/extensions/common/extensionsApiProposals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,9 @@ const _allApiProposals = {
markdownAlertSyntax: {
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.markdownAlertSyntax.d.ts',
},
mcpServerDefinitions: {
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.mcpServerDefinitions.d.ts',
},
mcpToolDefinitions: {
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.mcpToolDefinitions.d.ts',
},
Expand Down
34 changes: 32 additions & 2 deletions src/vs/workbench/api/browser/mainThreadMcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
*--------------------------------------------------------------------------------------------*/

import { mapFindFirst } from '../../../base/common/arraysFind.js';
import { disposableTimeout } from '../../../base/common/async.js';
import { disposableTimeout, RunOnceScheduler } from '../../../base/common/async.js';
import { CancellationError } from '../../../base/common/errors.js';
import { Emitter } from '../../../base/common/event.js';
import { Disposable, DisposableMap, DisposableStore, MutableDisposable } from '../../../base/common/lifecycle.js';
import { ISettableObservable, observableValue } from '../../../base/common/observable.js';
import { autorun, ISettableObservable, observableValue } from '../../../base/common/observable.js';
import Severity from '../../../base/common/severity.js';
import { URI } from '../../../base/common/uri.js';
import * as nls from '../../../nls.js';
Expand Down Expand Up @@ -98,6 +98,36 @@ export class MainThreadMcp extends Disposable implements MainThreadMcpShape {
return launch;
},
}));

// Subscribe to MCP server definition changes and notify ext host
const onDidChangeMcpServerDefinitionsTrigger = this._register(new RunOnceScheduler(() => this._publishServerDefinitions(), 500));
this._register(autorun(reader => {
const collections = this._mcpRegistry.collections.read(reader);
// Read all server definitions to track changes
for (const collection of collections) {
collection.serverDefinitions.read(reader);
}
// Notify ext host that definitions changed (it will re-fetch if needed)
if (!onDidChangeMcpServerDefinitionsTrigger.isScheduled()) {
onDidChangeMcpServerDefinitionsTrigger.schedule();
}
}));

onDidChangeMcpServerDefinitionsTrigger.schedule();
}

private _publishServerDefinitions() {
const collections = this._mcpRegistry.collections.get();
const allServers: McpServerDefinition.Serialized[] = [];

for (const collection of collections) {
const servers = collection.serverDefinitions.get();
for (const server of servers) {
allServers.push(McpServerDefinition.toSerialized(server));
}
}

this._proxy.$onDidChangeMcpServerDefinitions(allServers);
}

$upsertMcpCollection(collection: McpCollectionDefinition.FromExtHost, serversDto: McpServerDefinition.Serialized[]): void {
Expand Down
8 changes: 8 additions & 0 deletions src/vs/workbench/api/common/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1630,6 +1630,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
registerMcpServerDefinitionProvider(id, provider) {
return extHostMcp.registerMcpConfigurationProvider(extension, id, provider);
},
onDidChangeMcpServerDefinitions: (...args) => {
checkProposedApiEnabled(extension, 'mcpServerDefinitions');
return _asExtensionEvent(extHostMcp.onDidChangeMcpServerDefinitions)(...args);
},
get mcpServerDefinitions() {
checkProposedApiEnabled(extension, 'mcpServerDefinitions');
return extHostMcp.mcpServerDefinitions;
},
onDidChangeChatRequestTools(...args) {
checkProposedApiEnabled(extension, 'chatParticipantAdditions');
return _asExtensionEvent(extHostChatAgents2.onDidChangeChatRequestTools)(...args);
Expand Down
3 changes: 3 additions & 0 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3163,13 +3163,16 @@ export interface IStartMcpOptions {
errorOnUserInteraction?: boolean;
}



export interface ExtHostMcpShape {
$substituteVariables(workspaceFolder: UriComponents | undefined, value: McpServerLaunch.Serialized): Promise<McpServerLaunch.Serialized>;
$resolveMcpLaunch(collectionId: string, label: string): Promise<McpServerLaunch.Serialized | undefined>;
$startMcp(id: number, opts: IStartMcpOptions): void;
$stopMcp(id: number): void;
$sendMessage(id: number, message: string): void;
$waitForInitialCollectionProviders(): Promise<void>;
$onDidChangeMcpServerDefinitions(servers: McpServerDefinition.Serialized[]): void;
}

export interface IMcpAuthenticationDetails {
Expand Down
5 changes: 3 additions & 2 deletions src/vs/workbench/api/common/extHostApiCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { isFalsyOrEmpty } from '../../../base/common/arrays.js';
import { VSBuffer } from '../../../base/common/buffer.js';
import { Schemas, matchesSomeScheme } from '../../../base/common/network.js';
import { URI, UriComponents } from '../../../base/common/uri.js';
import { URI } from '../../../base/common/uri.js';
import { IPosition } from '../../../editor/common/core/position.js';
import { IRange } from '../../../editor/common/core/range.js';
import { ISelection } from '../../../editor/common/core/selection.js';
Expand All @@ -23,6 +23,7 @@ import { TransientCellMetadata, TransientDocumentMetadata } from '../../contrib/
import * as search from '../../contrib/search/common/search.js';
import type * as vscode from 'vscode';
import { PromptsType } from '../../contrib/chat/common/promptSyntax/promptTypes.js';
import type { IExtensionPromptFileResult } from '../../contrib/chat/common/promptSyntax/chatPromptFilesContribution.js';

//#region --- NEW world

Expand Down Expand Up @@ -560,7 +561,7 @@ const newCommands: ApiCommand[] = [
new ApiCommand(
'vscode.extensionPromptFileProvider', '_listExtensionPromptFiles', 'Get all extension-contributed prompt files (custom agents, instructions, and prompt files).',
[],
new ApiCommandResult<{ uri: UriComponents; type: PromptsType }[], { uri: vscode.Uri; type: PromptsType }[]>(
new ApiCommandResult<IExtensionPromptFileResult[], { uri: vscode.Uri; type: PromptsType }[]>(
'A promise that resolves to an array of objects containing uri and type.',
(value) => {
if (!value) {
Expand Down
Loading
Loading