Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions src/vs/sessions/common/sessionsTelemetry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { ITelemetryService } from '../../platform/telemetry/common/telemetry.js';

// --- Titlebar button interactions ---

export type SessionsInteractionButton =
| 'newSession'
| 'runPrimaryTask'
| 'addTask'
| 'generateNewTask'
| 'openTerminal'
| 'openInVSCode';

type SessionsInteractionEvent = {
button: string;
};

type SessionsInteractionClassification = {
owner: 'osortega';
comment: 'Tracks user interactions with buttons in the Agents window';
button: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The identifier of the button that was clicked' };
};

/**
* Log a titlebar button interaction in the Agents window.
*/
export function logSessionsInteraction(telemetryService: ITelemetryService, button: SessionsInteractionButton): void {
telemetryService.publicLog2<SessionsInteractionEvent, SessionsInteractionClassification>('vscodeAgents.interaction', { button });
}

// --- Changes panel interactions ---

type ChangesViewTogglePanelEvent = {
visible: boolean;
};

type ChangesViewTogglePanelClassification = {
owner: 'osortega';
comment: 'Tracks when the user toggles the Changes panel open or closed.';
visible: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the Changes panel is now visible.' };
};

export function logChangesViewToggle(telemetryService: ITelemetryService, visible: boolean): void {
telemetryService.publicLog2<ChangesViewTogglePanelEvent, ChangesViewTogglePanelClassification>('vscodeAgents.changesView/togglePanel', { visible });
}

type ChangesViewVersionModeChangeEvent = {
mode: string;
};

type ChangesViewVersionModeChangeClassification = {
owner: 'osortega';
comment: 'Tracks when the user switches the version mode in the Changes panel (Branch Changes, All Changes, Last Turn).';
mode: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The version mode selected by the user.' };
};

export function logChangesViewVersionModeChange(telemetryService: ITelemetryService, mode: string): void {
telemetryService.publicLog2<ChangesViewVersionModeChangeEvent, ChangesViewVersionModeChangeClassification>('vscodeAgents.changesView/versionModeChange', { mode });
}

type ChangesViewFileSelectEvent = {
changeType: string;
};

type ChangesViewFileSelectClassification = {
owner: 'osortega';
comment: 'Tracks when the user selects a changed file in the Changes panel.';
changeType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The type of change (added, modified, deleted).' };
};

export function logChangesViewFileSelect(telemetryService: ITelemetryService, changeType: string): void {
telemetryService.publicLog2<ChangesViewFileSelectEvent, ChangesViewFileSelectClassification>('vscodeAgents.changesView/fileSelect', { changeType });
}

type ChangesViewViewModeChangeEvent = {
mode: string;
};

type ChangesViewViewModeChangeClassification = {
owner: 'osortega';
comment: 'Tracks when the user switches between list and tree view modes in the Changes panel.';
mode: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The view mode selected by the user (list or tree).' };
};

export function logChangesViewViewModeChange(telemetryService: ITelemetryService, mode: string): void {
telemetryService.publicLog2<ChangesViewViewModeChangeEvent, ChangesViewViewModeChangeClassification>('vscodeAgents.changesView/viewModeChange', { mode });
}

type ChangesViewReviewCommentAddedEvent = {
hasExistingFeedback: boolean;
hasSuggestion: boolean;
isFromPRReview: boolean;
};

type ChangesViewReviewCommentAddedClassification = {
owner: 'osortega';
comment: 'Tracks when a user adds a review comment (feedback) to a file in the Changes panel.';
hasExistingFeedback: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether there was already feedback on this file.' };
hasSuggestion: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the feedback includes a code suggestion.' };
isFromPRReview: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the feedback was converted from a PR review comment.' };
};

export function logChangesViewReviewCommentAdded(telemetryService: ITelemetryService, data: { hasExistingFeedback: boolean; hasSuggestion: boolean; isFromPRReview: boolean }): void {
telemetryService.publicLog2<ChangesViewReviewCommentAddedEvent, ChangesViewReviewCommentAddedClassification>('vscodeAgents.changesView/reviewCommentAdded', data);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { IChatWidgetService } from '../../../../workbench/contrib/chat/browser/c
import { ICommandService } from '../../../../platform/commands/common/commands.js';
import { ILogService } from '../../../../platform/log/common/log.js';
import { ICodeReviewSuggestion } from '../../codeReview/browser/codeReviewService.js';
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
import { logChangesViewReviewCommentAdded } from '../../../common/sessionsTelemetry.js';

// --- Types --------------------------------------------------------------------

Expand Down Expand Up @@ -144,6 +146,7 @@ export class AgentFeedbackService extends Disposable implements IAgentFeedbackSe
@IChatWidgetService private readonly _chatWidgetService: IChatWidgetService,
@ICommandService private readonly _commandService: ICommandService,
@ILogService private readonly _logService: ILogService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
) {
super();
}
Expand Down Expand Up @@ -201,6 +204,12 @@ export class AgentFeedbackService extends Disposable implements IAgentFeedbackSe

