@@ -17,6 +17,7 @@ import { getErrorDetail, toError } from "./error/errorUtils";
1717import { OAuthSessionManager } from "./oauth/sessionManager" ;
1818import { Remote } from "./remote/remote" ;
1919import { getRemoteSshExtension } from "./remote/sshExtension" ;
20+ import { ActivationTelemetry } from "./telemetry/startup" ;
2021import { registerUriHandler } from "./uri/uriHandler" ;
2122import { initVscodeProposed } from "./vscodeProposed" ;
2223import { ChatPanelProvider } from "./webviews/chat/chatPanelProvider" ;
@@ -30,6 +31,22 @@ const MY_WORKSPACES_TREE_ID = "myWorkspaces";
3031const ALL_WORKSPACES_TREE_ID = "allWorkspaces" ;
3132
3233export async function activate ( ctx : vscode . ExtensionContext ) : Promise < void > {
34+ const serviceContainer = new ServiceContainer ( ctx ) ;
35+ ctx . subscriptions . push ( serviceContainer ) ;
36+ const activationTelemetry = new ActivationTelemetry (
37+ serviceContainer . getTelemetryService ( ) ,
38+ ) ;
39+
40+ await activationTelemetry . trace ( ( ) =>
41+ doActivate ( ctx , serviceContainer , activationTelemetry ) ,
42+ ) ;
43+ }
44+
45+ async function doActivate (
46+ ctx : vscode . ExtensionContext ,
47+ serviceContainer : ServiceContainer ,
48+ activationTelemetry : ActivationTelemetry ,
49+ ) : Promise < void > {
3350 // The Remote SSH extension's proposed APIs are used to override the SSH host
3451 // name in VS Code itself. It's visually unappealing having a lengthy name!
3552 //
@@ -39,6 +56,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
3956 // Cursor and VSCode are covered by ms remote, and the only other is windsurf for now
4057 // Means that vscodium is not supported by this for now
4158
59+ activationTelemetry . setPhase ( "remote_ssh_extension" ) ;
4260 const remoteSshExtension = getRemoteSshExtension ( ) ;
4361
4462 let vscodeProposed : typeof vscode = vscode ;
@@ -57,24 +75,31 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
5775 }
5876
5977 // Initialize the global vscodeProposed module for use throughout the extension
78+ activationTelemetry . setPhase ( "proposed_api_init" ) ;
6079 initVscodeProposed ( vscodeProposed ) ;
6180
62- const serviceContainer = new ServiceContainer ( ctx ) ;
63- ctx . subscriptions . push ( serviceContainer ) ;
64-
6581 const output = serviceContainer . getLogger ( ) ;
6682 const mementoManager = serviceContainer . getMementoManager ( ) ;
6783 const secretsManager = serviceContainer . getSecretsManager ( ) ;
6884 const contextManager = serviceContainer . getContextManager ( ) ;
6985 const commandManager = serviceContainer . getCommandManager ( ) ;
7086
7187 // Migrate auth storage from old flat format to new label-based format
88+ activationTelemetry . setPhase ( "auth_migration" ) ;
7289 await migrateAuthStorage ( serviceContainer ) ;
7390
7491 // Clear and capture the startup mode before anything else.
92+ activationTelemetry . setPhase ( "startup_mode" ) ;
7593 const startupMode = await mementoManager . getAndClearStartupMode ( ) ;
7694
95+ activationTelemetry . setPhase ( "deployment_read" ) ;
7796 const deployment = await secretsManager . getCurrentDeployment ( ) ;
97+ const deploymentSessionAuth = deployment
98+ ? await secretsManager . getSessionAuth ( deployment . safeHostname )
99+ : undefined ;
100+ activationTelemetry . setAuthState (
101+ deploymentSessionAuth ? "valid_token" : "none" ,
102+ ) ;
78103
79104 // Shared handler for auth failures (used by interceptor + session manager)
80105 const handleAuthFailure = ( ) : Promise < void > => {
@@ -99,6 +124,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
99124 } ;
100125
101126 // Create OAuth session manager - callback handles background refresh failures
127+ activationTelemetry . setPhase ( "oauth_session" ) ;
102128 const oauthSessionManager = OAuthSessionManager . create (
103129 deployment ,
104130 serviceContainer ,
@@ -109,10 +135,10 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
109135 // This client tracks the current login and will be used through the life of
110136 // the plugin to poll workspaces for the current login, as well as being used
111137 // in commands that operate on the current login.
138+ activationTelemetry . setPhase ( "client_setup" ) ;
112139 const client = CoderApi . create (
113140 deployment ?. url || "" ,
114- ( await secretsManager . getSessionAuth ( deployment ?. safeHostname ?? "" ) )
115- ?. token ,
141+ deploymentSessionAuth ?. token ,
116142 output ,
117143 ) ;
118144 ctx . subscriptions . push ( client ) ;
@@ -132,6 +158,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
132158
133159 const isAuthenticated = ( ) => contextManager . get ( "coder.authenticated" ) ;
134160
161+ activationTelemetry . setPhase ( "tree_views" ) ;
135162 const myWorkspacesProvider = new WorkspaceProvider (
136163 WorkspaceQuery . Mine ,
137164 client ,
@@ -178,6 +205,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
178205 ) ;
179206
180207 // Create deployment manager to centralize deployment state management
208+ activationTelemetry . setPhase ( "deployment_manager" ) ;
181209 const deploymentManager = DeploymentManager . create (
182210 serviceContainer ,
183211 client ,
@@ -188,6 +216,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
188216
189217 // Register globally available commands. Many of these have visibility
190218 // controlled by contexts, see `when` in the package.json.
219+ activationTelemetry . setPhase ( "commands" ) ;
191220 const commands = new Commands ( serviceContainer , client , deploymentManager ) ;
192221
193222 // Placeholder tree view for the coderTasks container when not authenticated.
@@ -363,6 +392,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
363392 // (this would require the user to uninstall the Coder extension and
364393 // reinstall after installing the remote SSH extension, which is annoying)
365394 if ( remoteSshExtension && vscodeProposed . env . remoteAuthority ) {
395+ activationTelemetry . setPhase ( "remote_setup" ) ;
366396 try {
367397 const details = await remote . setup (
368398 vscodeProposed . env . remoteAuthority ,
@@ -372,11 +402,14 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
372402 if ( details ) {
373403 ctx . subscriptions . push ( details ) ;
374404
375- await deploymentManager . setDeploymentIfValid ( {
405+ const deploymentSet = await deploymentManager . setDeploymentIfValid ( {
376406 safeHostname : details . safeHostname ,
377407 url : details . url ,
378408 token : details . token ,
379409 } ) ;
410+ activationTelemetry . setAuthState (
411+ deploymentSet ? "valid_token" : "expired" ,
412+ ) ;
380413
381414 // If a deep link stored a chat agent ID before the
382415 // remote-authority reload, open it now that the
@@ -391,7 +424,9 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
391424 } catch ( ex ) {
392425 if ( ex instanceof CertificateError ) {
393426 output . warn ( ex . detail ) ;
394- await ex . showNotification ( "Failed to open workspace" , { modal : true } ) ;
427+ await ex . showNotification ( "Failed to open workspace" , {
428+ modal : true ,
429+ } ) ;
395430 } else if ( isAxiosError ( ex ) ) {
396431 const msg = getErrorMessage ( ex , "None" ) ;
397432 const detail = getErrorDetail ( ex ) || "None" ;
@@ -422,6 +457,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
422457 }
423458 // Always close remote session when we fail to open a workspace.
424459 await remote . closeRemote ( ) ;
460+ activationTelemetry . complete ( ) ;
425461 return ;
426462 }
427463 }
@@ -431,10 +467,12 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
431467 if ( deploymentManager . getCurrentDeployment ( ) ) {
432468 contextManager . set ( "coder.loaded" , true ) ;
433469 } else if ( deployment ) {
470+ activationTelemetry . setPhase ( "deployment_init" ) ;
434471 output . info ( `Initializing deployment: ${ deployment . url } ` ) ;
435- deploymentManager
436- . setDeploymentIfValid ( deployment )
437- // Failure is logged internally
472+ activationTelemetry
473+ . traceDeploymentInit ( ( ) =>
474+ deploymentManager . setDeploymentIfValid ( deployment ) ,
475+ )
438476 . then ( ( success ) => {
439477 if ( success ) {
440478 output . info ( "Deployment authenticated and set" ) ;
@@ -467,6 +505,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
467505 }
468506 }
469507 }
508+ activationTelemetry . complete ( ) ;
470509}
471510
472511/**
0 commit comments