chat: reduce mutability in ichatmodel (#278239)

refs https://github.com/microsoft/vscode/issues/277318
This commit is contained in:
Connor Peet
2025-11-19 11:11:47 -08:00
committed by GitHub
parent d9fd98fd18
commit 9cf56d4ac7
14 changed files with 109 additions and 82 deletions

View File

@@ -83,6 +83,7 @@ export const serverOptions: OptionDescriptions<Required<ServerParsedArgs>> = {
'enable-remote-auto-shutdown': { type: 'boolean' },
'remote-auto-shutdown-without-delay': { type: 'boolean' },
'inspect-ptyhost': { type: 'string', allowEmptyValue: true },
'use-host-proxy': { type: 'boolean' },
'without-browser-env-var': { type: 'boolean' },
@@ -212,6 +213,7 @@ export interface ServerParsedArgs {
'enable-remote-auto-shutdown'?: boolean;
'remote-auto-shutdown-without-delay'?: boolean;
'inspect-ptyhost'?: string;
'use-host-proxy'?: boolean;
'without-browser-env-var'?: boolean;

View File

@@ -3,31 +3,32 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Action2, MenuId, MenuItemAction } from '../../../../../platform/actions/common/actions.js';
import { IDisposable } from '../../../../../base/common/lifecycle.js';
import { ActionWidgetDropdownActionViewItem } from '../../../../../platform/actions/browser/actionWidgetDropdownActionViewItem.js';
import { IActionWidgetService } from '../../../../../platform/actionWidget/browser/actionWidget.js';
import { IActionWidgetDropdownAction, IActionWidgetDropdownActionProvider } from '../../../../../platform/actionWidget/browser/actionWidgetDropdown.js';
import { ContextKeyExpr, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js';
import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js';
import { IChatSessionsExtensionPoint, IChatSessionsService } from '../../common/chatSessionsService.js';
import { Codicon } from '../../../../../base/common/codicons.js';
import { AgentSessionProviders, getAgentSessionProviderIcon, getAgentSessionProviderName } from '../agentSessions/agentSessions.js';
import { localize, localize2 } from '../../../../../nls.js';
import { IDisposable } from '../../../../../base/common/lifecycle.js';
import { basename } from '../../../../../base/common/resources.js';
import { ThemeIcon } from '../../../../../base/common/themables.js';
import { URI } from '../../../../../base/common/uri.js';
import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js';
import { isITextModel } from '../../../../../editor/common/model.js';
import { localize, localize2 } from '../../../../../nls.js';
import { ActionWidgetDropdownActionViewItem } from '../../../../../platform/actions/browser/actionWidgetDropdownActionViewItem.js';
import { Action2, MenuId, MenuItemAction } from '../../../../../platform/actions/common/actions.js';
import { IActionWidgetService } from '../../../../../platform/actionWidget/browser/actionWidget.js';
import { IActionWidgetDropdownAction, IActionWidgetDropdownActionProvider } from '../../../../../platform/actionWidget/browser/actionWidgetDropdown.js';
import { ICommandService } from '../../../../../platform/commands/common/commands.js';
import { ContextKeyExpr, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js';
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js';
import { IEditorService } from '../../../../services/editor/common/editorService.js';
import { IChatAgentService } from '../../common/chatAgents.js';
import { ChatContextKeys } from '../../common/chatContextKeys.js';
import { ChatModel } from '../../common/chatModel.js';
import { ChatRequestParser } from '../../common/chatRequestParser.js';
import { IChatService } from '../../common/chatService.js';
import { IChatSessionsExtensionPoint, IChatSessionsService } from '../../common/chatSessionsService.js';
import { ChatAgentLocation } from '../../common/constants.js';
import { AgentSessionProviders, getAgentSessionProviderIcon, getAgentSessionProviderName } from '../agentSessions/agentSessions.js';
import { IChatWidgetService } from '../chat.js';
import { ThemeIcon } from '../../../../../base/common/themables.js';
import { ICommandService } from '../../../../../platform/commands/common/commands.js';
import { CHAT_SETUP_ACTION_ID } from './chatActions.js';
export class ContinueChatInSessionAction extends Action2 {
@@ -159,7 +160,8 @@ class CreateRemoteAgentJobAction {
if (!widget.viewModel) {
return;
}
const chatModel = widget.viewModel.model;
// todo@connor4312: remove 'as' cast
const chatModel = widget.viewModel.model as ChatModel;
if (!chatModel) {
return;
}

View File

@@ -12,7 +12,7 @@ import { MarkdownString } from '../../../../../base/common/htmlContent.js';
import { Iterable } from '../../../../../base/common/iterator.js';
import { Disposable, DisposableStore, dispose } from '../../../../../base/common/lifecycle.js';
import { ResourceMap } from '../../../../../base/common/map.js';
import { autorun, derived, IObservable, IReader, ITransaction, observableValue, transaction } from '../../../../../base/common/observable.js';
import { derived, IObservable, IReader, ITransaction, observableValue, transaction } from '../../../../../base/common/observable.js';
import { isEqual } from '../../../../../base/common/resources.js';
import { hasKey, Mutable } from '../../../../../base/common/types.js';
import { URI } from '../../../../../base/common/uri.js';
@@ -39,7 +39,7 @@ import { CellUri, ICellEditOperation } from '../../../notebook/common/notebookCo
import { INotebookService } from '../../../notebook/common/notebookService.js';
import { chatEditingSessionIsReady, ChatEditingSessionState, ChatEditKind, getMultiDiffSourceUri, IChatEditingSession, IModifiedEntryTelemetryInfo, IModifiedFileEntry, ISnapshotEntry, IStreamingEdits, ModifiedFileEntryState } from '../../common/chatEditingService.js';
import { IChatResponseModel } from '../../common/chatModel.js';
import { IChatProgress, IChatService } from '../../common/chatService.js';
import { IChatProgress } from '../../common/chatService.js';
import { ChatAgentLocation } from '../../common/constants.js';
import { IChatEditingCheckpointTimeline } from './chatEditingCheckpointTimeline.js';
import { ChatEditingCheckpointTimelineImpl, IChatEditingTimelineFsDelegate } from './chatEditingCheckpointTimelineImpl.js';
@@ -165,6 +165,10 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
public readonly canUndo: IObservable<boolean>;
public readonly canRedo: IObservable<boolean>;
public get requestDisablement() {
return this._timeline.requestDisablement;
}
private readonly _onDidDispose = new Emitter<void>();
get onDidDispose() {
this._assertNotDisposed();
@@ -183,7 +187,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
@IBulkEditService public readonly _bulkEditService: IBulkEditService,
@IEditorGroupsService private readonly _editorGroupsService: IEditorGroupsService,
@IEditorService private readonly _editorService: IEditorService,
@IChatService private readonly _chatService: IChatService,
@INotebookService private readonly _notebookService: INotebookService,
@IAccessibilitySignalService private readonly _accessibilitySignalService: IAccessibilitySignalService,
@ILogService private readonly _logService: ILogService,
@@ -200,11 +203,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
this.canUndo = this._timeline.canUndo.map((hasHistory, reader) =>
hasHistory && this._state.read(reader) === ChatEditingSessionState.Idle);
this._register(autorun(reader => {
const disabled = this._timeline.requestDisablement.read(reader);
this._chatService.getSession(this.chatSessionResource)?.setDisabledRequests(disabled);
}));
this._init(transferFrom);
}
@@ -447,9 +445,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
override dispose() {
this._assertNotDisposed();
this._chatService.cancelCurrentRequestForSession(this.chatSessionResource);
dispose(this._entriesObs.get());
super.dispose();
this._state.set(ChatEditingSessionState.Disposed, undefined);

View File

@@ -26,6 +26,7 @@ import { IEditorGroup } from '../../../services/editor/common/editorGroupsServic
import { ChatContextKeys } from '../common/chatContextKeys.js';
import { IChatModel, IExportableChatData, ISerializableChatData } from '../common/chatModel.js';
import { CHAT_PROVIDER_ID } from '../common/chatParticipantContribTypes.js';
import { IChatService } from '../common/chatService.js';
import { IChatSessionsService, localChatSessionType } from '../common/chatSessionsService.js';
import { ChatAgentLocation, ChatModeKind } from '../common/constants.js';
import { clearChatEditor } from './actions/chatClear.js';
@@ -65,6 +66,7 @@ export class ChatEditor extends EditorPane {
@IStorageService private readonly storageService: IStorageService,
@IChatSessionsService private readonly chatSessionsService: IChatSessionsService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IChatService private readonly chatService: IChatService,
) {
super(ChatEditorInput.EditorID, group, telemetryService, themeService, storageService);
}
@@ -232,8 +234,8 @@ export class ChatEditor extends EditorPane {
const viewState = options?.viewState ?? input.options.viewState;
this.updateModel(editorModel.model, viewState);
if (isContributedChatSession && options?.title?.preferred) {
editorModel.model.setCustomTitle(options.title.preferred);
if (isContributedChatSession && options?.title?.preferred && input.sessionResource) {
this.chatService.setChatSessionTitle(input.sessionResource, options.title.preferred);
}
} catch (error) {
this.hideLoadingInChatWidget();

View File

@@ -1784,7 +1784,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
if (isRequestVM(currentElement) && !this.viewModel?.editing) {
const requests = this.viewModel?.model.getRequests();
if (!requests) {
if (!requests || !this.viewModel?.sessionResource) {
return;
}

View File

@@ -306,7 +306,7 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo
IChatToolInvocation.confirmWith(toolInvocation, autoConfirmed);
}
model.acceptResponseProgress(request, toolInvocation);
this._chatService.appendProgress(request, toolInvocation);
dto.toolSpecificData = toolInvocation?.toolSpecificData;
if (preparedInvocation?.confirmationMessages?.title) {

View File

@@ -20,7 +20,7 @@ import { createDecorator } from '../../../../platform/instantiation/common/insta
import { IEditorPane } from '../../../common/editor.js';
import { ICellEditOperation } from '../../notebook/common/notebookCommon.js';
import { IChatAgentResult } from './chatAgents.js';
import { ChatModel, IChatResponseModel } from './chatModel.js';
import { ChatModel, IChatRequestDisablement, IChatResponseModel } from './chatModel.js';
import { IChatProgress } from './chatService.js';
export const IChatEditingService = createDecorator<IChatEditingService>('chatEditingService');
@@ -117,6 +117,9 @@ export interface IChatEditingSession extends IDisposable {
readonly onDidDispose: Event<void>;
readonly state: IObservable<ChatEditingSessionState>;
readonly entries: IObservable<readonly IModifiedFileEntry[]>;
/** Requests disabled by undo/redo in the session */
readonly requestDisablement: IObservable<IChatRequestDisablement[]>;
show(previousChanges?: boolean): Promise<void>;
accept(...uris: URI[]): Promise<void>;
reject(...uris: URI[]): Promise<void>;

View File

@@ -13,7 +13,7 @@ import { ResourceMap } from '../../../../base/common/map.js';
import { revive } from '../../../../base/common/marshalling.js';
import { Schemas } from '../../../../base/common/network.js';
import { equals } from '../../../../base/common/objects.js';
import { IObservable, autorunSelfDisposable, observableFromEvent, observableSignalFromEvent } from '../../../../base/common/observable.js';
import { IObservable, autorun, autorunSelfDisposable, observableFromEvent, observableSignalFromEvent } from '../../../../base/common/observable.js';
import { basename, isEqual } from '../../../../base/common/resources.js';
import { ThemeIcon } from '../../../../base/common/themables.js';
import { URI, UriComponents, UriDto, isUriComponents } from '../../../../base/common/uri.js';
@@ -216,9 +216,10 @@ export interface IChatResponseModel {
export type ChatResponseModelChangeReason =
| { reason: 'other' }
| { reason: 'completedRequest' }
| { reason: 'undoStop'; id: string };
const defaultChatResponseModelChangeReason: ChatResponseModelChangeReason = { reason: 'other' };
export const defaultChatResponseModelChangeReason: ChatResponseModelChangeReason = { reason: 'other' };
export interface IChatRequestModeInfo {
kind: ChatModeKind | undefined; // is undefined in case of modeId == 'apply'
@@ -1011,13 +1012,13 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
}
this._isComplete = true;
this._onDidChange.fire(defaultChatResponseModelChangeReason);
this._onDidChange.fire({ reason: 'completedRequest' });
}
cancel(): void {
this._isComplete = true;
this._isCanceled = true;
this._onDidChange.fire(defaultChatResponseModelChangeReason);
this._onDidChange.fire({ reason: 'completedRequest' });
}
setFollowups(followups: IChatFollowup[] | undefined): void {
@@ -1082,24 +1083,13 @@ export interface IChatModel extends IDisposable {
readonly requestNeedsInput: IObservable<boolean>;
readonly inputPlaceholder?: string;
readonly editingSession?: IChatEditingSession | undefined;
/**
* Sets requests as 'disabled', removing them from the UI. If a request ID
* is given without undo stops, it's removed entirely. If an undo stop
* is given, all content after that stop is removed.
*/
setDisabledRequests(requestIds: IChatRequestDisablement[]): void;
readonly checkpoint: IChatRequestModel | undefined;
getRequests(): IChatRequestModel[];
setCheckpoint(requestId: string | undefined): void;
readonly checkpoint: IChatRequestModel | undefined;
addRequest(message: IParsedChatRequest, variableData: IChatRequestVariableData, attempt: number, modeInfo?: IChatRequestModeInfo, chatAgent?: IChatAgentData, slashCommand?: IChatAgentCommand, confirmation?: string, locationData?: IChatLocationData, attachments?: IChatRequestVariableEntry[], isCompleteAddedRequest?: boolean, modelId?: string, userSelectedTools?: UserSelectedTools): IChatRequestModel;
acceptResponseProgress(request: IChatRequestModel, progress: IChatProgress, quiet?: boolean): void;
setResponse(request: IChatRequestModel, result: IChatAgentResult): void;
completeResponse(request: IChatRequestModel): void;
setCustomTitle(title: string): void;
toExport(): IExportableChatData;
toJSON(): ISerializableChatData;
readonly contributedChatSession: IChatSessionContext | undefined;
setContributedChatSession(session: IChatSessionContext | undefined): void;
}
export interface ISerializableChatsData {
@@ -1320,7 +1310,6 @@ export interface IChatRemoveRequestEvent {
export interface IChatSetHiddenEvent {
kind: 'setHidden';
hiddenRequestIds: readonly IChatRequestDisablement[];
}
export interface IChatMoveEvent {
@@ -1482,25 +1471,42 @@ export class ChatModel extends Disposable implements IChatModel {
this._initialLocation = initialData?.initialLocation ?? initialModelProps.initialLocation;
this._canUseTools = initialModelProps.canUseTools;
const lastResponse = observableFromEvent(this, this.onDidChange, () => this._requests.at(-1)?.response);
const lastRequest = observableFromEvent(this, this.onDidChange, () => this._requests.at(-1));
this.requestInProgress = lastResponse.map((response, r) => {
return response?.isInProgress.read(r) ?? false;
this._register(autorun(reader => {
const request = lastRequest.read(reader);
if (!request?.response) {
return;
}
reader.store.add(request.response.onDidChange(ev => {
if (ev.reason === 'completedRequest') {
this._onDidChange.fire({ kind: 'completedRequest', request });
}
}));
}));
this.requestInProgress = lastRequest.map((request, r) => {
return request?.response?.isInProgress.read(r) ?? false;
});
this.requestNeedsInput = lastResponse.map((response, r) => {
return response?.isPendingConfirmation.read(r) ?? false;
this.requestNeedsInput = lastRequest.map((request, r) => {
return request?.response?.isPendingConfirmation.read(r) ?? false;
});
}
startEditingSession(isGlobalEditingSession?: boolean, transferFromSession?: IChatEditingSession): void {
this._editingSession ??= this._register(
const session = this._editingSession ??= this._register(
transferFromSession
? this.chatEditingService.transferEditingSession(this, transferFromSession)
: isGlobalEditingSession
? this.chatEditingService.startOrContinueGlobalEditingSession(this)
: this.chatEditingService.createEditingSession(this)
);
this._register(autorun(reader => {
this._setDisabledRequests(session.requestDisablement.read(reader));
}));
}
private currentEditedFileEvents = new ResourceMap<IChatAgentEditedFileEvent>();
@@ -1678,7 +1684,7 @@ export class ChatModel extends Disposable implements IChatModel {
return this._checkpoint;
}
setDisabledRequests(requestIds: IChatRequestDisablement[]) {
private _setDisabledRequests(requestIds: IChatRequestDisablement[]) {
this._requests.forEach((request) => {
const shouldBeRemovedOnSend = requestIds.find(r => r.requestId === request.id);
request.shouldBeRemovedOnSend = shouldBeRemovedOnSend;
@@ -1687,10 +1693,7 @@ export class ChatModel extends Disposable implements IChatModel {
}
});
this._onDidChange.fire({
kind: 'setHidden',
hiddenRequestIds: requestIds,
});
this._onDidChange.fire({ kind: 'setHidden' });
}
addRequest(message: IParsedChatRequest, variableData: IChatRequestVariableData, attempt: number, modeInfo?: IChatRequestModeInfo, chatAgent?: IChatAgentData, slashCommand?: IChatAgentCommand, confirmation?: string, locationData?: IChatLocationData, attachments?: IChatRequestVariableEntry[], isCompleteAddedRequest?: boolean, modelId?: string, userSelectedTools?: UserSelectedTools): ChatRequestModel {
@@ -1770,7 +1773,6 @@ export class ChatModel extends Disposable implements IChatModel {
throw new Error('acceptResponseProgress: Adding progress to a completed response');
}
if (progress.kind === 'usedContext' || progress.kind === 'reference') {
request.response.applyReference(progress);
} else if (progress.kind === 'codeCitation') {
@@ -1818,15 +1820,6 @@ export class ChatModel extends Disposable implements IChatModel {
request.response.setResult(result);
}
completeResponse(request: ChatRequestModel): void {
if (!request.response) {
throw new Error('Call setResponse before completeResponse');
}
request.response.complete();
this._onDidChange.fire({ kind: 'completedRequest', request });
}
setFollowups(request: ChatRequestModel, followups: IChatFollowup[] | undefined): void {
if (!request.response) {
// Maybe something went wrong?

View File

@@ -959,6 +959,11 @@ export interface IChatService {
*/
sendRequest(sessionResource: URI, message: string, options?: IChatSendRequestOptions): Promise<IChatSendRequestData | undefined>;
/**
* Sets a custom title for a chat model.
*/
setTitle(sessionResource: URI, title: string): void;
appendProgress(request: IChatRequestModel, progress: IChatProgress): void;
resendRequest(request: IChatRequestModel, options?: IChatSendRequestOptions): Promise<void>;
adoptRequest(sessionResource: URI, request: IChatRequestModel): Promise<void>;
removeRequest(sessionResource: URI, requestId: string): Promise<void>;

View File

@@ -6,7 +6,7 @@
import { DeferredPromise } from '../../../../base/common/async.js';
import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js';
import { toErrorMessage } from '../../../../base/common/errorMessage.js';
import { ErrorNoTelemetry } from '../../../../base/common/errors.js';
import { BugIndicatingError, ErrorNoTelemetry } from '../../../../base/common/errors.js';
import { Emitter, Event } from '../../../../base/common/event.js';
import { MarkdownString } from '../../../../base/common/htmlContent.js';
import { Iterable } from '../../../../base/common/iterator.js';
@@ -592,7 +592,7 @@ export class ChatService extends Disposable implements IChatService {
for (const message of providedSession.history) {
if (message.type === 'request') {
if (lastRequest) {
model.completeResponse(lastRequest);
lastRequest.response?.complete();
}
const requestText = message.prompt;
@@ -666,13 +666,13 @@ export class ChatService extends Disposable implements IChatService {
// Handle completion
if (isComplete) {
model?.completeResponse(lastRequest);
lastRequest.response?.complete();
cancellationListener.clear();
}
}));
} else {
if (lastRequest) {
model.completeResponse(lastRequest);
lastRequest.response?.complete();
}
}
@@ -1051,7 +1051,7 @@ export class ChatService extends Disposable implements IChatService {
completeResponseCreated();
this.trace('sendRequest', `Provider returned response for session ${model.sessionResource}`);
model.completeResponse(request);
request.response?.complete();
if (agentOrCommandFollowups) {
agentOrCommandFollowups.then(followups => {
model.setFollowups(request, followups);
@@ -1079,7 +1079,7 @@ export class ChatService extends Disposable implements IChatService {
const rawResult: IChatAgentResult = { errorDetails: { message: err.message } };
model.setResponse(request, rawResult);
completeResponseCreated();
model.completeResponse(request);
request.response?.complete();
}
} finally {
store.dispose();
@@ -1216,7 +1216,7 @@ export class ChatService extends Disposable implements IChatService {
if (response.followups !== undefined) {
model.setFollowups(request, response.followups);
}
model.completeResponse(request);
request.response?.complete();
}
cancelCurrentRequestForSession(sessionResource: URI): void {
@@ -1282,6 +1282,19 @@ export class ChatService extends Disposable implements IChatService {
this._chatSessionStore.logIndex();
}
setTitle(sessionResource: URI, title: string): void {
this._sessionModels.get(sessionResource)?.setCustomTitle(title);
}
appendProgress(request: IChatRequestModel, progress: IChatProgress): void {
const model = this._sessionModels.get(request.session.sessionResource);
if (!(request instanceof ChatRequestModel)) {
throw new BugIndicatingError('Can only append progress to requests of type ChatRequestModel');
}
model?.acceptResponseProgress(request, progress);
}
private toLocalSessionId(sessionResource: URI) {
const localSessionId = LocalChatSessionUri.parseLocalSessionId(sessionResource);
if (!localSessionId) {

View File

@@ -21,7 +21,7 @@ import { ExtensionIdentifier } from '../../../../../platform/extensions/common/e
import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js';
import { workbenchInstantiationService } from '../../../../test/browser/workbenchTestServices.js';
import { LanguageModelToolsService } from '../../browser/languageModelToolsService.js';
import { IChatModel } from '../../common/chatModel.js';
import { ChatModel, IChatModel } from '../../common/chatModel.js';
import { IChatService, IChatToolInputInvocationData, IChatToolInvocation, ToolConfirmKind } from '../../common/chatService.js';
import { ChatConfiguration } from '../../common/constants.js';
import { GithubCopilotToolReference, isToolResultInputOutputDetails, IToolData, IToolImpl, IToolInvocation, ToolDataSource, ToolSet, VSCodeToolReference } from '../../common/languageModelToolsService.js';
@@ -89,9 +89,12 @@ function stubGetSession(chatService: MockChatService, sessionId: string, options
sessionId,
sessionResource: LocalChatSessionUri.forSession(sessionId),
getRequests: () => [{ id: requestId, modelId: 'test-model' }],
acceptResponseProgress: (_req: any, progress: any) => { if (capture) { capture.invocation = progress; } },
} as IChatModel;
} as ChatModel;
chatService.addSession(fakeModel);
chatService.appendProgress = (request, progress) => {
if (capture) { capture.invocation = progress; }
};
return fakeModel;
}

View File

@@ -7,7 +7,7 @@ import assert from 'assert';
import { CancellationToken } from '../../../../../base/common/cancellation.js';
import { Event } from '../../../../../base/common/event.js';
import { MarkdownString } from '../../../../../base/common/htmlContent.js';
import { Disposable } from '../../../../../base/common/lifecycle.js';
import { observableValue } from '../../../../../base/common/observable.js';
import { URI } from '../../../../../base/common/uri.js';
import { assertSnapshot } from '../../../../../base/test/common/snapshot.js';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
@@ -147,7 +147,10 @@ suite('ChatService', () => {
instantiationService.stub(ILifecycleService, { onWillShutdown: Event.None });
instantiationService.stub(IChatEditingService, new class extends mock<IChatEditingService>() {
override startOrContinueGlobalEditingSession(): IChatEditingSession {
return Disposable.None as IChatEditingSession;
return {
requestDisablement: observableValue('requestDisablement', []),
dispose: () => { }
} as unknown as IChatEditingSession;
}
});

View File

@@ -10,7 +10,7 @@ import { observableValue } from '../../../../../base/common/observable.js';
import { URI } from '../../../../../base/common/uri.js';
import { ChatModel, IChatModel, IChatRequestModel, IChatRequestVariableData, ISerializableChatData } from '../../common/chatModel.js';
import { IParsedChatRequest } from '../../common/chatParserTypes.js';
import { IChatCompleteResponse, IChatDetail, IChatProviderInfo, IChatSendRequestData, IChatSendRequestOptions, IChatService, IChatSessionContext, IChatTransferredSessionData, IChatUserActionEvent } from '../../common/chatService.js';
import { IChatCompleteResponse, IChatDetail, IChatProgress, IChatProviderInfo, IChatSendRequestData, IChatSendRequestOptions, IChatService, IChatSessionContext, IChatTransferredSessionData, IChatUserActionEvent } from '../../common/chatService.js';
import { ChatAgentLocation } from '../../common/constants.js';
export class MockChatService implements IChatService {
@@ -53,6 +53,12 @@ export class MockChatService implements IChatService {
}
loadSessionForResource(resource: URI, position: ChatAgentLocation, token: CancellationToken): Promise<IChatModel | undefined> {
throw new Error('Method not implemented.');
}
setTitle(sessionResource: URI, title: string): void {
throw new Error('Method not implemented.');
}
appendProgress(request: IChatRequestModel, progress: IChatProgress): void {
}
/**
* Returns whether the request was accepted.

View File

@@ -1616,7 +1616,7 @@ export async function reviewEdits(accessor: ServicesAccessor, editor: ICodeEdito
chatRequest.response.updateContent({ kind: 'textEdit', uri, edits: [], done: true });
if (!token.isCancellationRequested) {
chatModel.completeResponse(chatRequest);
chatRequest.response.complete();
}
const isSettled = derived(r => {