diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index 98e678996e5..3c368bf8774 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -28,6 +28,9 @@ const _allApiProposals = { canonicalUriProvider: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.canonicalUriProvider.d.ts', }, + chatEditing: { + proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatEditing.d.ts', + }, chatParticipantAdditions: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts', }, diff --git a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts index 5de097d706d..ca1cc0c6041 100644 --- a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts +++ b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts @@ -25,6 +25,7 @@ import { IChatWidgetService } from '../../contrib/chat/browser/chat.js'; import { ChatInputPart } from '../../contrib/chat/browser/chatInputPart.js'; import { AddDynamicVariableAction, IAddDynamicVariableContext } from '../../contrib/chat/browser/contrib/chatDynamicVariables.js'; import { ChatAgentLocation, IChatAgentHistoryEntry, IChatAgentImplementation, IChatAgentRequest, IChatAgentService } from '../../contrib/chat/common/chatAgents.js'; +import { IChatEditingService } from '../../contrib/chat/common/chatEditingService.js'; import { ChatRequestAgentPart } from '../../contrib/chat/common/chatParserTypes.js'; import { ChatRequestParser } from '../../contrib/chat/common/chatRequestParser.js'; import { IChatContentInlineReference, IChatContentReference, IChatFollowup, IChatProgress, IChatService, IChatTask, IChatWarningMessage } from '../../contrib/chat/common/chatService.js'; @@ -79,6 +80,8 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA private readonly _chatParticipantDetectionProviders = this._register(new DisposableMap()); + private readonly _chatRelatedFilesProviders = this._register(new DisposableMap()); + private readonly _pendingProgress = new Map void>(); private readonly _proxy: ExtHostChatAgentsShape2; @@ -91,6 +94,7 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA extHostContext: IExtHostContext, @IChatAgentService private readonly _chatAgentService: IChatAgentService, @IChatService private readonly _chatService: IChatService, + @IChatEditingService private readonly _chatEditingService: IChatEditingService, @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @@ -342,6 +346,18 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA $unregisterChatParticipantDetectionProvider(handle: number): void { this._chatParticipantDetectionProviders.deleteAndDispose(handle); } + + $registerRelatedFilesProvider(handle: number): void { + this._chatRelatedFilesProviders.set(handle, this._chatEditingService.registerRelatedFilesProvider(handle, { + provideRelatedFiles: async (request, token) => { + return (await this._proxy.$provideRelatedFiles(handle, request, token))?.map((v) => URI.from(v)); + } + })); + } + + $unregisterRelatedFilesProvider(handle: number): void { + this._chatRelatedFilesProviders.deleteAndDispose(handle); + } } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 9c2c8edabd0..81ba5af6ab3 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1450,6 +1450,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension, 'chatParticipantAdditions'); return extHostChatAgents2.registerChatParticipantDetectionProvider(extension, provider); }, + registerRelatedFilesProvider(provider: vscode.ChatRelatedFilesProvider) { + checkProposedApiEnabled(extension, 'chatEditing'); + return extHostChatAgents2.registerRelatedFilesProvider(extension, provider); + } }; // namespace: lm diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 0369719ee26..6f952af7b1c 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -52,6 +52,7 @@ import { IRevealOptions, ITreeItem, IViewBadge } from '../../common/views.js'; import { CallHierarchyItem } from '../../contrib/callHierarchy/common/callHierarchy.js'; import { ChatAgentLocation, IChatAgentMetadata, IChatAgentRequest, IChatAgentResult, IChatWelcomeMessageContent } from '../../contrib/chat/common/chatAgents.js'; import { ICodeMapperRequest, ICodeMapperResult } from '../../contrib/chat/common/chatCodeMapperService.js'; +import { IChatRequestDraft } from '../../contrib/chat/common/chatEditingService.js'; import { IChatProgressHistoryResponseContent } from '../../contrib/chat/common/chatModel.js'; import { IChatContentInlineReference, IChatFollowup, IChatProgress, IChatResponseErrorDetails, IChatTask, IChatTaskDto, IChatUserActionEvent, IChatVoteAction } from '../../contrib/chat/common/chatService.js'; import { IChatRequestVariableValue, IChatVariableData, IChatVariableResolverProgress } from '../../contrib/chat/common/chatVariables.js'; @@ -1274,6 +1275,8 @@ export interface MainThreadChatAgentsShape2 extends IDisposable { $registerAgent(handle: number, extension: ExtensionIdentifier, id: string, metadata: IExtensionChatAgentMetadata, dynamicProps: IDynamicChatAgentProps | undefined): void; $registerChatParticipantDetectionProvider(handle: number): void; $unregisterChatParticipantDetectionProvider(handle: number): void; + $registerRelatedFilesProvider(handle: number): void; + $unregisterRelatedFilesProvider(handle: number): void; $registerAgentCompletionsProvider(handle: number, id: string, triggerCharacters: string[]): void; $unregisterAgentCompletionsProvider(handle: number, id: string): void; $updateAgent(handle: number, metadataUpdate: IExtensionChatAgentMetadata): void; @@ -1331,6 +1334,7 @@ export interface ExtHostChatAgentsShape2 { $provideSampleQuestions(handle: number, location: ChatAgentLocation, token: CancellationToken): Promise; $releaseSession(sessionId: string): void; $detectChatParticipant(handle: number, request: Dto, context: { history: IChatAgentHistoryEntryDto[] }, options: { participants: IChatParticipantMetadata[]; location: ChatAgentLocation }, token: CancellationToken): Promise; + $provideRelatedFiles(handle: number, request: Dto, token: CancellationToken): Promise; } export interface IChatParticipantMetadata { participant: string; diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index 5735a7759ba..ba7ea591808 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -31,6 +31,7 @@ import { ExtHostLanguageModels } from './extHostLanguageModels.js'; import * as typeConvert from './extHostTypeConverters.js'; import * as extHostTypes from './extHostTypes.js'; import { isChatViewTitleActionContext } from '../../contrib/chat/common/chatActions.js'; +import { IChatRequestDraft } from '../../contrib/chat/common/chatEditingService.js'; class ChatAgentResponseStream { @@ -305,6 +306,9 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS private static _participantDetectionProviderIdPool = 0; private readonly _participantDetectionProviders = new Map(); + private static _relatedFilesProviderIdPool = 0; + private readonly _relatedFilesProviders = new Map(); + private readonly _sessionDisposables: DisposableMap = this._register(new DisposableMap()); private readonly _completionDisposables: DisposableMap = this._register(new DisposableMap()); @@ -362,6 +366,26 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS }); } + registerRelatedFilesProvider(extension: IExtensionDescription, provider: vscode.ChatRelatedFilesProvider): vscode.Disposable { + const handle = ExtHostChatAgents2._relatedFilesProviderIdPool++; + this._relatedFilesProviders.set(handle, new ExtHostRelatedFilesProvider(extension, provider)); + this._proxy.$registerRelatedFilesProvider(handle); + return toDisposable(() => { + this._relatedFilesProviders.delete(handle); + this._proxy.$unregisterRelatedFilesProvider(handle); + }); + } + + async $provideRelatedFiles(handle: number, request: IChatRequestDraft, token: CancellationToken): Promise { + const provider = this._relatedFilesProviders.get(handle); + if (!provider) { + return Promise.resolve([]); + } + + const extRequestDraft = typeConvert.ChatRequestDraft.to(request); + return await provider.provider.provideRelatedFiles(extRequestDraft, token) ?? undefined; + } + async $detectChatParticipant(handle: number, requestDto: Dto, context: { history: IChatAgentHistoryEntryDto[] }, options: { location: ChatAgentLocation; participants?: vscode.ChatParticipantMetadata[] }, token: CancellationToken): Promise { const { request, location, history } = await this._createRequest(requestDto, context); @@ -639,6 +663,13 @@ class ExtHostParticipantDetector { ) { } } +class ExtHostRelatedFilesProvider { + constructor( + public readonly extension: IExtensionDescription, + public readonly provider: vscode.ChatRelatedFilesProvider, + ) { } +} + class ExtHostChatAgent { private _followupProvider: vscode.ChatFollowupProvider | undefined; diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 3c4c3a842d8..f33ea98cf4f 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -57,6 +57,7 @@ import * as types from './extHostTypes.js'; import { IChatResponseTextPart, IChatResponsePromptTsxPart } from '../../contrib/chat/common/languageModels.js'; import { LanguageModelTextPart, LanguageModelPromptTsxPart } from './extHostTypes.js'; import { MarshalledId } from '../../../base/common/marshallingIds.js'; +import { IChatRequestDraft } from '../../contrib/chat/common/chatEditingService.js'; export namespace Command { @@ -2797,6 +2798,15 @@ export namespace ChatAgentRequest { } } +export namespace ChatRequestDraft { + export function to(request: IChatRequestDraft): vscode.ChatRequestDraft { + return { + prompt: request.prompt, + files: request.files.map((uri) => URI.revive(uri)) + }; + } +} + export namespace ChatLocation { export function to(loc: ChatAgentLocation): types.ChatLocation { switch (loc) { diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts index 57585bd074d..a83688f8cdd 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts @@ -9,7 +9,7 @@ import { CancellationToken, CancellationTokenSource } from '../../../../../base/ import { Codicon } from '../../../../../base/common/codicons.js'; import { BugIndicatingError } from '../../../../../base/common/errors.js'; import { Emitter, Event } from '../../../../../base/common/event.js'; -import { Disposable, DisposableStore, IDisposable } from '../../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; import { ResourceMap } from '../../../../../base/common/map.js'; import { derived, IObservable, observableValue, runOnChange, ValueWithChangeEventFromObservable } from '../../../../../base/common/observable.js'; import { compare } from '../../../../../base/common/strings.js'; @@ -34,7 +34,7 @@ import { MultiDiffEditorInput } from '../../../multiDiffEditor/browser/multiDiff import { IMultiDiffSourceResolver, IMultiDiffSourceResolverService, IResolvedMultiDiffSource, MultiDiffEditorItem } from '../../../multiDiffEditor/browser/multiDiffSourceResolverService.js'; import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.js'; import { ChatContextKeys } from '../../common/chatContextKeys.js'; -import { applyingChatEditsContextKey, applyingChatEditsFailedContextKey, CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME, chatEditingMaxFileAssignmentName, chatEditingResourceContextKey, ChatEditingSessionState, decidedChatEditingResourceContextKey, defaultChatEditingMaxFileLimit, hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, IChatEditingService, IChatEditingSession, IChatEditingSessionStream, IModifiedFileEntry, inChatEditingSessionContextKey, WorkingSetEntryState } from '../../common/chatEditingService.js'; +import { applyingChatEditsContextKey, applyingChatEditsFailedContextKey, CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME, chatEditingMaxFileAssignmentName, chatEditingResourceContextKey, ChatEditingSessionState, decidedChatEditingResourceContextKey, defaultChatEditingMaxFileLimit, hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, IChatEditingService, IChatEditingSession, IChatEditingSessionStream, IChatRelatedFilesProvider, IModifiedFileEntry, inChatEditingSessionContextKey, WorkingSetEntryState } from '../../common/chatEditingService.js'; import { IChatResponseModel, IChatTextEditGroup } from '../../common/chatModel.js'; import { IChatService } from '../../common/chatService.js'; import { ChatEditingSession } from './chatEditingSession.js'; @@ -76,6 +76,8 @@ export class ChatEditingService extends Disposable implements IChatEditingServic private _applyingChatEditsFailedContextKey: IContextKey; + private _chatRelatedFilesProviders = new Map(); + constructor( @IEditorGroupsService private readonly _editorGroupsService: IEditorGroupsService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @@ -384,6 +386,13 @@ export class ChatEditingService extends Disposable implements IChatEditingServic } return editors; } + + registerRelatedFilesProvider(handle: number, provider: IChatRelatedFilesProvider): IDisposable { + this._chatRelatedFilesProviders.set(handle, provider); + return toDisposable(() => { + this._chatRelatedFilesProviders.delete(handle); + }); + } } /** diff --git a/src/vs/workbench/contrib/chat/common/chatAgents.ts b/src/vs/workbench/contrib/chat/common/chatAgents.ts index dcbaf09dbbb..b8c595115e5 100644 --- a/src/vs/workbench/contrib/chat/common/chatAgents.ts +++ b/src/vs/workbench/contrib/chat/common/chatAgents.ts @@ -251,6 +251,8 @@ export class ChatAgentService extends Disposable implements IChatAgentService { private readonly _defaultAgentRegistered: IContextKey; private readonly _editingAgentRegistered: IContextKey; + private _chatParticipantDetectionProviders = new Map(); + constructor( @IContextKeyService private readonly contextKeyService: IContextKeyService, ) { @@ -481,7 +483,6 @@ export class ChatAgentService extends Disposable implements IChatAgentService { return data.impl.provideChatTitle(history, token); } - private _chatParticipantDetectionProviders = new Map(); registerChatParticipantDetectionProvider(handle: number, provider: IChatParticipantDetectionProvider) { this._chatParticipantDetectionProviders.set(handle, provider); return toDisposable(() => { diff --git a/src/vs/workbench/contrib/chat/common/chatEditingService.ts b/src/vs/workbench/contrib/chat/common/chatEditingService.ts index bc7ff4118f4..365cecfdf6c 100644 --- a/src/vs/workbench/contrib/chat/common/chatEditingService.ts +++ b/src/vs/workbench/contrib/chat/common/chatEditingService.ts @@ -3,8 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationTokenSource } from '../../../../base/common/cancellation.js'; +import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; import { Event } from '../../../../base/common/event.js'; +import { IDisposable } from '../../../../base/common/lifecycle.js'; import { ResourceMap } from '../../../../base/common/map.js'; import { IObservable, ITransaction } from '../../../../base/common/observable.js'; import { URI } from '../../../../base/common/uri.js'; @@ -40,6 +41,17 @@ export interface IChatEditingService { createSnapshot(requestId: string): void; getSnapshotUri(requestId: string, uri: URI): URI | undefined; restoreSnapshot(requestId: string | undefined): Promise; + + registerRelatedFilesProvider(handle: number, provider: IChatRelatedFilesProvider): IDisposable; +} + +export interface IChatRequestDraft { + readonly prompt: string; + readonly files: readonly URI[]; +} + +export interface IChatRelatedFilesProvider { + provideRelatedFiles(chatRequest: IChatRequestDraft, token: CancellationToken): Promise; } export interface IChatEditingSession { diff --git a/src/vscode-dts/vscode.proposed.chatEditing.d.ts b/src/vscode-dts/vscode.proposed.chatEditing.d.ts new file mode 100644 index 00000000000..7c57f38bef1 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.chatEditing.d.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + export interface ChatRequestDraft { + readonly prompt: string; + readonly files: readonly Uri[]; + } + + export interface ChatRelatedFilesProvider { + provideRelatedFiles(chatRequest: ChatRequestDraft, token: CancellationToken): ProviderResult; + } + + export namespace chat { + export function registerRelatedFilesProvider(provider: ChatRelatedFilesProvider): Disposable; + } +}