chatServiceImpl.ts ×75

Frontier kind: Code frontier

unlabeled · c_fab920592840

76 tests · 68617 LOC · 309 files · introduces 0 tests · 1063 LOC · 11 files

Introduces — evidence that enters the hierarchy at this concept

Code
130 ranges1063 lines · 11 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
5230 ranges68617 lines · 309 files · Browse complete extent
All tests (intent)
76 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

11 files ranked by introduced lines: 1063 introduced LOC across 130 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/contrib/chat/common/chatService/chatServiceImpl.ts 349 introduced LOC · 75 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- chatServiceImpl.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { DeferredPromise, raceCancellationError, raceTimeout } from '../../../../../base/common/async.js';
7 > import { CancellationToken, CancellationTokenSource } from '../../../../../base/common/cancellation.js';
8 > import { IStringDictionary } from '../../../../../base/common/collections.js';
9 > import { toErrorMessage } from '../../../../../base/common/errorMessage.js';
10 > import { BugIndicatingError, ErrorNoTelemetry } from '../../../../../base/common/errors.js';
11 > import { Emitter, Event } from '../../../../../base/common/event.js';
12 > import { MarkdownString } from '../../../../../base/common/htmlContent.js';
13 > import { Iterable } from '../../../../../base/common/iterator.js';
14 > import { Disposable, DisposableResourceMap, DisposableStore, IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js';
15 > import { ResourceMap } from '../../../../../base/common/map.js';
16 > import { revive } from '../../../../../base/common/marshalling.js';
17 > import { equals } from '../../../../../base/common/objects.js';
18 > import { autorun, derived, IObservable, ISettableObservable, observableValue } from '../../../../../base/common/observable.js';
19 > import { isEqual } from '../../../../../base/common/resources.js';
20 > import { StopWatch } from '../../../../../base/common/stopwatch.js';
21 > import { isDefined } from '../../../../../base/common/types.js';
22 > import { URI } from '../../../../../base/common/uri.js';
23 > import { generateUuid } from '../../../../../base/common/uuid.js';
24 > import { OffsetRange } from '../../../../../editor/common/core/ranges/offsetRange.js';
25 > import { localize } from '../../../../../nls.js';
26 > import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
27 > import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
28 > import { ILogService } from '../../../../../platform/log/common/log.js';
29 > import { Progress } from '../../../../../platform/progress/common/progress.js';
30 > import { IStorageService, StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js';
31 > import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js';
32 > import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js';
33 > import { IExtensionService } from '../../../../services/extensions/common/extensions.js';
34 > import { IChatEntitlementService } from '../../../../services/chat/common/chatEntitlementService.js';
35 > import { IChatDebugService } from '../chatDebugService.js';
36 > import { IMcpService } from '../../../mcp/common/mcpTypes.js';
37 > import { awaitStatsForSession } from '../chat.js';
38 > import { ChatPerfMark, clearChatMarks, markChat } from '../chatPerf.js';
39 > import { IChatAgentAttachmentCapabilities, IChatAgentCommand, IChatAgentData, IChatAgentHistoryEntry, IChatAgentRequest, IChatAgentResult, IChatAgentService } from '../participants/chatAgents.js';
40 > import { chatEditingSessionIsReady } from '../editing/chatEditingService.js';
41 > import { ChatModel, ChatRequestModel, ChatRequestRemovalReason, IChatModel, IChatPendingRequest, IChatRequestModel, IChatRequestModeInfo, IChatRequestVariableData, IChatResponseModel, IExportableChatData, ISerializableChatData, ISerializableChatDataIn, ISerializableChatsData, ISerializedChatDataReference, normalizeSerializableChatData, toChatHistoryContent, updateRanges, ISerializableChatModelInputState, logChangesToStateModel } from '../model/chatModel.js';
42 > import { ChatModelStore, IStartSessionProps } from '../model/chatModelStore.js';
43 > import { chatAgentLeader, ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, ChatRequestTextPart, chatSubcommandLeader, getPromptText, IParsedChatRequest } from '../requestParser/chatParserTypes.js';
44 > import { ChatRequestParser } from '../requestParser/chatRequestParser.js';
45 > import { ChatMcpServersStarting, ChatPendingRequestChangeClassification, ChatPendingRequestChangeEvent, ChatPendingRequestChangeEventName, ChatRequestQueueKind, ChatSendResult, ChatSendResultQueued, ChatSendResultSent, ChatStopCancellationNoopClassification, ChatStopCancellationNoopEvent, ChatStopCancellationNoopEventName, IChatCompleteResponse, IChatDetail, IChatFollowup, IChatModelReference, IChatProgress, IChatQuestionAnswers, IChatSendRequestOptions, IChatSendRequestResponseState, IChatService, IChatSessionStartOptions, IChatUserActionEvent, IRemotePendingRequest, ResponseModelState } from './chatService.js';
46 > import { ChatRequestTelemetry, ChatServiceTelemetry } from './chatServiceTelemetry.js';
47 > import { IChatSessionsService, isAgentHostTarget, isTerminalCommandPrompt, localChatSessionType } from '../chatSessionsService.js';
48 > import { ChatSessionStore, IChatSessionEntryMetadata } from '../model/chatSessionStore.js';
49 > import { IChatSlashCommandService } from '../participants/chatSlashCommands.js';
50 > import { IChatTransferService } from '../model/chatTransferService.js';
51 > import { chatSessionResourceToId, getChatSessionType, isUntitledChatSession, LocalChatSessionUri } from '../model/chatUri.js';
52 > import { ChatRequestVariableSet, IChatRequestVariableEntry, isExplicitFileOrImageVariableEntry, isPromptTextVariableEntry } from '../attachments/chatVariableEntries.js';
53 > import { IDynamicVariable } from '../attachments/chatVariables.js';
54 > import { ChatAgentLocation, ChatConfiguration, ChatModeKind } from '../constants.js';
55 > import { ChatMessageRole, IChatMessage, ILanguageModelsService } from '../languageModels.js';
56 > import { ILanguageModelToolsService, ToolAndToolSetEnablementMap } from '../tools/languageModelToolsService.js';
57 > import { ChatSessionOperationLog } from '../model/chatSessionOperationLog.js';
58 > import { IPromptsService } from '../promptSyntax/service/promptsService.js';
59 > import { AGENT_DEBUG_LOG_FILE_LOGGING_ENABLED_SETTING, TROUBLESHOOT_COMMAND_NAME, TROUBLESHOOT_SKILL_PATH, COPILOT_SKILL_URI_SCHEME } from '../promptSyntax/promptTypes.js';
60 > import { ChatRequestHooks, mergeHooks } from '../promptSyntax/hookSchema.js';
61 > import { ComputeAutomaticInstructions } from '../promptSyntax/computeAutomaticInstructions.js';
62 > import { findLast } from '../../../../../base/common/arraysFind.js';
63 > import { ChatMode } from '../chatModes.js';
64 >
65 > const serializedChatKey = 'interactive.sessions';
66 >
67 > /**
68 > * True when the user has typed text or attached non-trivial context to the input
69 > * but not yet sent it. Used to decide whether an external session needs metadata
70 > * persisted on dispose so the draft survives switching sessions.
71 > */
72 function hasDraftInput(model: ChatModel): boolean {
73 const state = model.inputModel.state.get();
80 return state.attachments.length > 0;
81 }
83 > class CancellableRequest implements IDisposable {
84 > private readonly _yieldRequested: ISettableObservable<boolean> = observableValue(this, false);
85 >
86 > get yieldRequested(): IObservable<boolean> {
87 > return this._yieldRequested;
88 > }
89 >
90 >
91 > constructor(
92 public readonly cancellationTokenSource: CancellationTokenSource,
93 public requestId: string | undefined,
96 @ILanguageModelToolsService private readonly toolsService: ILanguageModelToolsService
97 ) { }
99 > dispose() {
100 if (this.requestId) {
101 this.toolsService.cancelToolCallsForRequest(this.requestId);
103 this.cancellationTokenSource.dispose();
104 }
106 > cancel() {
107 if (this.requestId) {
108 this.toolsService.cancelToolCallsForRequest(this.requestId);
111 this.cancellationTokenSource.cancel();
112 }
114 > setYieldRequested(): void {
115 this._yieldRequested.set(true, undefined);
116 }
118 > resetYieldRequested(): void {
119 this._yieldRequested.set(false, undefined);
120 }
122 >
123 > const EMPTY_REFERENCES: ReadonlyArray<IDynamicVariable> = Object.freeze([]);
124 > const EMPTY_TOOL_ENABLEMENT_MAP: ToolAndToolSetEnablementMap = ToolAndToolSetEnablementMap.fromEntries([]);
125 >
126 > /**
127 > * Preserve the picker state from `stateToApply`, only recovering a custom agent mode from
128 > * `savedState` when the applied state fell back to the default Agent.
129 > *
130 > * `stateToApply` is the input state about to be applied to the session being restored (an
131 > * agent-host transferred draft, or the saved draft as a fallback). Its `selectedModel` is the
132 > * authoritative model selection.
133 > * `savedState` is only used for `mode`: prefer its custom agent over the plain default Agent, but
134 > * never override a different explicit mode already present in `stateToApply`.
135 > */
136 > export function backfillRestoredPickerState(
137 stateToApply: ISerializableChatModelInputState | undefined,
138 savedState: ISerializableChatModelInputState | undefined,
150 return { ...stateToApply, mode };
151 }
153 > /**
154 > * Recover the selected model on a transferred input state when it was dropped during a cold
155 > * handoff.
156 > *
157 > * At cold restore an agent-host transferred draft can arrive without its `selectedModel` (the live
158 > * model list is not loaded yet, so the model resolved to `undefined`). Fall back to the model
159 > * derived from the session's request history so the picker restores the last-used model instead of
160 > * Auto. The history-derived model carries full metadata (including `targetChatSessionType`), so the
161 > * input part can wait for the model pool and apply it once it loads. An explicit model already
162 > * present on `transferredState` is never overridden.
163 > */
164 > export function backfillTransferredModel(
165 transferredState: ISerializableChatModelInputState | undefined,
166 historyModel: ISerializableChatModelInputState['selectedModel'],
171 return { ...transferredState, selectedModel: historyModel };
172 }
174 > export class ChatService extends Disposable implements IChatService {
175 > declare _serviceBrand: undefined;
176 >
177 > private readonly _sessionModels: ChatModelStore;
178 > private readonly _pendingRequests = this._register(new DisposableResourceMap<CancellableRequest>());
179 > private readonly _queuedRequestDeferreds = new Map<string, DeferredPromise<ChatSendResult>>();
180 > /** Pending requests that are synthetic streamed-turn trackers (not real in-flight requests). */
181 > private readonly _syntheticPendingRequests = new WeakSet<CancellableRequest>();
182 >
183 > /**
184 > * In-flight untitled→real materializations, keyed by the original untitled
185 > * chat session resource. A first send to an untitled contributed session
186 > * stores the promise that resolves to the newly minted real resource (or
187 > * `undefined` on failure). A concurrent second send for the same untitled
188 > * resource awaits this instead of materializing a second real session.
189 > *
190 > * The committed (settled) untitled→real mapping is owned by
191 > * {@link IChatSessionsService} (published via `setMaterializedSessionResource`
192 > * and read via `getMaterializedSessionResource`); this map only tracks the
193 > * transient in-flight serialization.
194 > */
195 > private readonly _inFlightUntitledMaterializations = new ResourceMap<Promise<URI | undefined>>();
196 > private _saveModelsEnabled = true;
197 >
198 > private _transferredSessionResource: URI | undefined;
199 > public get transferredSessionResource(): URI | undefined {
200 > return this._transferredSessionResource;
201 > }
202 >
203 > private readonly _onDidSubmitRequest = this._register(new Emitter<{ readonly chatSessionResource: URI; readonly message?: IParsedChatRequest }>());
204 > public readonly onDidSubmitRequest = this._onDidSubmitRequest.event;
205 >
206 > public get onDidCreateModel() { return this._sessionModels.onDidCreateModel; }
207 >
208 > private readonly _onDidPerformUserAction = this._register(new Emitter<IChatUserActionEvent>());
209 > public readonly onDidPerformUserAction: Event<IChatUserActionEvent> = this._onDidPerformUserAction.event;
210 >
211 > private readonly _onDidReceiveQuestionCarouselAnswer = this._register(new Emitter<{ requestId: string; resolveId: string; answers: IChatQuestionAnswers | undefined }>());
212 > public readonly onDidReceiveQuestionCarouselAnswer = this._onDidReceiveQuestionCarouselAnswer.event;
213 >
214 > private readonly _onDidDisposeSession = this._register(new Emitter<{ readonly sessionResources: URI[]; reason: 'cleared' }>());
215 > public readonly onDidDisposeSession = this._onDidDisposeSession.event;
216 >
217 > private readonly _sessionFollowupCancelTokens = this._register(new DisposableResourceMap<CancellationTokenSource>());
218 > private readonly _chatServiceTelemetry: ChatServiceTelemetry;
219 > private readonly _chatSessionStore: ChatSessionStore;
220 >
221 > readonly requestInProgressObs: IObservable<boolean>;
222 >
223 > readonly chatModels: IObservable<Iterable<IChatModel>>;
224 >
225 > /**
226 > * For test use only
227 > */
228 > setSaveModelsEnabled(enabled: boolean): void {
229 this._saveModelsEnabled = enabled;
230 }
232 > /**
233 > * For test use only
234 > */
235 > waitForModelDisposals(): Promise<void> {
236 return this._sessionModels.waitForModelDisposals();
237 }
239 > private get isEmptyWindow(): boolean {
240 const workspace = this.workspaceContextService.getWorkspace();
241 return !workspace.configuration && workspace.folders.length === 0;
242 }
244 > constructor(
245 @IStorageService private readonly storageService: IStorageService,
246 @ILogService private readonly logService: ILogService,
311 });
312 }
314 > public get editingSessions() {
315 return [...this._sessionModels.values()].map(v => v.editingSession).filter(isDefined);
316 }
318 > isEnabled(location: ChatAgentLocation): boolean {
319 return this.chatAgentService.getContributedDefaultAgent(location) !== undefined;
320 }
322 > private migrateData(): ISerializableChatsData | undefined {
323 const sessionData = this.storageService.get(serializedChatKey, this.isEmptyWindow ? StorageScope.APPLICATION : StorageScope.WORKSPACE, '');
324 if (sessionData) {
334 return;
335 }
337 > private saveState(): void {
338 if (!this._saveModelsEnabled) {
339 return;
358 this._chatSessionStore.storeSessionsMetadataOnly(liveNonLocalChats);
359 }
361 > /**
362 > * Only persist local sessions from chat that are not imported.
363 > */
364 > private shouldStoreSession(session: ChatModel): boolean {
365 if (session.isDeleted) {
366 return false;
371 return session.initialLocation === ChatAgentLocation.Chat && !session.isImported;
372 }
374 > notifyUserAction(action: IChatUserActionEvent): void {
375 this._chatServiceTelemetry.notifyUserAction(action);
376 this._onDidPerformUserAction.fire(action);
382 }
383 }
385 > notifyQuestionCarouselAnswer(requestId: string, resolveId: string, answers: IChatQuestionAnswers | undefined): void {
386 this._onDidReceiveQuestionCarouselAnswer.fire({ requestId, resolveId, answers });
387 }
389 > async setChatSessionTitle(sessionResource: URI, title: string): Promise<void> {
390 const model = this._sessionModels.get(sessionResource);
391 if (model) {
401 }
402 }
404 > private trace(method: string, message?: string): void {
405 if (message) {
406 this.logService.trace(`ChatService#${method}: ${message}`);
409 }
410 }
412 > private info(method: string, message?: string): void {
413 if (message) {
414 this.logService.info(`ChatService#${method}: ${message}`);
417 }
418 }
420 > private error(method: string, message: string): void {
421 this.logService.error(`ChatService#${method} ${message}`);
422 }
424 > private deserializeChats(sessionData: string): ISerializableChatsData {
425 try {
426 const arrayOfSessions: ISerializableChatDataIn[] = revive(JSON.parse(sessionData)); // Revive serialized URIs in session data
453 }
454 }
456 > /**
457 > * Returns an array of chat details for all persisted chat sessions that have at least one request.
458 > * Chat sessions that have already been loaded into the chat view are excluded from the result.
459 > * Imported chat sessions are also excluded from the result.
460 > * TODO this is only used by the old "show chats" command which can be removed when the pre-agents view
461 > * options are removed.
462 > */
463 > async getLocalSessionHistory(): Promise<IChatDetail[]> {
464 const liveSessionItems = await this.getLiveSessionItems();
465 const historySessionItems = await this.getHistorySessionItems();
467 return [...liveSessionItems, ...historySessionItems];
468 }
470 > /**
471 > * Returns an array of chat details for all local live chat sessions.
472 > */
473 > async getLiveSessionItems(): Promise<IChatDetail[]> {
474 return await Promise.all(Array.from(this._sessionModels.values())
475 .filter(session => this.shouldBeInHistory(session))
476 .map(chatModelToChatDetail));
477 }
479 > /**
480 > * Returns an array of chat details for all local chat sessions in history (not currently loaded).
481 > */
482 > async getHistorySessionItems(): Promise<IChatDetail[]> {
483 const index = await this._chatSessionStore.getIndex();
484 return Object.values(index)
496 });
497 }
499 > async getMetadataForSession(sessionResource: URI): Promise<IChatDetail | undefined> {
500 const index = await this._chatSessionStore.getIndex();
501 const metadata: IChatSessionEntryMetadata | undefined = index[sessionResource.toString()];
512 return undefined;
513 }
515 > private shouldBeInHistory(entry: ChatModel): boolean {
516 return !entry.isImported && !entry.isDeleted && !!LocalChatSessionUri.parseLocalSessionId(entry.sessionResource) && entry.initialLocation === ChatAgentLocation.Chat;
517 }
519 > async removeHistoryEntry(sessionResource: URI): Promise<void> {
520 await this._chatSessionStore.deleteSession(this.toLocalSessionId(sessionResource));
521 const model = this._sessionModels.get(sessionResource);
525 this._onDidDisposeSession.fire({ sessionResources: [sessionResource], reason: 'cleared' });
526 }
528 > async clearAllHistoryEntries(): Promise<void> {
529 await this._chatSessionStore.clearAllSessions();
530 }
532 > startNewLocalSession(location: ChatAgentLocation, options?: IChatSessionStartOptions): IChatModelReference {
533 this.trace('startNewLocalSession');
534 const sessionResource = LocalChatSessionUri.forSession(generateUuid());
541 }, options?.debugOwner ?? 'ChatService#startNewLocalSession');
542 }
544 > private _startSession(props: IStartSessionProps): ChatModel {
545 const { initialData, location, sessionResource, canUseTools, transferEditingSession, disableBackgroundKeepAlive, inputState, isReadOnly } = props;
546 const model = this.instantiationService.createInstance(ChatModel, initialData, { initialLocation: location, canUseTools, resource: sessionResource, disableBackgroundKeepAlive, inputState, isReadOnly });
552 return model;
553 }
555 > private initializeSession(model: ChatModel): void {
556 this.trace('initializeSession', `Initialize session ${model.sessionResource}`);
557
561 this.activateDefaultAgent(model.initialLocation).catch(e => this.logService.error(e));
562 }
564 > async activateDefaultAgent(location: ChatAgentLocation): Promise<void> {
565 await this.extensionService.whenInstalledExtensionsRegistered();
566
586 }
587 }
589 > getSession(sessionResource: URI): IChatModel | undefined {
590 return this._sessionModels.get(sessionResource);
591 }
593 > acquireExistingSession(sessionResource: URI, debugOwner?: string): IChatModelReference | undefined {
594 return this._sessionModels.acquireExisting(sessionResource, debugOwner ?? 'ChatService#acquireExistingSession');
595 }
597 > getChatModelReferenceDebugInfo() {
598 return this._sessionModels.getReferenceDebugSnapshot();
599 }
601 > private async acquireOrRestoreLocalSession(sessionResource: URI, debugOwner?: string): Promise<IChatModelReference | undefined> {
602 this.trace('acquireOrRestoreSession', `${sessionResource}`);
603 const existingRef = this.acquireExistingSession(sessionResource, debugOwner);
630 return sessionRef;
631 }
633 > // There are some cases where this returns a real string. What happens if it doesn't?
634 > // This had titles restored from the index, so just return titles from index instead, sync.
635 > getSessionTitle(sessionResource: URI): string | undefined {
636 const sessionId = LocalChatSessionUri.parseLocalSessionId(sessionResource);
637 if (!sessionId) {
642 this._chatSessionStore.getMetadataForSessionSync(sessionResource)?.title;
643 }
645 > loadSessionFromData(data: IExportableChatData | ISerializableChatData, debugOwner?: string): IChatModelReference {
646 const sessionId = (data as ISerializableChatData).sessionId ?? generateUuid();
647 const sessionResource = LocalChatSessionUri.forSession(sessionId);
653 }, debugOwner ?? 'ChatService#loadSessionFromData');
654 }
656 > async acquireOrLoadSession(sessionResource: URI, location: ChatAgentLocation, token: CancellationToken, debugOwner?: string): Promise<IChatModelReference | undefined> {
657 if (LocalChatSessionUri.isLocalSession(sessionResource)) {
658 return this.acquireOrRestoreLocalSession(sessionResource, debugOwner);
661 }
662 }
664 > private async loadRemoteSession(sessionResource: URI, location: ChatAgentLocation, token: CancellationToken, debugOwner?: string): Promise<IChatModelReference | undefined> {
665 // Check if session already exists before resolving the provider,
666 // so we can return a cached model even if the provider was unregistered.
1055 return modelRef;
1056 }
1058 > async resendRequest(request: IChatRequestModel, options?: IChatSendRequestOptions): Promise<void> {
1059 const model = this._sessionModels.get(request.session.sessionResource);
1060 if (!model && model !== request.session) {
1085 await this._sendRequestAsync(model, model.sessionResource, request.message, attempt, enableCommandDetection, defaultAgent, location, resendOptions).responseCompletePromise;
1086 }
1088 > private queuePendingRequest(model: ChatModel, sessionResource: URI, request: string, options: IChatSendRequestOptions): ChatSendResultQueued {
1089 const location = options.location ?? model.initialLocation;
1090 const parsedRequest = this.parseChatRequest(sessionResource, request, location, options);
1116 return { kind: 'queued', deferred: deferred.p };
1117 }
1119 > async sendRequest(sessionResource: URI, request: string, options?: IChatSendRequestOptions): Promise<ChatSendResult> {
1120 this.trace('sendRequest', `sessionResource: ${sessionResource.toString()}, message: ${request.substring(0, 20)}${request.length > 20 ? '[...]' : ''}}`);
1121
1217 };
1218 }
1220 > /**
1221 > * Converts an untitled contributed chat session into its real session on the
1222 > * first send and returns the real model/resource so the caller can re-target
1223 > * the request. Serialized per untitled resource: a first send stores an
1224 > * in-flight promise, and a concurrent second send awaits it and converges on
1225 > * the same real session (where the caller's pending-request check then rejects
1226 > * the duplicate) instead of minting a second real session.
1227 > *
1228 > * Returns `undefined` when no conversion happened — either there is no
1229 > * `newChatSessionItem` handler / the handler declined, or a concurrent
1230 > * materialization failed — in which case the caller keeps using the untitled
1231 > * session (the original behavior).
1232 > */
1233 > private async _materializeUntitledSession(untitledResource: URI, request: string, options: IChatSendRequestOptions | undefined, untitledModel: ChatModel): Promise<{ model: ChatModel; sessionResource: URI; newSessionResource: URI } | undefined> {
1234 const inFlight = this._inFlightUntitledMaterializations.get(untitledResource);
1235 if (inFlight) {
1316 }
1317 }
1319 > private getAttachmentCapabilitiesForParser(chatSessionType: string, agent: IChatAgentData | undefined): IChatAgentAttachmentCapabilities | undefined {
1320 return this.chatSessionService.getCapabilitiesForSessionType(chatSessionType) ?? agent?.capabilities;
1321 }
1323 > private parseChatRequest(sessionResource: URI, request: string, location: ChatAgentLocation, options: IChatSendRequestOptions | undefined): IParsedChatRequest {
1324 let parserContext = options?.parserContext;
1325 let contextAgent = parserContext?.forcedAgent ?? parserContext?.selectedAgent;
1351 return parsedRequest;
1352 }
1354 > private refreshFollowupsCancellationToken(sessionResource: URI): CancellationToken {
1355 this._sessionFollowupCancelTokens.get(sessionResource)?.cancel();
1356 const newTokenSource = new CancellationTokenSource();
1359 return newTokenSource.token;
1360 }
1362 > private _sendRequestAsync(model: ChatModel, sessionResource: URI, parsedRequest: IParsedChatRequest, attempt: number, enableCommandDetection: boolean, defaultAgent: IChatAgentData, location: ChatAgentLocation, options?: IChatSendRequestOptions): IChatSendRequestResponseState {
1363 const followupsCancelToken = this.refreshFollowupsCancellationToken(sessionResource);
1364 let request: ChatRequestModel | undefined;
1825 };
1826 }
1828 > processPendingRequests(sessionResource: URI): void {
1829 const model = this._sessionModels.get(sessionResource);
1830 if (model && !this._pendingRequests.has(sessionResource)) {
1832 }
1833 }
1835 > /**
1836 > * Returns true if the session is backed by an agent host server, which
1837 > * controls queued-message dequeuing on the server side.
1838 > */
1839 > private _isServerManagedQueue(sessionResource: URI): boolean {
1840 return getChatSessionType(sessionResource).startsWith('agent-host-');
1841 }
1843 > /**
1844 > * Process the next pending request from the model's queue, if any.
1845 > * Called after a request completes to continue processing queued requests.
1846 > * Multiple consecutive steering requests are combined into a single request.
1847 > */
1848 > private processNextPendingRequest(model: ChatModel): void {
1849 // Agent host sessions delegate queue management to the server.
1850 // The server dispatches ChatTurnStarted with queuedMessageId when
1948 }
1949 }
1951 > private generateInitialChatTitleIfNeeded(model: ChatModel, request: IChatAgentRequest, defaultAgent: IChatAgentData, token: CancellationToken): void {
1952 // Generate a title only for the first request, and only via the default agent.
1953 // Use a single-entry history based on the current request (no full chat history).
1969 void generate();
1970 }
1972 > private prepareContext(attachedContextVariables: IChatRequestVariableEntry[] | undefined): IChatRequestVariableEntry[] {
1973 attachedContextVariables ??= [];
1974
1990 return attachedContextVariables;
1991 }
1993 > private getHistoryEntriesFromModel(requests: IChatRequestModel[], location: ChatAgentLocation, forAgentId: string): IChatAgentHistoryEntry[] {
1994 const history: IChatAgentHistoryEntry[] = [];
1995 const agent = this.chatAgentService.getAgent(forAgentId);
2027 return history;
2028 }
2030 > async removeRequest(sessionResource: URI, requestId: string): Promise<void> {
2031 const model = this._sessionModels.get(sessionResource);
2032 if (!model) {
2043 model.removeRequest(requestId);
2044 }
2046 > async adoptRequest(sessionResource: URI, request: IChatRequestModel) {
2047 if (!(request instanceof ChatRequestModel)) {
2048 throw new TypeError('Can only adopt requests of type ChatRequestModel');
2066 }
2067 }
2069 > async addCompleteRequest(sessionResource: URI, message: IParsedChatRequest | string, variableData: IChatRequestVariableData | undefined, attempt: number | undefined, response: IChatCompleteResponse): Promise<void> {
2070 this.trace('addCompleteRequest', `message: ${message}`);
2071
2093 request.response?.complete();
2094 }
2096 > async cancelCurrentRequestForSession(sessionResource: URI, source?: string): Promise<void> {
2097 this.trace('cancelCurrentRequestForSession', `session: ${sessionResource}`);
2098 const pendingRequest = this._pendingRequests.get(sessionResource);
2126 }
2127 }
2129 > setYieldRequested(sessionResource: URI): void {
2130 const pendingRequest = this._pendingRequests.get(sessionResource);
2131 if (pendingRequest) {
2133 }
2134 }
2136 > migrateRequests(originalResource: URI, targetResource: URI): void {
2137 const model = this._sessionModels.get(originalResource);
2138 if (!model) {
2159 }
2160 }
2162 > removePendingRequest(sessionResource: URI, requestId: string): void {
2163 const model = this._sessionModels.get(sessionResource) as ChatModel | undefined;
2164 if (model) {
2180 }
2181 }
2183 > setPendingRequests(sessionResource: URI, requests: readonly { requestId: string; kind: ChatRequestQueueKind }[]): void {
2184 const model = this._sessionModels.get(sessionResource) as ChatModel | undefined;
2185 if (model) {
2187 }
2188 }
2190 > syncPendingRequestsFromRemote(sessionResource: URI, requests: readonly IRemotePendingRequest[]): void {
2191 const model = this._sessionModels.get(sessionResource) as ChatModel | undefined;
2192 if (!model) {
2236 }
2237 }
2239 > async sendPendingRequestImmediately(sessionResource: URI, requestId: string): Promise<void> {
2240 const model = this._sessionModels.get(sessionResource) as ChatModel | undefined;
2241 if (!model) {
2288 this.processPendingRequests(sessionResource);
2289 }
2291 > public hasSessions(): boolean {
2292 return this._chatSessionStore.hasSessions();
2293 }
2295 > async transferChatSession(transferredSessionResource: URI, toWorkspace: URI): Promise<void> {
2296 if (!LocalChatSessionUri.isLocalSession(transferredSessionResource)) {
2297 throw new Error(`Can only transfer local chat sessions. Invalid session: ${transferredSessionResource}`);
2315 this.trace('transferChatSession', `Transferred session ${model.sessionResource} to workspace ${toWorkspace.toString()}`);
2316 }
2318 > getChatStorageFolder(): URI {
2319 return this._chatSessionStore.getChatStorageFolder();
2320 }
2322 > logChatIndex(): void {
2323 this._chatSessionStore.logIndex();
2324 }
2326 > setSessionTitle(sessionResource: URI, title: string): void {
2327 this._sessionModels.get(sessionResource)?.setCustomTitle(title);
2328 }
2330 > appendProgress(request: IChatRequestModel, progress: IChatProgress): void {
2331 const model = this._sessionModels.get(request.session.sessionResource);
2332 if (!(request instanceof ChatRequestModel)) {
2336 model?.acceptResponseProgress(request, progress);
2337 }
2339 > private toLocalSessionId(sessionResource: URI) {
2340 const localSessionId = LocalChatSessionUri.parseLocalSessionId(sessionResource);
2341 if (!localSessionId) {
2344 return localSessionId;
2345 }
2347 >
2348 export async function chatModelToChatDetail(model: IChatModel): Promise<IChatDetail> {
2349 const title = model.title || localize('newChat', "New Chat");
src/vs/workbench/contrib/chat/common/chatService/chatServiceTelemetry.ts 215 introduced LOC · 10 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- chatServiceTelemetry.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { URI } from '../../../../../base/common/uri.js';
7 > import { isLocation } from '../../../../../editor/common/languages.js';
8 > import { escapeModelIdForTelemetry, ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js';
9 > import { IChatAgentData } from '../participants/chatAgents.js';
10 > import { ChatRequestModel, IChatRequestVariableData } from '../model/chatModel.js';
11 > import { ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart } from '../requestParser/chatParserTypes.js';
12 > import { ChatAgentVoteDirection, ChatCopyKind, IChatSendRequestOptions, IChatUserActionEvent } from './chatService.js';
13 > import { isImageVariableEntry } from '../attachments/chatVariableEntries.js';
14 > import { ChatAgentLocation, ChatModeKind, ChatPermissionLevel } from '../constants.js';
15 > import { ILanguageModelsService } from '../languageModels.js';
16 > import { chatSessionResourceToId, getChatSessionType } from '../model/chatUri.js';
17 > import { isRemoteAgentHostSessionType, parseRemoteAgentHostHarness } from '../../../../../platform/agentHost/common/agentHostSessionType.js';
18 >
19 > type ChatVoteEvent = {
20 > direction: 'up' | 'down';
21 > agentId: string;
22 > command: string | undefined;
23 > };
24 >
25 > type ChatVoteClassification = {
26 > direction: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the user voted up or down.' };
27 > agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the chat agent that this vote is for.' };
28 > command: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the slash command that this vote is for.' };
29 > owner: 'roblourens';
30 > comment: 'Provides insight into the performance of Chat agents.';
31 > };
32 >
33 > type ChatCopyEvent = {
34 > copyKind: 'action' | 'toolbar';
35 > agentId: string;
36 > command: string | undefined;
37 > };
38 >
39 > type ChatCopyClassification = {
40 > copyKind: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'How the copy was initiated.' };
41 > agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the chat agent that the copy acted on.' };
42 > command: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the slash command the copy acted on.' };
43 > owner: 'roblourens';
44 > comment: 'Provides insight into the usage of Chat features.';
45 > };
46 >
47 > type ChatInsertEvent = {
48 > newFile: boolean;
49 > agentId: string;
50 > command: string | undefined;
51 > };
52 >
53 > type ChatInsertClassification = {
54 > newFile: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the code was inserted into a new untitled file.' };
55 > agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the chat agent that this insertion is for.' };
56 > command: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the slash command that this insertion is for.' };
57 > owner: 'roblourens';
58 > comment: 'Provides insight into the usage of Chat features.';
59 > };
60 >
61 > type ChatApplyEvent = {
62 > newFile: boolean;
63 > agentId: string;
64 > command: string | undefined;
65 > codeMapper: string | undefined;
66 > editsProposed: boolean;
67 > };
68 >
69 > type ChatApplyClassification = {
70 > newFile: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the code was inserted into a new untitled file.' };
71 > agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the chat agent that this insertion is for.' };
72 > command: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the slash command that this insertion is for.' };
73 > codeMapper: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The code mapper that wa used to compute the edit.' };
74 > editsProposed: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether there was a change proposed to the user.' };
75 > owner: 'aeschli';
76 > comment: 'Provides insight into the usage of Chat features.';
77 > };
78 >
79 > type ChatFollowupEvent = {
80 > agentId: string;
81 > command: string | undefined;
82 > };
83 >
84 > type ChatFollowupClassification = {
85 > agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the related chat agent.' };
86 > command: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the related slash command.' };
87 > owner: 'roblourens';
88 > comment: 'Provides insight into the usage of Chat features.';
89 > };
90 >
91 > type ChatTerminalEvent = {
92 > languageId: string;
93 > agentId: string;
94 > command: string | undefined;
95 > };
96 >
97 > type ChatTerminalClassification = {
98 > languageId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The language of the code that was run in the terminal.' };
99 > agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the related chat agent.' };
100 > command: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the related slash command.' };
101 > owner: 'roblourens';
102 > comment: 'Provides insight into the usage of Chat features.';
103 > };
104 >
105 > type ChatFollowupsRetrievedEvent = {
106 > agentId: string;
107 > command: string | undefined;
108 > numFollowups: number;
109 > };
110 >
111 > type ChatFollowupsRetrievedClassification = {
112 > agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the related chat agent.' };
113 > command: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the related slash command.' };
114 > numFollowups: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The number of followup prompts returned by the agent.' };
115 > owner: 'roblourens';
116 > comment: 'Provides insight into the usage of Chat features.';
117 > };
118 >
119 > type ChatEditHunkEvent = {
120 > agentId: string;
121 > outcome: 'accepted' | 'rejected';
122 > lineCount: number;
123 > hasRemainingEdits: boolean;
124 > requestId: string;
125 > modelId: string;
126 > modeId: string;
127 > };
128 >
129 > type ChatEditHunkClassification = {
130 > agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the related chat agent.' };
131 > outcome: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The outcome of the edit hunk action.' };
132 > lineCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The number of lines in the relevant change.' };
133 > hasRemainingEdits: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether there are remaining edits in the file after this action.' };
134 > requestId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the chat request that produced the edit.' };
135 > modelId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The AI model used to generate the edit.' };
136 > modeId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The chat mode used for the request (e.g. ask, edit, agent).' };
137 > owner: 'roblourens';
138 > comment: 'Provides insight into the usage of Chat features.';
139 > };
140 >
141 > export type ChatProviderInvokedEvent = {
142 > timeToFirstProgress: number | undefined;
143 > totalTime: number | undefined;
144 > result: 'success' | 'error' | 'errorWithOutput' | 'cancelled' | 'filtered';
145 > requestType: 'string' | 'followup' | 'slashCommand';
146 > chatSessionId: string;
147 > requestId: string;
148 > agent: string;
149 > agentExtensionId: string | undefined;
150 > slashCommand: string | undefined;
151 > location: ChatAgentLocation;
152 > citations: number;
153 > numCodeBlocks: number;
154 > isParticipantDetected: boolean;
155 > enableCommandDetection: boolean;
156 > attachmentKinds: string[];
157 > model: string | undefined;
158 > permissionLevel: ChatPermissionLevel | undefined;
159 > chatMode: string | undefined;
160 > sessionType: string | undefined;
161 > harness: string | undefined;
162 > };
163 >
164 > export type ChatProviderInvokedClassification = {
165 > timeToFirstProgress: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The time in milliseconds from invoking the provider to getting the first data.' };
166 > totalTime: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The total time it took to run the provider\'s `provideResponseWithProgress`.' };
167 > result: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether invoking the ChatProvider resulted in an error.' };
168 > requestType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The type of request that the user made.' };
169 > chatSessionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'A random ID for the session.' };
170 > requestId: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The identifier of the chat request, used to correlate workbench and agent host telemetry.' };
171 > agent: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The type of agent used.' };
172 > agentExtensionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The extension that contributed the agent.' };
173 > slashCommand?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The type of slashCommand used.' };
174 > location: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The location at which chat request was made.' };
175 > citations: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The number of public code citations that were returned with the response.' };
176 > numCodeBlocks: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The number of code blocks in the response.' };
177 > isParticipantDetected: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the participant was automatically detected.' };
178 > enableCommandDetection: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether participation detection was disabled for this invocation.' };
179 > attachmentKinds: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The types of variables/attachments that the user included with their query.' };
180 > model: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The model used to generate the response.' };
181 > permissionLevel: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The tool auto-approval permission level selected in the permission picker (default, assisted, autoApprove, or autopilot). Undefined when the picker is not applicable (e.g. ask mode or API-driven requests).' };
182 > chatMode: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The chat mode used for the request. Built-in modes (ask, agent, edit), extension-contributed names (e.g. Plan), or a hashed identifier for user-created custom agents.' };
183 > sessionType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The session type scheme (e.g. vscodeLocalChatSession for local, or remote session scheme).' };
184 > harness: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'For remote agent host sessions, the underlying harness/provider (e.g. copilotcli, claude, codex) so remote activity can be split by harness. Undefined for non-remote sessions.' };
185 > owner: 'roblourens';
186 > comment: 'Provides insight into the performance of Chat agents.';
187 > };
188 >
189 > export class ChatServiceTelemetry {
190 > constructor(
191 @ITelemetryService private readonly telemetryService: ITelemetryService,
192 ) { }
194 > notifyUserAction(action: IChatUserActionEvent): void {
195 if (action.action.kind === 'vote') {
196 this.telemetryService.publicLog2<ChatVoteEvent, ChatVoteClassification>('interactiveSessionVote', {
242 }
243 }
245 > retrievedFollowups(agentId: string, command: string | undefined, numFollowups: number): void {
246 this.telemetryService.publicLog2<ChatFollowupsRetrievedEvent, ChatFollowupsRetrievedClassification>('chatFollowupsRetrieved', {
247 agentId,
250 });
251 }
253 >
254 function getCodeBlocks(text: string): string[] {
255 const lines = text.split('\n');
274 return codeBlockLanguages;
275 }
277 > export class ChatRequestTelemetry {
278 > private isComplete = false;
279 >
280 > constructor(private readonly opts: {
281 agent: IChatAgentData;
282 agentSlashCommandPart: ChatRequestAgentSubcommandPart | undefined;
290 @ILanguageModelsService private readonly languageModelsService: ILanguageModelsService
291 ) { }
293 > complete({ timeToFirstProgress, totalTime, result, requestType, request, detectedAgent }: {
294 timeToFirstProgress: number | undefined;
295 totalTime: number | undefined;
328 });
329 }
331 > private attachmentKindsForTelemetry(variableData: IChatRequestVariableData): string[] {
332 // this shows why attachments still have to be cleaned up somewhat
333 return variableData.variables.map(v => {
366 });
367 }
369 > private resolveModelId(userSelectedModelId: string | undefined): string | undefined {
370 return userSelectedModelId && this.languageModelsService.lookupLanguageModel(userSelectedModelId)?.id;
371 }
373 >
374 function getChatSessionTypeForTelemetry(sessionResource: URI): string {
375 const sessionType = getChatSessionType(sessionResource);
378 return isRemoteAgentHostSessionType(sessionType) ? 'remote-agent-host' : sessionType;
379 }
381 > /**
382 > * For remote agent host sessions, the underlying harness/provider so remote
383 > * activity can be split by harness (the collapsed sessionType cannot). See
384 > * telemetry gap #2 in #8209. Undefined for non-remote sessions.
385 > */
386 function getHarnessForTelemetry(sessionResource: URI): string | undefined {
387 return parseRemoteAgentHostHarness(getChatSessionType(sessionResource));
src/vs/platform/assignment/common/assignment.ts 135 introduced LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- assignment.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { Event } from '../../../base/common/event.js';
7 > import * as platform from '../../../base/common/platform.js';
8 > import type { IExperimentationFilterProvider } from 'tas-client';
9 >
10 > export const ASSIGNMENT_STORAGE_KEY = 'VSCode.ABExp.FeatureData';
11 > export const ASSIGNMENT_REFETCH_INTERVAL = 60 * 60 * 1000; // 1 hour
12 >
13 > export interface IAssignmentService {
14 > readonly _serviceBrand: undefined;
15 >
16 > readonly onDidRefetchAssignments: Event<void>;
17 > getTreatment<T extends string | number | boolean>(name: string): Promise<T | undefined>;
18 > }
19 >
20 > export enum TargetPopulation {
21 > Insiders = 'insider',
22 > Public = 'public',
23 > Exploration = 'exploration'
24 > }
25 >
26 > /*
27 > Based upon the official VSCode currently existing filters in the
28 > ExP backend for the VSCode cluster.
29 > https://experimentation.visualstudio.com/Analysis%20and%20Experimentation/_git/AnE.ExP.TAS.TachyonHost.Configuration?path=%2FConfigurations%2Fvscode%2Fvscode.json&version=GBmaster
30 > "X-MSEdge-Market": "detection.market",
31 > "X-FD-Corpnet": "detection.corpnet",
32 > "X-VSCode-AppVersion": "appversion",
33 > "X-VSCode-Build": "build",
34 > "X-MSEdge-ClientId": "clientid",
35 > "X-VSCode-ExtensionName": "extensionname",
36 > "X-VSCode-ExtensionVersion": "extensionversion",
37 > "X-VSCode-TargetPopulation": "targetpopulation",
38 > "X-VSCode-Language": "language",
39 > "X-VSCode-Platform": "platform",
40 > "X-VSCode-ReleaseDate": "releasedate",
41 > "X-VSCode-WindowKind": "windowkind"
42 > */
43 > export enum Filters {
44 > /**
45 > * The market in which the extension is distributed.
46 > */
47 > Market = 'X-MSEdge-Market',
48 >
49 > /**
50 > * The corporation network.
51 > */
52 > CorpNet = 'X-FD-Corpnet',
53 >
54 > /**
55 > * Version of the application which uses experimentation service.
56 > */
57 > ApplicationVersion = 'X-VSCode-AppVersion',
58 >
59 > /**
60 > * Insiders vs Stable.
61 > */
62 > Build = 'X-VSCode-Build',
63 >
64 > /**
65 > * Client Id which is used as primary unit for the experimentation.
66 > */
67 > ClientId = 'X-MSEdge-ClientId',
68 >
69 > /**
70 > * Developer Device Id which can be used as an alternate unit for experimentation.
71 > */
72 > DeveloperDeviceId = 'X-VSCode-DevDeviceId',
73 >
74 > /**
75 > * Extension header.
76 > */
77 > ExtensionName = 'X-VSCode-ExtensionName',
78 >
79 > /**
80 > * The version of the extension.
81 > */
82 > ExtensionVersion = 'X-VSCode-ExtensionVersion',
83 >
84 > /**
85 > * The language in use by VS Code
86 > */
87 > Language = 'X-VSCode-Language',
88 >
89 > /**
90 > * The target population.
91 > * This is used to separate internal, early preview, GA, etc.
92 > */
93 > TargetPopulation = 'X-VSCode-TargetPopulation',
94 >
95 > /**
96 > * The platform (OS) on which VS Code is running.
97 > */
98 > Platform = 'X-VSCode-Platform',
99 >
100 > /**
101 > * The release/build date of VS Code (UTC) in the format yyyymmddHH.
102 > */
103 > ReleaseDate = 'X-VSCode-ReleaseDate',
104 >
105 > /**
106 > * The kind of window VS Code is running in (`editor` or `agents`).
107 > */
108 > WindowKind = 'X-VSCode-WindowKind',
109 > }
110 >
111 > export const enum WindowKind {
112 > Editor = 'editor',
113 > Agents = 'agents',
114 > }
115 >
116 > export class AssignmentFilterProvider implements IExperimentationFilterProvider {
117 > constructor(
118 private version: string,
119 private appName: string,
124 private windowKind: WindowKind
125 ) { }
127 > /**
128 > * Returns a version string that can be parsed by the TAS client.
129 > * The tas client cannot handle suffixes lke "-insider"
130 > * Ref: https://github.com/microsoft/tas-client/blob/30340d5e1da37c2789049fcf45928b954680606f/vscode-tas-client/src/vscode-tas-client/VSCodeFilterProvider.ts#L35
131 > *
132 > * @param version Version string to be trimmed.
133 > */
134 > private static trimVersionSuffix(version: string): string {
135 const regex = /\-[a-zA-Z0-9]+$/;
136 const result = version.split(regex);
138 return result[0];
139 }
141 > getFilterValue(filter: string): string | null {
142 switch (filter) {
143 case Filters.ApplicationVersion:
167 }
168 }
170 > private static formatReleaseDate(iso: string): string {
171 // Expect ISO format, fall back to empty string if not provided
172 if (!iso) {
181 return match.slice(1, 5).join('');
182 }
184 > getFilters(): Map<string, unknown> {
185 const filters: Map<string, unknown> = new Map<string, unknown>();
186 const filterValues = Object.values(Filters);
191 return filters;
192 }
193 > } assignment.ts
194 >
195 > export function getInternalOrg(organisations: string[] | undefined): 'vscode' | 'github' | 'microsoft' | undefined {
196 const isVSCodeInternal = organisations?.includes('Visual-Studio-Code');
197 const isGitHubInternal = organisations?.includes('github');
src/vs/workbench/services/assignment/common/assignmentService.ts 130 introduced LOC · 16 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- assignmentService.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { localize } from '../../../../nls.js';
7 > import { createDecorator, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
8 > import type { IKeyValueStorage, IExperimentationTelemetry, ExperimentationService as TASClient } from 'tas-client';
9 > import { Memento } from '../../../common/memento.js';
10 > import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
11 > import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
12 > import { ITelemetryData } from '../../../../base/common/actions.js';
13 > import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
14 > import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
15 > import { IProductService } from '../../../../platform/product/common/productService.js';
16 > import { ASSIGNMENT_REFETCH_INTERVAL, ASSIGNMENT_STORAGE_KEY, AssignmentFilterProvider, IAssignmentService, TargetPopulation, WindowKind } from '../../../../platform/assignment/common/assignment.js';
17 > import { Registry } from '../../../../platform/registry/common/platform.js';
18 > import { workbenchConfigurationNodeBase } from '../../../common/configuration.js';
19 > import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from '../../../../platform/configuration/common/configurationRegistry.js';
20 > import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js';
21 > import { importAMDNodeModule } from '../../../../amdX.js';
22 > import { timeout } from '../../../../base/common/async.js';
23 > import { StopWatch } from '../../../../base/common/stopwatch.js';
24 > import { CopilotAssignmentFilterProvider } from './assignmentFilters.js';
25 > import { AssignmentContextFilter } from './assignmentContextFilter.js';
26 > import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js';
27 > import { Emitter, Event } from '../../../../base/common/event.js';
28 > import { experimentsEnabled } from '../../telemetry/common/workbenchTelemetryUtils.js';
29 >
30 > export interface IAssignmentFilter {
31 > /**
32 > * Stable identifier for this filter. Used to persist and reconcile the set of
33 > * assignment-context ids this filter has excluded, independently of other filters.
34 > */
35 > readonly id: string;
36 > exclude(assignment: string): boolean;
37 > onDidChange: Event<void>;
38 > }
39 >
40 > export const IWorkbenchAssignmentService = createDecorator<IWorkbenchAssignmentService>('assignmentService');
41 >
42 > export interface IWorkbenchAssignmentService extends IAssignmentService {
43 > getCurrentExperiments(): Promise<string[] | undefined>;
44 > addTelemetryAssignmentFilter(filter: IAssignmentFilter): void;
45 > }
46 >
47 > class MementoKeyValueStorage implements IKeyValueStorage {
48 >
49 > private readonly mementoObj: Record<string, unknown>;
50 >
51 > constructor(private readonly memento: Memento<Record<string, unknown>>) {
52 this.mementoObj = memento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE);
53 }
55 > async getValue<T>(key: string, defaultValue?: T | undefined): Promise<T | undefined> {
56 const value = await this.mementoObj[key] as T | undefined;
57
58 return value || defaultValue;
59 }
61 > setValue<T>(key: string, value: T): void {
62 this.mementoObj[key] = value;
63 this.memento.saveMemento();
64 }
66 >
67 > class WorkbenchAssignmentServiceTelemetry extends Disposable implements IExperimentationTelemetry {
68 >
69 > private readonly _onDidUpdateAssignmentContext = this._register(new Emitter<void>());
70 > readonly onDidUpdateAssignmentContext = this._onDidUpdateAssignmentContext.event;
71 >
72 > private _previousAssignmentContext: string | undefined;
73 > private _lastAssignmentContext: string | undefined;
74 > get assignmentContext(): string[] | undefined {
75 > return this._lastAssignmentContext?.split(';');
76 > }
77 >
78 > constructor(
79 private readonly telemetryService: ITelemetryService,
80 private readonly productService: IProductService,
90 }));
91 }
93 > private _setAssignmentContext(value: string): void {
94 const filteredValue = this.contextFilter.filter(value);
95 this._lastAssignmentContext = filteredValue;
100 }
101 }
103 > // __GDPR__COMMON__ "abexp.assignmentcontext" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
104 > setSharedProperty(name: string, value: string): void {
105 if (name === this.productService.tasConfig?.assignmentContextTelemetryPropertyName) {
106 this._previousAssignmentContext = value;
110 this.telemetryService.setExperimentProperty(name, value);
111 }
113 > postEvent(eventName: string, props: Map<string, string>): void {
114 const data: ITelemetryData = {};
115 for (const [key, value] of props.entries()) {
126 this.telemetryService.publicLog(eventName, data);
127 }
129 >
130 > export class WorkbenchAssignmentService extends Disposable implements IAssignmentService {
131 >
132 > declare readonly _serviceBrand: undefined;
133 >
134 > private readonly tasClient: Promise<TASClient> | undefined;
135 > private readonly tasSetupDisposables = new DisposableStore();
136 >
137 > private networkInitialized = false;
138 > private readonly overrideInitDelay: Promise<void>;
139 >
140 > private readonly contextFilter: AssignmentContextFilter;
141 > private readonly telemetry: WorkbenchAssignmentServiceTelemetry;
142 > private readonly keyValueStorage: IKeyValueStorage;
143 >
144 > private readonly experimentsEnabled: boolean;
145 >
146 > private readonly _onDidRefetchAssignments = this._register(new Emitter<void>());
147 > public readonly onDidRefetchAssignments = this._onDidRefetchAssignments.event;
148 >
149 > constructor(
150 @ITelemetryService private readonly telemetryService: ITelemetryService,
151 @IStorageService storageService: IStorageService,
179 this.overrideInitDelay = timeout(overrideDelay);
180 }
182 > async getTreatment<T extends string | number | boolean>(name: string): Promise<T | undefined> {
183 const result = await this.doGetTreatment<T>(name);
184
202 return result;
203 }
205 > private async doGetTreatment<T extends string | number | boolean>(name: string): Promise<T | undefined> {
206 await this.overrideInitDelay; // For development purposes, allow overriding tas assignments to test variants locally.
207
235 return result;
236 }
238 > private async setupTASClient(): Promise<TASClient> {
239 this.tasSetupDisposables.clear();
240
285 return tasClient;
286 }
288 > private logFetchLatency(fetchType: 'initial' | 'refetch', durationMs: number): void {
289 type TASClientFetchLatencyData = {
290 fetchType: string;
304 });
305 }
307 > private async refetchAssignments(): Promise<void> {
308 if (!this.tasClient) {
309 return; // Setup has not started, assignments will use latest filters
319 this.logFetchLatency('refetch', refetchStopWatch.elapsed());
320 }
322 > async getCurrentExperiments(): Promise<string[] | undefined> {
323 if (!this.tasClient) {
324 return undefined;
333 return this.telemetry.assignmentContext;
334 }
336 > addTelemetryAssignmentFilter(filter: IAssignmentFilter): void {
337 this.contextFilter.addFilter(filter);
338 }
340 >
341 > registerSingleton(IWorkbenchAssignmentService, WorkbenchAssignmentService, InstantiationType.Delayed);
342 >
343 > const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
344 > registry.registerConfiguration({
345 > ...workbenchConfigurationNodeBase,
346 > 'properties': {
347 > 'workbench.enableExperiments': {
348 > 'type': 'boolean',
349 > 'description': localize('workbench.enableExperiments', "Fetches experiments to run from a Microsoft online service."),
350 > 'default': true,
351 > 'scope': ConfigurationScope.APPLICATION,
352 > 'restricted': true,
353 > 'tags': ['usesOnlineServices']
354 > }
355 > }
356 > });
src/vs/workbench/services/assignment/common/assignmentFilters.ts 106 introduced LOC · 8 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- assignmentFilters.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import type { IExperimentationFilterProvider } from 'tas-client';
7 > import { Emitter } from '../../../../base/common/event.js';
8 > import { Disposable } from '../../../../base/common/lifecycle.js';
9 > import { getInternalOrg } from '../../../../platform/assignment/common/assignment.js';
10 > import { IDefaultAccountService } from '../../../../platform/defaultAccount/common/defaultAccount.js';
11 > import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js';
12 > import { ILogService } from '../../../../platform/log/common/log.js';
13 > import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
14 > import { IChatEntitlementService } from '../../chat/common/chatEntitlementService.js';
15 > import { IExtensionService } from '../../extensions/common/extensions.js';
16 >
17 > export enum ExtensionsFilter {
18 >
19 > /**
20 > * Version of the github.copilot extension.
21 > */
22 > CopilotExtensionVersion = 'X-Copilot-RelatedPluginVersion-githubcopilot',
23 >
24 > /**
25 > * Version of the github.copilot-chat extension.
26 > */
27 > CopilotChatExtensionVersion = 'X-Copilot-RelatedPluginVersion-githubcopilotchat',
28 >
29 > /**
30 > * Version of the completions version.
31 > */
32 > CompletionsVersionInCopilotChat = 'X-VSCode-CompletionsInChatExtensionVersion',
33 >
34 > /**
35 > * SKU of the copilot entitlement.
36 > */
37 > CopilotSku = 'X-GitHub-Copilot-SKU',
38 >
39 > /**
40 > * The internal org of the user.
41 > */
42 > MicrosoftInternalOrg = 'X-Microsoft-Internal-Org',
43 >
44 > /**
45 > * The tracking ID of the user from Copilot entitlement API.
46 > */
47 > CopilotTrackingId = 'X-Copilot-CopilotTrackingId',
48 >
49 > /**
50 > * Whether the `sn` flag is set to `'1'` in the copilot token.
51 > */
52 > CopilotIsSn = 'X-GitHub-Copilot-IsSn',
53 >
54 > /**
55 > * Whether the `fcv1` flag is set to `'1'` in the copilot token.
56 > */
57 > CopilotIsFcv1 = 'X-GitHub-Copilot-IsFcv1',
58 > }
59 >
60 > enum StorageVersionKeys {
61 > CopilotExtensionVersion = 'extensionsAssignmentFilterProvider.copilotExtensionVersion',
62 > CopilotChatExtensionVersion = 'extensionsAssignmentFilterProvider.copilotChatExtensionVersion',
63 > CompletionsVersion = 'extensionsAssignmentFilterProvider.copilotCompletionsVersion',
64 > CopilotSku = 'extensionsAssignmentFilterProvider.copilotSku',
65 > CopilotInternalOrg = 'extensionsAssignmentFilterProvider.copilotInternalOrg',
66 > CopilotTrackingId = 'extensionsAssignmentFilterProvider.copilotTrackingId',
67 > CopilotIsSn = 'extensionsAssignmentFilterProvider.copilotIsSn',
68 > CopilotIsFcv1 = 'extensionsAssignmentFilterProvider.copilotIsFcv1',
69 > }
70 >
71 > export class CopilotAssignmentFilterProvider extends Disposable implements IExperimentationFilterProvider {
72 > private copilotChatExtensionVersion: string | undefined;
73 > private copilotExtensionVersion: string | undefined;
74 > // TODO@benibenj remove this when completions have been ported to chat
75 > private copilotCompletionsVersion: string | undefined;
76 >
77 > private copilotInternalOrg: string | undefined;
78 > private copilotSku: string | undefined;
79 > private copilotTrackingId: string | undefined;
80 > private copilotIsSn: string | undefined;
81 > private copilotIsFcv1: string | undefined;
82 >
83 > private readonly _onDidChangeFilters = this._register(new Emitter<void>());
84 > readonly onDidChangeFilters = this._onDidChangeFilters.event;
85 >
86 > constructor(
87 @IExtensionService private readonly _extensionService: IExtensionService,
88 @ILogService private readonly _logService: ILogService,
120 this.updateCopilotTokenInfo();
121 }
123 > private async updateExtensionVersions() {
124 let copilotExtensionVersion;
125 let copilotChatExtensionVersion;
156 this._onDidChangeFilters.fire();
157 }
159 > private updateCopilotEntitlementInfo() {
160 const newSku = this._chatEntitlementService.sku;
161 const newTrackingId = this._chatEntitlementService.copilotTrackingId;
177 this._onDidChangeFilters.fire();
178 }
180 > private updateCopilotTokenInfo() {
181 const tokenInfo = this._defaultAccountService.copilotTokenInfo;
182 const newIsSn = tokenInfo?.sn === '1' ? '1' : '0';
196 this._onDidChangeFilters.fire();
197 }
199 > /**
200 > * Returns a version string that can be parsed by the TAS client.
201 > * The tas client cannot handle suffixes lke "-insider"
202 > * Ref: https://github.com/microsoft/tas-client/blob/30340d5e1da37c2789049fcf45928b954680606f/vscode-tas-client/src/vscode-tas-client/VSCodeFilterProvider.ts#L35
203 > *
204 > * @param version Version string to be trimmed.
205 > */
206 > private static trimVersionSuffix(version: string): string {
207 const regex = /\-[a-zA-Z0-9]+$/;
208 const result = version.split(regex);
210 return result[0];
211 }
213 > getFilterValue(filter: string): string | null {
214 switch (filter) {
215 case ExtensionsFilter.CopilotExtensionVersion:
233 }
234 }
236 > getFilters(): Map<string, string | null> {
237 const filters = new Map<string, string | null>();
238 const filterValues = Object.values(ExtensionsFilter);
src/vs/workbench/services/views/common/viewsService.ts 37 introduced LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- viewsService.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { Event } from '../../../../base/common/event.js';
7 > import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
8 > import { IProgressIndicator } from '../../../../platform/progress/common/progress.js';
9 > import { IPaneComposite } from '../../../common/panecomposite.js';
10 > import { IView, IViewDescriptor, IViewPaneContainer, ViewContainer, ViewContainerLocation } from '../../../common/views.js';
11 >
12 > export const IViewsService = createDecorator<IViewsService>('viewsService');
13 > export interface IViewsService {
14 >
15 > readonly _serviceBrand: undefined;
16 >
17 > // View Container APIs
18 > readonly onDidChangeViewContainerVisibility: Event<{ id: string; visible: boolean; location: ViewContainerLocation }>;
19 > isViewContainerVisible(id: string): boolean;
20 > isViewContainerActive(id: string): boolean;
21 > openViewContainer(id: string, focus?: boolean): Promise<IPaneComposite | null>;
22 > closeViewContainer(id: string): void;
23 > getVisibleViewContainer(location: ViewContainerLocation): ViewContainer | null;
24 > getActiveViewPaneContainerWithId(viewContainerId: string): IViewPaneContainer | null;
25 > getFocusedView(): IViewDescriptor | null;
26 > getFocusedViewName(): string;
27 >
28 > // View APIs
29 > readonly onDidChangeViewVisibility: Event<{ id: string; visible: boolean }>;
30 > readonly onDidChangeFocusedView: Event<void>;
31 > isViewVisible(id: string): boolean;
32 > openView<T extends IView>(id: string, focus?: boolean): Promise<T | null>;
33 > closeView(id: string): void;
34 > getActiveViewWithId<T extends IView>(id: string): T | null;
35 > getViewWithId<T extends IView>(id: string): T | null;
36 > getViewProgressIndicator(id: string): IProgressIndicator | undefined;
37 > }
src/vs/workbench/contrib/chat/common/model/chatTransferService.ts 35 introduced LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- chatTransferService.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 > import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js';
6 > import { IStorageService, StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js';
7 > import { IFileService } from '../../../../../platform/files/common/files.js';
8 > import { IWorkspaceTrustManagementService } from '../../../../../platform/workspace/common/workspaceTrust.js';
9 > import { areWorkspaceFoldersEmpty } from '../../../../services/workspaces/common/workspaceUtils.js';
10 > import { createDecorator } from '../../../../../platform/instantiation/common/instantiation.js';
11 > import { URI } from '../../../../../base/common/uri.js';
12 >
13 > export const IChatTransferService = createDecorator<IChatTransferService>('chatTransferService');
14 > const transferredWorkspacesKey = 'chat.transferedWorkspaces';
15 >
16 > export interface IChatTransferService {
17 > readonly _serviceBrand: undefined;
18 >
19 > checkAndSetTransferredWorkspaceTrust(): Promise<void>;
20 > addWorkspaceToTransferred(workspace: URI): void;
21 > }
22 >
23 > export class ChatTransferService implements IChatTransferService {
24 > _serviceBrand: undefined;
25 >
26 > constructor(
27 @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService,
28 @IStorageService private readonly storageService: IStorageService,
30 @IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService
31 ) { }
33 > private deleteWorkspaceFromTransferredList(workspace: URI): void {
34 const transferredWorkspaces = this.storageService.getObject<string[]>(transferredWorkspacesKey, StorageScope.PROFILE, []);
35 const updatedWorkspaces = transferredWorkspaces.filter(uri => uri !== workspace.toString());
36 this.storageService.store(transferredWorkspacesKey, updatedWorkspaces, StorageScope.PROFILE, StorageTarget.MACHINE);
37 }
39 > addWorkspaceToTransferred(workspace: URI): void {
40 const transferredWorkspaces = this.storageService.getObject<string[]>(transferredWorkspacesKey, StorageScope.PROFILE, []);
41 transferredWorkspaces.push(workspace.toString());
42 this.storageService.store(transferredWorkspacesKey, transferredWorkspaces, StorageScope.PROFILE, StorageTarget.MACHINE);
43 }
45 > async checkAndSetTransferredWorkspaceTrust(): Promise<void> {
46 const workspace = this.workspaceService.getWorkspace();
47 const currentWorkspaceUri = workspace.folders[0]?.uri;
54 }
55 }
57 > private isChatTransferredWorkspace(workspace: URI, storageService: IStorageService): boolean {
58 if (!workspace) {
59 return false;
62 return chatWorkspaceTransfer.some(item => item.toString() === workspace.toString());
63 }
src/vs/workbench/contrib/chat/test/common/languageModels.ts 17 introduced LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- languageModels.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CancellationToken } from '../../../../../base/common/cancellation.js';
7 > import { IStringDictionary } from '../../../../../base/common/collections.js';
8 > import { Event } from '../../../../../base/common/event.js';
9 > import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js';
10 > import { observableValue } from '../../../../../base/common/observable.js';
11 > import { IAction } from '../../../../../base/common/actions.js';
12 > import { ExtensionIdentifier } from '../../../../../platform/extensions/common/extensions.js';
13 > import { IChatMessage, IModelsControlManifest, ILanguageModelChatMetadata, ILanguageModelChatMetadataAndIdentifier, ILanguageModelChatProvider, ILanguageModelChatRequestOptions, ILanguageModelChatResponse, ILanguageModelChatSelector, ILanguageModelProviderDescriptor, ILanguageModelsGroup, ILanguageModelsService, IUserFriendlyLanguageModel } from '../../common/languageModels.js';
14 > import { ILanguageModelsProviderGroup } from '../../common/languageModelsConfiguration.js';
15 >
16 > export class NullLanguageModelsService implements ILanguageModelsService {
17 _serviceBrand: undefined;
18
140
141 restrictedChatParticipants = observableValue('restrictedChatParticipants', Object.create(null));
src/vs/workbench/services/assignment/test/common/nullAssignmentService.ts 16 introduced LOC · 4 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- nullAssignmentService.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { Event } from '../../../../../base/common/event.js';
7 > import { IAssignmentFilter, IWorkbenchAssignmentService } from '../../common/assignmentService.js';
8 >
9 > export class NullWorkbenchAssignmentService implements IWorkbenchAssignmentService {
10 _serviceBrand: undefined;
11
12 readonly onDidRefetchAssignments: Event<void> = Event.None;
14 > async getCurrentExperiments(): Promise<string[] | undefined> {
15 return [];
16 }
18 > async getTreatment<T extends string | number | boolean>(name: string): Promise<T | undefined> {
19 return undefined;
20 }
22 > addTelemetryAssignmentFilter(filter: IAssignmentFilter): void { }
23 > }
src/vs/workbench/services/telemetry/common/workbenchTelemetryUtils.ts 16 introduced LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- workbenchTelemetryUtils.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
7 > import { IProductService } from '../../../../platform/product/common/productService.js';
8 > import { TelemetryLevel } from '../../../../platform/telemetry/common/telemetry.js';
9 > import { getTelemetryLevel } from '../../../../platform/telemetry/common/telemetryUtils.js';
10 > import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js';
11 >
12 > /**
13 > * Determines if experiment properties will be set on telemetry events.
14 > * When true, TelemetryService should buffer events until setExperimentProperty is called.
15 > */
16 > export function experimentsEnabled(
17 configurationService: IConfigurationService,
18 productService: IProductService,
src/vs/workbench/services/workspaces/common/workspaceUtils.ts 7 introduced LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- workspaceUtils.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 > import { IWorkspace } from '../../../../platform/workspace/common/workspace.js';
6 > import { IFileService } from '../../../../platform/files/common/files.js';
7 >
8 export async function areWorkspaceFoldersEmpty(workspace: IWorkspace, fileService: IFileService): Promise<boolean> {
9 for (const folder of workspace.folders) {