@@ -11,6 +11,7 @@ import {
1111 IDeepnoteKernelAutoSelector ,
1212 IDeepnoteServerStarter ,
1313 IDeepnoteToolkitInstaller ,
14+ IDeepnoteServerProvider ,
1415 DEEPNOTE_NOTEBOOK_TYPE ,
1516 DeepnoteKernelConnectionMetadata
1617} from '../../kernels/deepnote/types' ;
@@ -19,7 +20,6 @@ import { JVSC_EXTENSION_ID } from '../../platform/common/constants';
1920import { getDisplayPath } from '../../platform/common/platform/fs-paths' ;
2021import { JupyterServerProviderHandle } from '../../kernels/jupyter/types' ;
2122import { IPythonExtensionChecker } from '../../platform/api/types' ;
22- import { DeepnoteServerProvider } from '../../kernels/deepnote/deepnoteServerProvider.node' ;
2323import { JupyterLabHelper } from '../../kernels/jupyter/session/jupyterLabHelper' ;
2424import { createJupyterConnectionInfo } from '../../kernels/jupyter/jupyterUtils' ;
2525import { IJupyterRequestCreator , IJupyterRequestAgentCreator } from '../../kernels/jupyter/types' ;
@@ -31,14 +31,17 @@ import { disposeAsync } from '../../platform/common/utils';
3131 */
3232@injectable ( )
3333export class DeepnoteKernelAutoSelector implements IDeepnoteKernelAutoSelector , IExtensionSyncActivationService {
34+ // Track server handles per notebook URI for cleanup
35+ private readonly notebookServerHandles = new Map < string , string > ( ) ;
36+
3437 constructor (
3538 @inject ( IDisposableRegistry ) private readonly disposables : IDisposableRegistry ,
3639 @inject ( IControllerRegistration ) private readonly controllerRegistration : IControllerRegistration ,
3740 @inject ( IInterpreterService ) private readonly interpreterService : IInterpreterService ,
3841 @inject ( IDeepnoteToolkitInstaller ) private readonly toolkitInstaller : IDeepnoteToolkitInstaller ,
3942 @inject ( IDeepnoteServerStarter ) private readonly serverStarter : IDeepnoteServerStarter ,
4043 @inject ( IPythonExtensionChecker ) private readonly pythonExtensionChecker : IPythonExtensionChecker ,
41- @inject ( DeepnoteServerProvider ) private readonly serverProvider : DeepnoteServerProvider ,
44+ @inject ( IDeepnoteServerProvider ) private readonly serverProvider : IDeepnoteServerProvider ,
4245 @inject ( IJupyterRequestCreator ) private readonly requestCreator : IJupyterRequestCreator ,
4346 @inject ( IJupyterRequestAgentCreator )
4447 @optional ( )
@@ -50,8 +53,13 @@ export class DeepnoteKernelAutoSelector implements IDeepnoteKernelAutoSelector,
5053 // Listen to notebook open events
5154 workspace . onDidOpenNotebookDocument ( this . onDidOpenNotebook , this , this . disposables ) ;
5255
53- // Handle currently open notebooks
54- workspace . notebookDocuments . forEach ( ( d ) => this . onDidOpenNotebook ( d ) ) ;
56+ // Listen to notebook close events for cleanup
57+ workspace . onDidCloseNotebookDocument ( this . onDidCloseNotebook , this , this . disposables ) ;
58+
59+ // Handle currently open notebooks - await all async operations
60+ Promise . all ( workspace . notebookDocuments . map ( ( d ) => this . onDidOpenNotebook ( d ) ) ) . catch ( ( error ) => {
61+ logger . error ( `Error handling open notebooks during activation: ${ error } ` ) ;
62+ } ) ;
5563 }
5664
5765 private async onDidOpenNotebook ( notebook : NotebookDocument ) {
@@ -69,8 +77,34 @@ export class DeepnoteKernelAutoSelector implements IDeepnoteKernelAutoSelector,
6977 return ;
7078 }
7179
72- // Auto-select Deepnote kernel
73- await this . ensureKernelSelected ( notebook ) ;
80+ // Auto-select Deepnote kernel with error handling
81+ try {
82+ await this . ensureKernelSelected ( notebook ) ;
83+ } catch ( error ) {
84+ logger . error ( `Failed to auto-select Deepnote kernel for ${ getDisplayPath ( notebook . uri ) } : ${ error } ` ) ;
85+ // Don't rethrow - we want activation to continue even if one notebook fails
86+ }
87+ }
88+
89+ private onDidCloseNotebook ( notebook : NotebookDocument ) {
90+ // Only handle deepnote notebooks
91+ if ( notebook . notebookType !== DEEPNOTE_NOTEBOOK_TYPE ) {
92+ return ;
93+ }
94+
95+ logger . info ( `Deepnote notebook closed: ${ getDisplayPath ( notebook . uri ) } ` ) ;
96+
97+ // Extract the base file URI to match what we used when registering
98+ const baseFileUri = notebook . uri . with ( { query : '' , fragment : '' } ) ;
99+ const notebookKey = baseFileUri . fsPath ;
100+
101+ // Unregister the server if we have a handle for this notebook
102+ const serverHandle = this . notebookServerHandles . get ( notebookKey ) ;
103+ if ( serverHandle ) {
104+ logger . info ( `Unregistering server for closed notebook: ${ serverHandle } ` ) ;
105+ this . serverProvider . unregisterServer ( serverHandle ) ;
106+ this . notebookServerHandles . delete ( notebookKey ) ;
107+ }
74108 }
75109
76110 public async ensureKernelSelected ( notebook : NotebookDocument , token ?: CancellationToken ) : Promise < void > {
@@ -121,6 +155,9 @@ export class DeepnoteKernelAutoSelector implements IDeepnoteKernelAutoSelector,
121155 // Register the server with the provider so it can be resolved
122156 this . serverProvider . registerServer ( serverProviderHandle . handle , serverInfo ) ;
123157
158+ // Track the server handle for cleanup when notebook is closed
159+ this . notebookServerHandles . set ( baseFileUri . fsPath , serverProviderHandle . handle ) ;
160+
124161 // Connect to the server and get available kernel specs
125162 const connectionInfo = createJupyterConnectionInfo (
126163 serverProviderHandle ,
0 commit comments