Skip to content

Commit e32b95e

Browse files
authored
Merge pull request microsoft#291621 from microsoft/osortega/mean-warbler
2 parents a1728b9 + b6efe49 commit e32b95e

File tree

3 files changed

+129
-6
lines changed

3 files changed

+129
-6
lines changed

src/vs/workbench/contrib/welcomeAgentSessions/browser/agentSessionsWelcome.contribution.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { CommandsRegistry } from '../../../../platform/commands/common/commands.
2121
import { IStorageService, StorageScope } from '../../../../platform/storage/common/storage.js';
2222
import { AgentSessionsWelcomeInput } from './agentSessionsWelcomeInput.js';
2323
import { AgentSessionsWelcomePage, AgentSessionsWelcomeInputSerializer } from './agentSessionsWelcome.js';
24+
import { IWorkspaceContextService, WorkbenchState } from '../../../../platform/workspace/common/workspace.js';
2425

2526
// Registration priority
2627
const agentSessionsWelcomeInputTypeId = 'workbench.editors.agentSessionsWelcomeInput';
@@ -41,13 +42,29 @@ Registry.as<IEditorPaneRegistry>(EditorExtensions.EditorPane).registerEditorPane
4142
]
4243
);
4344

45+
const getWorkspaceKind = (workspaceContextService: IWorkspaceContextService) => {
46+
const state = workspaceContextService.getWorkbenchState();
47+
switch (state) {
48+
case WorkbenchState.EMPTY:
49+
return 'empty';
50+
case WorkbenchState.FOLDER:
51+
return 'folder';
52+
case WorkbenchState.WORKSPACE:
53+
return 'workspace';
54+
default:
55+
return 'empty';
56+
57+
}
58+
};
59+
4460
// Register resolver contribution
4561
class AgentSessionsWelcomeEditorResolverContribution extends Disposable implements IWorkbenchContribution {
4662
static readonly ID = 'workbench.contrib.agentSessionsWelcomeEditorResolver';
4763

4864
constructor(
4965
@IEditorResolverService editorResolverService: IEditorResolverService,
5066
@IInstantiationService instantiationService: IInstantiationService,
67+
@IWorkspaceContextService workspaceContextService: IWorkspaceContextService,
5168
) {
5269
super();
5370

@@ -67,7 +84,7 @@ class AgentSessionsWelcomeEditorResolverContribution extends Disposable implemen
6784
{
6885
createEditorInput: () => {
6986
return {
70-
editor: instantiationService.createInstance(AgentSessionsWelcomeInput, {}),
87+
editor: instantiationService.createInstance(AgentSessionsWelcomeInput, { workspaceKind: getWorkspaceKind(workspaceContextService) }),
7188
};
7289
}
7390
}
@@ -79,7 +96,8 @@ class AgentSessionsWelcomeEditorResolverContribution extends Disposable implemen
7996
CommandsRegistry.registerCommand(AgentSessionsWelcomePage.COMMAND_ID, (accessor) => {
8097
const editorService = accessor.get(IEditorService);
8198
const instantiationService = accessor.get(IInstantiationService);
82-
const input = instantiationService.createInstance(AgentSessionsWelcomeInput, {});
99+
const workspaceContextService = accessor.get(IWorkspaceContextService);
100+
const input = instantiationService.createInstance(AgentSessionsWelcomeInput, { initiator: 'command', workspaceKind: getWorkspaceKind(workspaceContextService) });
83101
return editorService.openEditor(input, { pinned: true });
84102
});
85103

@@ -93,7 +111,8 @@ class AgentSessionsWelcomeRunnerContribution extends Disposable implements IWork
93111
@IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService,
94112
@IInstantiationService private readonly instantiationService: IInstantiationService,
95113
@IContextKeyService private readonly contextKeyService: IContextKeyService,
96-
@IStorageService private readonly storageService: IStorageService
114+
@IStorageService private readonly storageService: IStorageService,
115+
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService
97116
) {
98117
super();
99118
this.run();
@@ -125,7 +144,7 @@ class AgentSessionsWelcomeRunnerContribution extends Disposable implements IWork
125144
}
126145

127146
// Open the agent sessions welcome page
128-
const input = this.instantiationService.createInstance(AgentSessionsWelcomeInput, {});
147+
const input = this.instantiationService.createInstance(AgentSessionsWelcomeInput, { initiator: 'startup', workspaceKind: getWorkspaceKind(this.workspaceContextService) });
129148
await this.editorService.openEditor(input, { pinned: false });
130149
}
131150
}