this._onDidChangeFeedback.fire({ sessionResource, feedbackItems });

logChangesViewReviewCommentAdded(this._telemetryService, {
hasExistingFeedback: hasExistingForFile,
hasSuggestion: !!suggestion,
isFromPRReview: !!sourcePRReviewCommentId,
});

return feedback;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { AgentFeedbackService, IAgentFeedbackService } from '../../browser/agent
import { IChatEditingService } from '../../../../../workbench/contrib/chat/common/editing/chatEditingService.js';
import { IAgentSessionsService } from '../../../../../workbench/contrib/chat/browser/agentSessions/agentSessionsService.js';
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
import { NullTelemetryService } from '../../../../../platform/telemetry/common/telemetryUtils.js';
import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js';

function r(startLine: number, endLine: number = startLine): Range {
return new Range(startLine, 1, endLine, 1);
Expand All @@ -36,6 +38,7 @@ suite('AgentFeedbackService - Ordering', () => {

instantiationService.stub(IChatEditingService, new class extends mock<IChatEditingService>() { });
instantiationService.stub(IAgentSessionsService, new class extends mock<IAgentSessionsService>() { });
instantiationService.stub(ITelemetryService, NullTelemetryService);

service = store.add(instantiationService.createInstance(AgentFeedbackService));
session = URI.parse('test://session/1');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ import { IsAuxiliaryWindowContext, AuxiliaryBarVisibleContext } from '../../../.
import { getAgentChangesSummary } from '../../../../workbench/contrib/chat/browser/agentSessions/agentSessionsModel.js';
import { IWorkbenchLayoutService, Parts } from '../../../../workbench/services/layout/browser/layoutService.js';
import { IPaneCompositePartService } from '../../../../workbench/services/panecomposite/browser/panecomposite.js';
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
import { ViewContainerLocation } from '../../../../workbench/common/views.js';
import { Menus } from '../../../browser/menus.js';
import { SessionsWelcomeVisibleContext } from '../../../common/contextkeys.js';
import { logChangesViewToggle } from '../../../common/sessionsTelemetry.js';
import { ISessionsManagementService } from '../../sessions/browser/sessionsManagementService.js';
import { CHANGES_VIEW_CONTAINER_ID } from './changesView.js';

Expand Down Expand Up @@ -182,11 +184,14 @@ registerAction2(class extends Action2 {
run(accessor: ServicesAccessor): void {
const layoutService = accessor.get(IWorkbenchLayoutService);
const paneCompositeService = accessor.get(IPaneCompositePartService);
const telemetryService = accessor.get(ITelemetryService);

const isVisible = !layoutService.isVisible(Parts.AUXILIARYBAR_PART);
layoutService.setPartHidden(!isVisible, Parts.AUXILIARYBAR_PART);
if (isVisible) {
paneCompositeService.openPaneComposite(CHANGES_VIEW_CONTAINER_ID, ViewContainerLocation.AuxiliaryBar);
}

logChangesViewToggle(telemetryService, isVisible);
}
});
17 changes: 13 additions & 4 deletions src/vs/sessions/contrib/changes/browser/changesView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import { IView, Sizing, SplitView } from '../../../../base/browser/ui/splitview/
import { Color } from '../../../../base/common/color.js';
import { PANEL_SECTION_BORDER } from '../../../../workbench/common/theme.js';
import { EditorResourceAccessor, SideBySideEditor } from '../../../../workbench/common/editor.js';
import { logChangesViewFileSelect, logChangesViewVersionModeChange, logChangesViewViewModeChange } from '../../../common/sessionsTelemetry.js';

const $ = dom.$;

Expand Down Expand Up @@ -551,7 +552,8 @@ export class ChangesViewPane extends ViewPane {
@ISessionsManagementService private readonly sessionManagementService: ISessionsManagementService,
@ILabelService private readonly labelService: ILabelService,
@ICodeReviewService private readonly codeReviewService: ICodeReviewService,
@IGitHubService private readonly gitHubService: IGitHubService
@IGitHubService private readonly gitHubService: IGitHubService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
) {
super({ ...options, titleMenuId: MenuId.ChatEditingSessionTitleToolbar }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService);

Expand Down Expand Up @@ -1119,6 +1121,8 @@ export class ChangesViewPane extends ViewPane {
return;
}

logChangesViewFileSelect(this.telemetryService, e.element.changeType);

const items = combinedEntriesObs.get();
openFileItem(e.element, items, e.sideBySide, !!e.editorOptions?.preserveFocus, !!e.editorOptions?.pinned, items.length > 1);
}));
Expand Down Expand Up @@ -1748,7 +1752,8 @@ class SetChangesListViewModeAction extends ViewAction<ChangesViewPane> {
});
}

