diff --git a/.github/instructions/kusto.instructions.md b/.github/instructions/kusto.instructions.md new file mode 100644 index 0000000000000..2c77e92555d6c --- /dev/null +++ b/.github/instructions/kusto.instructions.md @@ -0,0 +1,19 @@ +--- +description: Kusto exploration and telemetry analysis instructions +--- + +# Kusto Exploration Instructions + +When performing Kusto queries, telemetry analysis, or data exploration tasks for VS Code, consult the comprehensive Kusto instructions located at: + +**[kusto-vscode-instructions.md](../../../vscode-internalbacklog/instructions/kusto/kusto-vscode-instructions.md)** + +These instructions contain valuable information about: +- Available Kusto clusters and databases for VS Code telemetry +- Common query patterns and best practices +- Schema information and table structures +- Tips for effective data exploration + +Reading these instructions before writing Kusto queries will help you write more accurate and efficient queries, avoid common pitfalls, and leverage existing knowledge about VS Code's telemetry infrastructure. + +(Make sure to have the main branch of vscode-internalbacklog up to date in case there are problems). diff --git a/src/vs/platform/update/electron-main/updateService.snap.ts b/src/vs/platform/update/electron-main/updateService.snap.ts index 1176dc3466b72..9805d43790b1b 100644 --- a/src/vs/platform/update/electron-main/updateService.snap.ts +++ b/src/vs/platform/update/electron-main/updateService.snap.ts @@ -156,7 +156,7 @@ export class SnapUpdateService extends AbstractUpdateService { const onDebouncedCurrentChange = Event.debounce(onCurrentChange, (_, e) => e, 2000); const listener = onDebouncedCurrentChange(() => this.checkForUpdates(false)); - lifecycleMainService.onWillShutdown(() => { + Event.once(lifecycleMainService.onWillShutdown)(() => { listener.dispose(); watcher.close(); }); diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 9e1a40ec4b327..3ea632f77b186 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2843,14 +2843,29 @@ export namespace ChatResponseExtensionsPart { } export namespace ChatResponsePullRequestPart { - export function from(part: vscode.ChatResponsePullRequestPart): Dto { + export function from(part: Omit & { command?: vscode.Command }, commandsConverter: CommandsConverter, commandDisposables: DisposableStore): Dto { + // If the command isn't in the converter, then this session may have been restored, and the command args don't exist anymore + let command: extHostProtocol.ICommandDto; + if (!part.command) { + if (!part.uri) { + throw new Error('Pull request part must have a command if URI is provided'); + } + command = { + title: 'Open Pull Request', + id: 'vscode.open', + arguments: [part.uri] + }; + } else { + command = commandsConverter.toInternal(part.command, commandDisposables); + } return { kind: 'pullRequest', author: part.author, title: part.title, description: part.description, uri: part.uri, - linkTag: part.linkTag + linkTag: part.linkTag, + command }; } } @@ -3296,7 +3311,7 @@ export namespace ChatResponsePart { } else if (part instanceof types.ChatResponseExtensionsPart) { return ChatResponseExtensionsPart.from(part); } else if (part instanceof types.ChatResponsePullRequestPart) { - return ChatResponsePullRequestPart.from(part); + return ChatResponsePullRequestPart.from(part, commandsConverter, commandDisposables); } else if (part instanceof types.ChatToolInvocationPart) { return ChatToolInvocationPart.from(part); } else if (part instanceof types.ChatResponseWorkspaceEditPart) { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index a0a9ed3a0a5a1..b80dfdf080562 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -14,7 +14,7 @@ import { MarshalledId } from '../../../base/common/marshallingIds.js'; import { Mimes } from '../../../base/common/mime.js'; import { nextCharLength } from '../../../base/common/strings.js'; import { isNumber, isObject, isString, isStringArray } from '../../../base/common/types.js'; -import { URI } from '../../../base/common/uri.js'; +import { isUriComponents, URI } from '../../../base/common/uri.js'; import { generateUuid } from '../../../base/common/uuid.js'; import { TextEditorSelectionSource } from '../../../platform/editor/common/editor.js'; import { ExtensionIdentifier, IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; @@ -3298,13 +3298,26 @@ export class ChatResponseExtensionsPart { } export class ChatResponsePullRequestPart { + public readonly uri?: vscode.Uri; + public readonly command: vscode.Command; + constructor( - public readonly uri: vscode.Uri, + uriOrCommand: vscode.Uri | vscode.Command, public readonly title: string, public readonly description: string, public readonly author: string, public readonly linkTag: string ) { + if (isUriComponents(uriOrCommand)) { + this.uri = uriOrCommand as vscode.Uri; + this.command = { + title: 'Open Pull Request', + command: 'vscode.open', + arguments: [uriOrCommand] + }; + } else { + this.command = uriOrCommand; + } } toJSON() { diff --git a/src/vs/workbench/browser/parts/editor/media/modalEditorPart.css b/src/vs/workbench/browser/parts/editor/media/modalEditorPart.css index 41f2b76b8d7f5..f7acd3ff3a4cc 100644 --- a/src/vs/workbench/browser/parts/editor/media/modalEditorPart.css +++ b/src/vs/workbench/browser/parts/editor/media/modalEditorPart.css @@ -23,7 +23,7 @@ /** Modal Editor Part: Shadow Container */ .monaco-modal-editor-block .modal-editor-shadow { - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); + box-shadow: 0 4px 32px var(--vscode-widget-shadow, rgba(0, 0, 0, 0.2)); border-radius: 8px; overflow: hidden; } @@ -49,21 +49,21 @@ display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; - height: 24px; - min-height: 24px; - padding: 0 8px; + height: 32px; + min-height: 32px; + padding: 0 8px 0 16px; background-color: var(--vscode-titleBar-activeBackground); border-bottom: 1px solid var(--vscode-titleBar-border, transparent); } .monaco-modal-editor-block .modal-editor-title { - grid-column: 2; + grid-column: 1; font-size: 12px; + font-weight: 500; color: var(--vscode-titleBar-activeForeground); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - text-align: center; } .monaco-modal-editor-block .modal-editor-action-container { @@ -73,6 +73,10 @@ justify-content: flex-end; } +.monaco-modal-editor-block .modal-editor-action-container .actions-container { + gap: 4px; +} + /** Modal Editor Part: Ensure proper sizing */ .monaco-modal-editor-block .modal-editor-part .content { flex: 1; diff --git a/src/vs/workbench/browser/parts/editor/modalEditorPart.ts b/src/vs/workbench/browser/parts/editor/modalEditorPart.ts index 72ca9e19c5904..023a7116c1e87 100644 --- a/src/vs/workbench/browser/parts/editor/modalEditorPart.ts +++ b/src/vs/workbench/browser/parts/editor/modalEditorPart.ts @@ -123,7 +123,7 @@ export class ModalEditorPart { editorPartContainer.style.height = `${height}px`; const borderSize = 2; // Account for 1px border on all sides and modal header height - const headerHeight = 24 + 1 /* border bottom */; + const headerHeight = 32 + 1 /* border bottom */; editorPart.layout(width - borderSize, height - borderSize - headerHeight, 0, 0); })); diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatPullRequestContentPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatPullRequestContentPart.ts index 2f66cea6395d1..448910aeb37ec 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatPullRequestContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatPullRequestContentPart.ts @@ -13,15 +13,14 @@ import { IChatContentPart } from './chatContentParts.js'; import { Codicon } from '../../../../../../base/common/codicons.js'; import { ThemeIcon } from '../../../../../../base/common/themables.js'; import { addDisposableListener } from '../../../../../../base/browser/dom.js'; -import { IOpenerService } from '../../../../../../platform/opener/common/opener.js'; +import { ICommandService } from '../../../../../../platform/commands/common/commands.js'; export class ChatPullRequestContentPart extends Disposable implements IChatContentPart { public readonly domNode: HTMLElement; constructor( private readonly pullRequestContent: IChatPullRequestContent, - @IOpenerService private readonly openerService: IOpenerService - ) { + @ICommandService private readonly commandService: ICommandService) { super(); this.domNode = dom.$('.chat-pull-request-content-part'); @@ -33,11 +32,13 @@ export class ChatPullRequestContentPart extends Disposable implements IChatConte icon.classList.add(...ThemeIcon.asClassNameArray(Codicon.gitPullRequest)); const titleLink: HTMLAnchorElement = dom.append(titleContainer, dom.$('a.title')); titleLink.textContent = `${this.pullRequestContent.title} - ${this.pullRequestContent.author}`; - titleLink.href = this.pullRequestContent.uri.toString(); + if (this.pullRequestContent.uri) { + titleLink.href = this.pullRequestContent.uri?.toString(); + } this._register(addDisposableListener(titleLink, 'click', (e) => { e.preventDefault(); e.stopPropagation(); - this.openerService.open(this.pullRequestContent.uri, { allowCommands: true }); + this.commandService.executeCommand(this.pullRequestContent.command.id, ...(this.pullRequestContent.command.arguments ?? [])); })); } diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/widget/chatListRenderer.ts index 1b15146851d92..5792cfb7da8ab 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatListRenderer.ts @@ -2007,8 +2007,9 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer handleSubmit(answers, part!) }); diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatListWidget.ts b/src/vs/workbench/contrib/chat/browser/widget/chatListWidget.ts index 1c2b78fbe3c9f..61a26fa93430f 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatListWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatListWidget.ts @@ -190,6 +190,7 @@ export class ChatListWidget extends Disposable { private _lastItem: ChatTreeItem | undefined; private _mostRecentlyFocusedItemIndex: number = -1; private _scrollLock: boolean = true; + private _suppressAutoScroll: boolean = false; private _settingChangeCounter: number = 0; private _visibleChangeCount: number = 0; @@ -525,7 +526,6 @@ export class ChatListWidget extends Disposable { })); const editing = this._viewModel.editing; - const checkpoint = this._viewModel.model?.checkpoint; this._withPersistedAutoScroll(() => { this._tree.setChildren(null, treeItems, { @@ -534,6 +534,9 @@ export class ChatListWidget extends Disposable { // Pending types only have 'id', request/response have 'dataId' const baseId = (isRequestVM(element) || isResponseVM(element)) ? element.dataId : element.id; const disablement = (isRequestVM(element) || isResponseVM(element)) ? element.shouldBeRemovedOnSend : undefined; + // Per-element editing state: only re-render items whose editing role changed + const isEditTarget = isRequestVM(element) && editing?.id === element.id; + const isBlocked = (isRequestVM(element) || isResponseVM(element)) ? element.shouldBeBlocked.get() : false; return baseId + // If a response is in the process of progressive rendering, we need to ensure that it will // be re-rendered so progressive rendering is restarted, even if the model wasn't updated. @@ -542,10 +545,11 @@ export class ChatListWidget extends Disposable { (isResponseVM(element) ? `_${element.contentReferences.length}` : '') + // Re-render if element becomes hidden due to undo/redo `_${disablement ? `${disablement.afterUndoStop || '1'}` : '0'}` + - // Re-render if we have an element currently being edited - `_${editing ? '1' : '0'}` + - // Re-render if we have an element currently being checkpointed - `_${checkpoint ? '1' : '0'}` + + // Re-render the request being edited and requests whose blocked state changed + `_${isEditTarget ? 'edit' : ''}` + + `_${isBlocked ? 'blocked' : ''}` + + // Re-render requests when editing starts/stops (for hover button visibility, click handlers) + (isRequestVM(element) ? `_${editing ? '1' : '0'}` : '') + // Re-render all if invoked by setting change `_setting${this._settingChangeCounter}` + // Rerender request if we got new content references in the response @@ -706,7 +710,19 @@ export class ChatListWidget extends Disposable { } } + /** + * Suppress auto-scroll behavior temporarily. While suppressed, + * _withPersistedAutoScroll will not scroll to bottom after operations. + */ + set suppressAutoScroll(value: boolean) { + this._suppressAutoScroll = value; + } + private _withPersistedAutoScroll(fn: () => void): void { + if (this._suppressAutoScroll) { + fn(); + return; + } const wasScrolledToBottom = this.isScrolledToBottom; fn(); if (wasScrolledToBottom) { diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts index f85397da22e73..39995958c1ea0 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts @@ -1550,6 +1550,7 @@ export class ChatWidget extends Disposable implements IChatWidget { this.inputPart.dnd.setDisabledOverlay(!isInput); this.input.renderAttachedContext(); this.input.setValue(currentElement.messageText, false); + this.listWidget.suppressAutoScroll = true; this.onDidChangeItems(); this.input.inputEditor.focus(); @@ -1586,6 +1587,7 @@ export class ChatWidget extends Disposable implements IChatWidget { finishedEditing(completedEdit?: boolean): void { // reset states + this.listWidget.suppressAutoScroll = false; const editedRequest = this.listWidget.getTemplateDataForRequestId(this.viewModel?.editing?.id); if (this.recentlyRestoredCheckpoint) { this.recentlyRestoredCheckpoint = false; diff --git a/src/vs/workbench/contrib/chat/common/chatService/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService/chatService.ts index 9d1331c282592..25d226f89b115 100644 --- a/src/vs/workbench/contrib/chat/common/chatService/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService/chatService.ts @@ -498,7 +498,7 @@ export interface ILegacyChatTerminalToolInvocationData { } export function isLegacyChatTerminalToolInvocationData(data: unknown): data is ILegacyChatTerminalToolInvocationData { - return !!data && typeof data === 'object' && 'command' in data; + return !!data && typeof data === 'object' && 'command' in data && 'language' in data; } export interface IChatToolInputInvocationData { @@ -817,7 +817,11 @@ export interface IChatExtensionsContent { } export interface IChatPullRequestContent { - uri: URI; + /** + * @deprecated use `command` instead + */ + uri?: URI; + command: Command; title: string; description: string; author: string; diff --git a/src/vs/workbench/contrib/chat/common/model/chatModel.ts b/src/vs/workbench/contrib/chat/common/model/chatModel.ts index a1dc066456011..fb343cc6bc229 100644 --- a/src/vs/workbench/contrib/chat/common/model/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/model/chatModel.ts @@ -2305,6 +2305,9 @@ export class ChatModel extends Disposable implements IChatModel { resetCheckpoint(): void { for (const request of this._requests) { request.setShouldBeBlocked(false); + if (request.response) { + request.response.setBlockedState(false); + } } } @@ -2329,6 +2332,9 @@ export class ChatModel extends Disposable implements IChatModel { const request = this._requests[i]; if (this._checkpoint && !checkpoint) { request.setShouldBeBlocked(false); + if (request.response) { + request.response.setBlockedState(false); + } } else if (checkpoint && i >= checkpointIndex) { request.setShouldBeBlocked(true); if (request.response) { @@ -2336,6 +2342,9 @@ export class ChatModel extends Disposable implements IChatModel { } } else if (checkpoint && i < checkpointIndex) { request.setShouldBeBlocked(false); + if (request.response) { + request.response.setBlockedState(false); + } } } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/hookSchema.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/hookSchema.ts index 2fde3773eaaa8..3483bf2fb6437 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/hookSchema.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/hookSchema.ts @@ -45,7 +45,7 @@ export const HOOK_TYPES = [ { id: HookType.PreToolUse, label: nls.localize('hookType.preToolUse.label', "Pre-Tool Use"), - description: nls.localize('hookType.preToolUse.description', "Executed before the agent uses any tool (such as bash, edit, view).") + description: nls.localize('hookType.preToolUse.description', "Executed before the agent uses any tool.") }, { id: HookType.PostToolUse, @@ -181,7 +181,7 @@ export const hookFileSchema: IJSONSchema = { }, PreToolUse: { ...hookArraySchema, - description: nls.localize('hookFile.preToolUse', 'Executed before the agent uses any tool (such as bash, edit, view). This is the most powerful hook as it can approve or deny tool executions. Use to block dangerous commands, enforce security policies, require approval for sensitive operations, or log tool usage.') + description: nls.localize('hookFile.preToolUse', 'Executed before the agent uses any tool. This is the most powerful hook as it can approve or deny tool executions. Use to block dangerous commands, enforce security policies, require approval for sensitive operations, or log tool usage.') }, PostToolUse: { ...hookArraySchema, diff --git a/src/vs/workbench/contrib/chat/test/browser/accessibility/chatResponseAccessibleView.test.ts b/src/vs/workbench/contrib/chat/test/browser/accessibility/chatResponseAccessibleView.test.ts index 7b2b9af22783a..9aca92e5856d1 100644 --- a/src/vs/workbench/contrib/chat/test/browser/accessibility/chatResponseAccessibleView.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/accessibility/chatResponseAccessibleView.test.ts @@ -125,6 +125,7 @@ suite('ChatResponseAccessibleView', () => { const prData: IChatPullRequestContent = { kind: 'pullRequest', uri: URI.file('/test'), + command: { id: 'vscode.open', title: 'Open Pull Request', arguments: [URI.file('/test')] }, title: 'Add new feature', description: 'This PR adds a great feature', author: 'testuser', diff --git a/src/vs/workbench/contrib/editTelemetry/browser/telemetry/editSourceTrackingImpl.ts b/src/vs/workbench/contrib/editTelemetry/browser/telemetry/editSourceTrackingImpl.ts index b5c9a99c87329..2a60ae28108aa 100644 --- a/src/vs/workbench/contrib/editTelemetry/browser/telemetry/editSourceTrackingImpl.ts +++ b/src/vs/workbench/contrib/editTelemetry/browser/telemetry/editSourceTrackingImpl.ts @@ -18,6 +18,9 @@ import { sumByCategory } from '../helpers/utils.js'; import { ScmAdapter, ScmRepoAdapter } from './scmAdapter.js'; import { IRandomService } from '../randomService.js'; +type EditTelemetryMode = 'longterm' | '5minWindow' | '20minFocusWindow'; +type EditTelemetryTrigger = '10hours' | 'hashChange' | 'branchChange' | 'closed' | 'time'; + export class EditSourceTrackingImpl extends Disposable { public readonly docsState; private readonly _states; @@ -63,7 +66,7 @@ class TrackedDocumentInfo extends Disposable { const longtermResetSignal = observableSignal('resetSignal'); - let longtermReason: '10hours' | 'hashChange' | 'branchChange' | 'closed' = 'closed'; + let longtermReason: EditTelemetryTrigger = 'closed'; this.longtermTracker = derived((reader) => { if (!this._statsEnabled.read(reader)) { return undefined; } longtermResetSignal.read(reader); @@ -167,7 +170,7 @@ class TrackedDocumentInfo extends Disposable { } - async sendTelemetry(mode: 'longterm' | '5minWindow' | '20minFocusWindow', trigger: string, t: DocumentEditSourceTracker, focusTime: number, actualTime: number) { + async sendTelemetry(mode: EditTelemetryMode, trigger: EditTelemetryTrigger, t: DocumentEditSourceTracker, focusTime: number, actualTime: number) { const ranges = t.getTrackedRanges(); const keys = t.getAllKeys(); if (keys.length === 0) { @@ -198,7 +201,7 @@ class TrackedDocumentInfo extends Disposable { const deltaModifiedCount = t.getTotalInsertedCharactersCount(key); this._telemetryService.publicLog2<{ - mode: string; + mode: EditTelemetryMode; sourceKey: string; sourceKeyCleaned: string; @@ -206,7 +209,7 @@ class TrackedDocumentInfo extends Disposable { extensionVersion: string | undefined; modelId: string | undefined; - trigger: string; + trigger: EditTelemetryTrigger; languageId: string; statsUuid: string; modifiedCount: number; @@ -254,7 +257,7 @@ class TrackedDocumentInfo extends Disposable { const isTrackedByGit = await data.isTrackedByGit; this._telemetryService.publicLog2<{ - mode: string; + mode: EditTelemetryMode; languageId: string; statsUuid: string; nesModifiedCount: number; @@ -269,7 +272,7 @@ class TrackedDocumentInfo extends Disposable { isTrackedByGit: number; focusTime: number; actualTime: number; - trigger: string; + trigger: EditTelemetryTrigger; }, { owner: 'hediet'; comment: 'Aggregates character counts by edit source category (user typing, AI completions, NES, IDE actions, external changes) for each editing session. Sessions represent units of work and end when documents close, branches change, commits occur, or time limits are reached (5 minutes of wall-clock time, 20 minutes of focus time for visible documents, or 10 hours otherwise). Focus time is computed as accumulated 1-minute blocks where VS Code has focus and there was recent user activity. Tracks both total characters inserted and characters remaining at session end to measure retention. This high-level summary complements editSources.details which provides granular per-source breakdowns. @sentToGitHub'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 154ed6f780869..38c377fc8407b 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -35,7 +35,7 @@ import { IConfigurationService } from '../../../../platform/configuration/common import { IHostService } from '../../../services/host/browser/host.js'; import { URI } from '../../../../base/common/uri.js'; import { IExtension, ExtensionState, IExtensionsWorkbenchService, AutoUpdateConfigurationKey, AutoCheckUpdatesConfigurationKey, HasOutdatedExtensionsContext, AutoUpdateConfigurationValue, InstallExtensionOptions, ExtensionRuntimeState, ExtensionRuntimeActionType, AutoRestartConfigurationKey, VIEWLET_ID, IExtensionsViewPaneContainer, IExtensionsNotification } from '../common/extensions.js'; -import { IEditorService, MODAL_GROUP } from '../../../services/editor/common/editorService.js'; +import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from '../../../services/editor/common/editorService.js'; import { IURLService, IURLHandler, IOpenURLOptions } from '../../../../platform/url/common/url.js'; import { ExtensionsInput, IExtensionEditorOptions } from '../common/extensionsInput.js'; import { ILogService } from '../../../../platform/log/common/log.js'; @@ -1563,7 +1563,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension if (!extension) { throw new Error(`Extension not found. ${extension}`); } - await this.editorService.openEditor(this.instantiationService.createInstance(ExtensionsInput, extension), options, MODAL_GROUP); + await this.editorService.openEditor(this.instantiationService.createInstance(ExtensionsInput, extension), options, options?.sideByside ? SIDE_GROUP : ACTIVE_GROUP); } async openSearch(searchValue: string, preserveFoucs?: boolean): Promise { diff --git a/src/vs/workbench/contrib/mcp/browser/mcpCommandsAddConfiguration.ts b/src/vs/workbench/contrib/mcp/browser/mcpCommandsAddConfiguration.ts index 0f842a00418b5..d3a53876dc893 100644 --- a/src/vs/workbench/contrib/mcp/browser/mcpCommandsAddConfiguration.ts +++ b/src/vs/workbench/contrib/mcp/browser/mcpCommandsAddConfiguration.ts @@ -383,10 +383,10 @@ export class McpAddConfigurationCommand { }); const loadingAction = await new Promise<{ id: LoadAction; helpUri?: URI } | undefined>(resolve => { - loadingQuickPick.onDidAccept(() => resolve(loadingQuickPick.selectedItems[0])); - loadingQuickPick.onDidHide(() => resolve(undefined)); + loadingQuickPickStore.add(loadingQuickPick.onDidAccept(() => resolve(loadingQuickPick.selectedItems[0]))); + loadingQuickPickStore.add(loadingQuickPick.onDidHide(() => resolve(undefined))); loadingQuickPick.show(); - }).finally(() => loadingQuickPick.dispose()); + }).finally(() => loadingQuickPickStore.dispose()); switch (loadingAction?.id) { case LoadAction.Retry: diff --git a/src/vs/workbench/contrib/mcp/common/mcpRegistry.ts b/src/vs/workbench/contrib/mcp/common/mcpRegistry.ts index 084d2f5126ccd..f650edaec320a 100644 --- a/src/vs/workbench/contrib/mcp/common/mcpRegistry.ts +++ b/src/vs/workbench/contrib/mcp/common/mcpRegistry.ts @@ -408,13 +408,13 @@ export class McpRegistry extends Disposable implements IMcpRegistry { })); return new Promise(resolve => { - picker.onDidAccept(() => { + store.add(picker.onDidAccept(() => { resolve(picker.selectedItems.map(item => item.definitonId)); picker.hide(); - }); - picker.onDidHide(() => { + })); + store.add(picker.onDidHide(() => { resolve(undefined); - }); + })); picker.show(); }).finally(() => store.dispose()); } diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index c5a6ccbe54e06..f519bf0352a73 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -482,7 +482,7 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo if (!this.peek.get()) { const peek = this.instantiationService.createInstance(TestResultsPeek, this.editor); this.peek.set(peek, undefined); - peek.onDidClose(() => { + Event.once(peek.onDidClose)(() => { this.visible.set(false); this.peek.set(undefined, undefined); }); diff --git a/src/vs/workbench/contrib/testing/common/testingContentProvider.ts b/src/vs/workbench/contrib/testing/common/testingContentProvider.ts index dc5d6d8d017b3..4751f1847e2da 100644 --- a/src/vs/workbench/contrib/testing/common/testingContentProvider.ts +++ b/src/vs/workbench/contrib/testing/common/testingContentProvider.ts @@ -78,7 +78,7 @@ export class TestingContentProvider implements IWorkbenchContribution, ITextMode dispose.dispose(); } }); - model.onWillDispose(() => dispose.dispose()); + dispose.add(model.onWillDispose(() => dispose.dispose())); return model; } diff --git a/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts b/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts index 63e5c99bdcbda..defe9953b2e1b 100644 --- a/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts +++ b/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts @@ -488,12 +488,16 @@ declare module 'vscode' { } export class ChatResponsePullRequestPart { - readonly uri: Uri; + /** + * @deprecated use `command` instead + */ + readonly uri?: Uri; + readonly command: Command; readonly linkTag: string; readonly title: string; readonly description: string; readonly author: string; - constructor(uri: Uri, title: string, description: string, author: string, linkTag: string); + constructor(uriOrCommand: Uri | Command, title: string, description: string, author: string, linkTag: string); } export interface ChatResponseStream { diff --git a/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts b/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts index 9af69709a22ba..7b3e24ec14aba 100644 --- a/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts +++ b/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts @@ -132,6 +132,10 @@ declare module 'vscode' { * ChatRequestTurn + private additions. Note- at runtime this is the SAME as ChatRequestTurn and instanceof is safe. */ export class ChatRequestTurn2 { + /** + * The id of the chat request. Used to identity an interaction with any of the chat surfaces. + */ + readonly id?: string; /** * The prompt as entered by the user. * @@ -170,7 +174,7 @@ declare module 'vscode' { /** * @hidden */ - constructor(prompt: string, command: string | undefined, references: ChatPromptReference[], participant: string, toolReferences: ChatLanguageModelToolReference[], editedFileEvents: ChatRequestEditedFileEvent[] | undefined); + constructor(prompt: string, command: string | undefined, references: ChatPromptReference[], participant: string, toolReferences: ChatLanguageModelToolReference[], editedFileEvents: ChatRequestEditedFileEvent[] | undefined, id: string | undefined); } export class ChatResponseTurn2 {