src/vs/workbench/contrib/welcomeAgentSessions/browser/agentSessionsWelcome.ts

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import { ChatWidget } from '../../chat/browser/widget/chatWidget.js';
3939
import { IAgentSessionsService } from '../../chat/browser/agentSessions/agentSessionsService.js';
4040
import { AgentSessionProviders } from '../../chat/browser/agentSessions/agentSessions.js';
4141
import { IAgentSession } from '../../chat/browser/agentSessions/agentSessionsModel.js';
42-
import { AgentSessionsWelcomeEditorOptions, AgentSessionsWelcomeInput } from './agentSessionsWelcomeInput.js';
42+
import { AgentSessionsWelcomeEditorOptions, AgentSessionsWelcomeInput, AgentSessionsWelcomeWorkspaceKind } from './agentSessionsWelcomeInput.js';
4343
import { IChatService } from '../../chat/common/chatService/chatService.js';
4444
import { IChatModel } from '../../chat/common/model/chatModel.js';
4545
import { ChatViewId, ChatViewPaneTarget, IChatWidgetService, ISessionTypePickerDelegate, IWorkspacePickerDelegate, IWorkspacePickerItem } from '../../chat/browser/chat.js';
@@ -65,6 +65,42 @@ const MAX_SESSIONS = 6;
6565
const MAX_REPO_PICKS = 10;
6666
const MAX_WALKTHROUGHS = 10;
6767