async runInView(_: ServicesAccessor, view: ChangesViewPane): Promise<void> {
async runInView(accessor: ServicesAccessor, view: ChangesViewPane): Promise<void> {
logChangesViewViewModeChange(accessor.get(ITelemetryService), ChangesViewMode.List);
view.viewModel.setViewMode(ChangesViewMode.List);
}
}
Expand All @@ -1770,7 +1775,8 @@ class SetChangesTreeViewModeAction extends ViewAction<ChangesViewPane> {
});
}

async runInView(_: ServicesAccessor, view: ChangesViewPane): Promise<void> {
async runInView(accessor: ServicesAccessor, view: ChangesViewPane): Promise<void> {
logChangesViewViewModeChange(accessor.get(ITelemetryService), ChangesViewMode.Tree);
view.viewModel.setViewMode(ChangesViewMode.Tree);
}
}
Expand Down Expand Up @@ -1810,7 +1816,7 @@ class ChangesPickerActionItem extends ActionWidgetDropdownActionViewItem {
@IKeybindingService keybindingService: IKeybindingService,
@IContextKeyService contextKeyService: IContextKeyService,
@ISessionsManagementService sessionManagementService: ISessionsManagementService,
@ITelemetryService telemetryService: ITelemetryService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
) {
const actionProvider: IActionWidgetDropdownActionProvider = {
getActions: () => {
Expand All @@ -1829,6 +1835,7 @@ class ChangesPickerActionItem extends ActionWidgetDropdownActionViewItem {
category: { label: 'changes', order: 1, showHeader: false },
run: async () => {
viewModel.setVersionMode(ChangesVersionMode.BranchChanges);
logChangesViewVersionModeChange(this.telemetryService, ChangesVersionMode.BranchChanges);
if (this.element) {
this.renderLabel(this.element);
}
Expand All @@ -1845,6 +1852,7 @@ class ChangesPickerActionItem extends ActionWidgetDropdownActionViewItem {
viewModel.activeSessionLastCheckpointRefObs.get() !== undefined,
run: async () => {
viewModel.setVersionMode(ChangesVersionMode.AllChanges);
logChangesViewVersionModeChange(this.telemetryService, ChangesVersionMode.AllChanges);
if (this.element) {
this.renderLabel(this.element);
}
Expand All @@ -1861,6 +1869,7 @@ class ChangesPickerActionItem extends ActionWidgetDropdownActionViewItem {
viewModel.activeSessionLastCheckpointRefObs.get() !== undefined,
run: async () => {
viewModel.setVersionMode(ChangesVersionMode.LastTurn);
logChangesViewVersionModeChange(this.telemetryService, ChangesVersionMode.LastTurn);
if (this.element) {
this.renderLabel(this.element);
}
Expand Down
5 changes: 5 additions & 0 deletions src/vs/sessions/contrib/chat/browser/chat.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { Schemas } from '../../../../base/common/network.js';
import { URI } from '../../../../base/common/uri.js';
import { IOpenerService } from '../../../../platform/opener/common/opener.js';
import { IProductService } from '../../../../platform/product/common/productService.js';
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
import { logSessionsInteraction } from '../../../common/sessionsTelemetry.js';
import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../../workbench/common/contributions.js';
import { IViewContainersRegistry, IViewsRegistry, ViewContainerLocation, Extensions as ViewExtensions, WindowVisibility } from '../../../../workbench/common/views.js';
import { Registry } from '../../../../platform/registry/common/platform.js';
Expand Down Expand Up @@ -60,6 +62,9 @@ export class OpenSessionWorktreeInVSCodeAction extends Action2 {
}

override async run(accessor: ServicesAccessor): Promise<void> {
const telemetryService = accessor.get(ITelemetryService);
logSessionsInteraction(telemetryService, 'openInVSCode');

const openerService = accessor.get(IOpenerService);
const productService = accessor.get(IProductService);
const sessionsManagementService = accessor.get(ISessionsManagementService);
Expand Down
6 changes: 6 additions & 0 deletions src/vs/sessions/contrib/chat/browser/runScriptAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { KeybindingsRegistry, KeybindingWeight } from '../../../../platform/keyb
import { IQuickInputButton, IQuickInputService, IQuickPickItem, IQuickPickSeparator } from '../../../../platform/quickinput/common/quickInput.js';
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
import { IWorkbenchContribution } from '../../../../workbench/common/contributions.js';
import { logSessionsInteraction } from '../../../common/sessionsTelemetry.js';
import { IWorkbenchLayoutService } from '../../../../workbench/services/layout/browser/layoutService.js';
import { SessionsCategories } from '../../../common/categories.js';
import { ISessionsManagementService } from '../../sessions/browser/sessionsManagementService.js';
Expand Down Expand Up @@ -121,6 +122,7 @@ export class RunScriptContribution extends Disposable implements IWorkbenchContr
@ISessionsConfigurationService private readonly _sessionsConfigService: ISessionsConfigurationService,
@IActionViewItemService private readonly _actionViewItemService: IActionViewItemService,
@IWorkbenchLayoutService private readonly _layoutService: IWorkbenchLayoutService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
) {
super();

Expand Down Expand Up @@ -194,6 +196,8 @@ export class RunScriptContribution extends Disposable implements IWorkbenchContr
return;
}

logSessionsInteraction(that._telemetryService, 'runPrimaryTask');

const { tasks, session } = activeState;
if (tasks.length === 0) {
const task = await that._showConfigureQuickPick(session);
Expand Down Expand Up @@ -238,6 +242,7 @@ export class RunScriptContribution extends Disposable implements IWorkbenchContr
}

async run(): Promise<void> {
logSessionsInteraction(that._telemetryService, 'addTask');
const task = await that._showConfigureQuickPick(session);
if (task) {
await that._sessionsConfigService.runTask(task, session);
Expand All @@ -261,6 +266,7 @@ export class RunScriptContribution extends Disposable implements IWorkbenchContr
}

async run(): Promise<void> {
logSessionsInteraction(that._telemetryService, 'generateNewTask');
await that._sessionManagementService.sendAndCreateChat(session, { query: '/generate-run-commands' });
}
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ import { Action2, MenuId, registerAction2 } from '../../../../../platform/action
import { Button } from '../../../../../base/browser/ui/button/button.js';
import { defaultButtonStyles } from '../../../../../platform/theme/browser/defaultStyles.js';
import { IStorageService, StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js';
import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js';
import { IHostService } from '../../../../../workbench/services/host/browser/host.js';
import { logSessionsInteraction } from '../../../../common/sessionsTelemetry.js';

const $ = DOM.$;
export const SessionsViewId = 'sessions.workbench.view.sessionsView';
Expand Down Expand Up @@ -70,6 +72,7 @@ export class SessionsView extends ViewPane {
@ISessionsManagementService private readonly sessionsManagementService: ISessionsManagementService,
@IHostService private readonly hostService: IHostService,
@IStorageService private readonly storageService: IStorageService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
) {
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService);

Expand Down Expand Up @@ -133,7 +136,10 @@ export class SessionsView extends ViewPane {
supportIcons: true,
}));
newSessionButton.label = `$(${Codicon.plus.id}) ${localize('sessionLabel', "Session")}`;
this._register(newSessionButton.onDidClick(() => this.sessionsManagementService.openNewSessionView()));
this._register(newSessionButton.onDidClick(() => {
logSessionsInteraction(this.telemetryService, 'newSession');
this.sessionsManagementService.openNewSessionView();
}));

const buttonLabel = $('.new-session-button-label');
const keybindingHint = $('span.new-session-keybinding-hint');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import { ISessionsManagementService } from '../../sessions/browser/sessionsManag
import { ISession } from '../../sessions/common/sessionData.js';
import { IsAuxiliaryWindowContext } from '../../../../workbench/common/contextkeys.js';
import { ContextKeyExpr, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
import { SessionsWelcomeVisibleContext } from '../../../common/contextkeys.js';
import { logSessionsInteraction } from '../../../common/sessionsTelemetry.js';
import { IViewsService } from '../../../../workbench/services/views/common/viewsService.js';
import { TERMINAL_VIEW_ID } from '../../../../workbench/contrib/terminal/common/terminal.js';
import { IWorkbenchLayoutService, Parts } from '../../../../workbench/services/layout/browser/layoutService.js';
Expand Down Expand Up @@ -319,6 +321,9 @@ class OpenSessionInTerminalAction extends Action2 {
}

override async run(_accessor: ServicesAccessor): Promise<void> {
const telemetryService = _accessor.get(ITelemetryService);
logSessionsInteraction(telemetryService, 'openTerminal');

const layoutService = _accessor.get(IWorkbenchLayoutService);
const viewsService = _accessor.get(IViewsService);

Expand Down
Loading