68+
/**
69+
* - visibleDurationMs: Do they close it right away or leave it open (#3)
70+
* - closedBy: Track what action caused the close (viewAllSessions, chatSubmission, sessionClicked, etc.) (#5)
71+
*/
72+
type AgentSessionsWelcomeClosedClassification = {
73+
visibleDurationMs: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'How long the welcome page was visible in milliseconds.' };
74+
closedBy: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'What action caused the welcome page to close.' };
75+
owner: 'osortega';
76+
comment: 'Tracks when the agent sessions welcome page is closed to understand engagement.';
77+
};
78+
79+
type AgentSessionsWelcomeClosedEvent = {
80+
visibleDurationMs: number;
81+
closedBy: string;
82+
};
83+
84+
/**
85+
* - mode/provider/workspaceKind: Track agent type, session provider, and workspace state (#4)
86+
* - selectedRecentWorkspace: Do users select a recent workspace before submitting chat (#8)
87+
*/
88+
type AgentSessionsWelcomeChatSubmittedClassification = {
89+
mode: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The chat mode used (ask, agent, edit).' };
90+
provider: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The session provider (local, cloud).' };
91+
workspaceKind: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The type of workspace - empty, folder, or workspace.' };
92+
selectedRecentWorkspace: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether a recent workspace was selected before submitting.' };
93+
owner: 'osortega';
94+
comment: 'Tracks chat submissions from the welcome page to understand session creation patterns.';
95+
};
96+
97+
type AgentSessionsWelcomeChatSubmittedEvent = {
98+
mode: string;
99+
provider: string;
100+
workspaceKind: AgentSessionsWelcomeWorkspaceKind;
101+
selectedRecentWorkspace: boolean;
102+
};
103+
68104
type AgentSessionsWelcomeActionClassification = {
69105
action: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'The action being executed on the agent sessions welcome page.' };
70106
actionId: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'Identifier of the action being executed, such as command ID or walkthrough ID.' };
@@ -100,6 +136,11 @@ export class AgentSessionsWelcomePage extends EditorPane {
100136
private _selectedWorkspace: IWorkspacePickerItem | undefined;
101137
private _recentWorkspaces: Array<IRecentWorkspace | IRecentFolder> = [];
102138
private _isEmptyWorkspace: boolean = false;
139+
private _workspaceKind: AgentSessionsWelcomeWorkspaceKind = 'empty';
140+
141+
// Telemetry tracking
142+
private _openedAt: number = 0;
143+
private _closedBy: string = 'unknown';
103144

104145
constructor(
105146
group: IEditorGroup,
@@ -152,6 +193,7 @@ export class AgentSessionsWelcomePage extends EditorPane {
152193

153194
override async setInput(input: AgentSessionsWelcomeInput, options: AgentSessionsWelcomeEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise<void> {
154195
await super.setInput(input, options, context, token);
196+
this._workspaceKind = input.workspaceKind ?? 'empty';
155197
await this.buildContent();
156198
}
157199

@@ -354,6 +396,19 @@ export class AgentSessionsWelcomePage extends EditorPane {
354396
// Automatically open the chat view when a request is submitted from this welcome view
355397
this.contentDisposables.add(this.chatService.onDidSubmitRequest(({ chatSessionResource }) => {
356398
if (this.chatModelRef?.object?.sessionResource.toString() === chatSessionResource.toString()) {
399+
// Send chat submitted telemetry
400+
const mode = this.chatWidget?.input.currentModeObs.get().name.get() || 'unknown';
401+
this.telemetryService.publicLog2<AgentSessionsWelcomeChatSubmittedEvent, AgentSessionsWelcomeChatSubmittedClassification>(
402+
'agentSessionsWelcome.chatSubmitted',
403+
{
404+
mode,
405+
provider: this._selectedSessionProvider,
406+
workspaceKind: this._workspaceKind,
407+
selectedRecentWorkspace: this._selectedWorkspace !== undefined
408+
}
409+
);
410+
411+
this._closedBy = 'chatSubmission';
357412
this.openSessionInChat(chatSessionResource);
358413
}
359414
}));
@@ -529,6 +584,7 @@ export class AgentSessionsWelcomePage extends EditorPane {
529584
trackActiveEditorSession: () => false,
530585
source: 'welcomeView',
531586
notifySessionOpened: () => {
587+
this._closedBy = 'sessionClicked';
532588
const isProjectionEnabled = this.configurationService.getValue<boolean>(ChatConfiguration.AgentSessionProjectionEnabled);
533589
if (!isProjectionEnabled) {
534590
this.revealMaximizedChat();
@@ -560,6 +616,7 @@ export class AgentSessionsWelcomePage extends EditorPane {
560616
const openButton = append(container, $('button.agentSessionsWelcome-openSessionsButton'));
561617
openButton.textContent = localize('viewAllSessions', "View All Sessions");
562618
openButton.onclick = () => {
619+
this._closedBy = 'viewAllSessions';
563620
this.revealMaximizedChat();
564621
};
565622
}
@@ -833,6 +890,22 @@ export class AgentSessionsWelcomePage extends EditorPane {
833890
this.layoutService.setAuxiliaryBarMaximized(true);
834891
}
835892
}
893+
894+
override dispose(): void {
895+
// Send closed telemetry before disposing
896+
if (this._openedAt > 0) {
897+
const visibleDurationMs = Date.now() - this._openedAt;
898+
this.telemetryService.publicLog2<AgentSessionsWelcomeClosedEvent, AgentSessionsWelcomeClosedClassification>(
899+
'agentSessionsWelcome.closed',
900+
{
901+
visibleDurationMs,
902+
closedBy: this._closedBy
903+
}
904+
);
905+
}
906+
907+
super.dispose();
908+
}
836909
}
837910

838911
export class AgentSessionsWelcomeInputSerializer implements IEditorSerializer {

src/vs/workbench/contrib/welcomeAgentSessions/browser/agentSessionsWelcomeInput.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@ import { IUntypedEditorInput } from '../../../common/editor.js';
1111
import { IEditorOptions } from '../../../../platform/editor/common/editor.js';
1212

1313
export const agentSessionsWelcomeInputTypeId = 'workbench.editors.agentSessionsWelcomeInput';
14+
export type AgentSessionsWelcomeInitiator = 'startup' | 'command';
15+
export type AgentSessionsWelcomeWorkspaceKind = 'empty' | 'folder' | 'workspace';
1416

1517
export interface AgentSessionsWelcomeEditorOptions extends IEditorOptions {
1618
showTelemetryNotice?: boolean;
19+
initiator?: AgentSessionsWelcomeInitiator;
20+
workspaceKind?: AgentSessionsWelcomeWorkspaceKind;
1721
}
1822

1923
export class AgentSessionsWelcomeInput extends EditorInput {
@@ -22,6 +26,8 @@ export class AgentSessionsWelcomeInput extends EditorInput {
2226
static readonly RESOURCE = URI.from({ scheme: Schemas.walkThrough, authority: 'vscode_agent_sessions_welcome' });
2327

2428
private _showTelemetryNotice: boolean;
29+
private _initiator: AgentSessionsWelcomeInitiator;
30+
private _workspaceKind?: AgentSessionsWelcomeWorkspaceKind;
2531

2632
override get typeId(): string {
2733
return AgentSessionsWelcomeInput.ID;
@@ -53,9 +59,13 @@ export class AgentSessionsWelcomeInput extends EditorInput {
5359
return other instanceof AgentSessionsWelcomeInput;
5460
}
5561

56-
constructor(options: AgentSessionsWelcomeEditorOptions = {}) {
62+
constructor(
63+
options: AgentSessionsWelcomeEditorOptions = {},
64+
) {
5765
super();
5866
this._showTelemetryNotice = !!options.showTelemetryNotice;
67+
this._initiator = options.initiator ?? 'command';
68+
this._workspaceKind = options.workspaceKind;
5969
}
6070

6171
override getName() {
@@ -69,4 +79,25 @@ export class AgentSessionsWelcomeInput extends EditorInput {
6979
set showTelemetryNotice(value: boolean) {
7080
this._showTelemetryNotice = value;
7181
}
82+
83+
get initiator(): AgentSessionsWelcomeInitiator {
84+
return this._initiator;
85+
}
86+
87+
get workspaceKind(): AgentSessionsWelcomeWorkspaceKind | undefined {
88+
return this._workspaceKind;
89+
}
90+
91+
override getTelemetryDescriptor(): { [key: string]: unknown } {
92+
const descriptor = super.getTelemetryDescriptor();
93+
descriptor['initiator'] = this._initiator;
94+
descriptor['workspaceKind'] = this._workspaceKind;
95+
/* __GDPR__FRAGMENT__
96+
"EditorTelemetryDescriptor" : {
97+
"initiator" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "How the welcome page was opened - startup or command." },
98+
"workspaceKind" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The type of workspace - empty, folder, or workspace." }
99+
}
100+
*/
101+
return descriptor;
102+
}
72103
}

0 commit comments

Comments
 (0)