@@ -35,13 +35,6 @@ suite('ToolSearchTool', () => {
3535 return tools . map ( candidate => candidate . name ) ;
3636 } ,
3737 } as any ,
38- {
39- tools : [
40- makeToolInfo ( 'read_file' ) ,
41- makeToolInfo ( 'enabled_deferred_tool' ) ,
42- makeToolInfo ( 'disabled_deferred_tool' ) ,
43- ] ,
44- } as any ,
4538 {
4639 isNonDeferredTool : ( name : string ) => nonDeferred . has ( name ) ,
4740 } as any ,
@@ -81,13 +74,6 @@ suite('ToolSearchTool', () => {
8174 return tools . map ( candidate => candidate . name ) ;
8275 } ,
8376 } as any ,
84- {
85- tools : [
86- makeToolInfo ( 'read_file' ) ,
87- makeToolInfo ( 'request_a_tool' ) ,
88- makeToolInfo ( 'request_b_tool' ) ,
89- ] ,
90- } as any ,
9177 {
9278 isNonDeferredTool : ( name : string ) => nonDeferred . has ( name ) ,
9379 } as any ,
@@ -132,6 +118,45 @@ suite('ToolSearchTool', () => {
132118 expect ( searchedToolNames ) . toEqual ( [ [ 'request_a_tool' ] ] ) ;
133119 } ) ;
134120
121+ test ( 'retains request-scoped virtual activate groups during tool search candidate selection' , async ( ) => {
122+ const searchedToolNames : string [ ] [ ] = [ ] ;
123+ const nonDeferred = new Set ( [ 'read_file' ] ) ;
124+ const tool = new ToolSearchTool (
125+ {
126+ searchToolsByQuery : async ( _query : string , tools : readonly vscode . LanguageModelToolInformation [ ] ) => {
127+ searchedToolNames . push ( tools . map ( candidate => candidate . name ) ) ;
128+ return tools . map ( candidate => candidate . name ) ;
129+ } ,
130+ } as any ,
131+ {
132+ isNonDeferredTool : ( name : string ) => nonDeferred . has ( name ) ,
133+ } as any ,
134+ { trace ( ) { } } as any ,
135+ ) ;
136+
137+ const resolvedInput = await ( tool as any ) . resolveInput ?.(
138+ { query : 'vscode interaction tools' } ,
139+ {
140+ tools : {
141+ toolReferences : [ ] ,
142+ toolInvocationToken : undefined as never ,
143+ availableTools : [
144+ makeToolInfo ( 'read_file' ) ,
145+ makeToolInfo ( 'activate_vs_code_interaction' ) ,
146+ ] ,
147+ } ,
148+ } ,
149+ 0 ,
150+ ) ;
151+
152+ await tool . invoke (
153+ { input : resolvedInput } as vscode . LanguageModelToolInvocationOptions < { query : string } > ,
154+ { isCancellationRequested : false } as vscode . CancellationToken ,
155+ ) ;
156+
157+ expect ( searchedToolNames ) . toEqual ( [ [ 'activate_vs_code_interaction' ] ] ) ;
158+ } ) ;
159+
135160 test ( 'fails explicitly when invoke runs without request-scoped resolveInput context' , async ( ) => {
136161 const nonDeferred = new Set ( [ 'read_file' ] ) ;
137162 const tool = new ToolSearchTool (
@@ -140,11 +165,6 @@ suite('ToolSearchTool', () => {
140165 throw new Error ( 'search should not run without resolveInput context' ) ;
141166 } ,
142167 } as any ,
143- {
144- tools : [
145- makeToolInfo ( 'enabled_deferred_tool' ) ,
146- ] ,
147- } as any ,
148168 {
149169 isNonDeferredTool : ( name : string ) => nonDeferred . has ( name ) ,
150170 } as any ,
@@ -156,6 +176,36 @@ suite('ToolSearchTool', () => {
156176 { isCancellationRequested : false } as vscode . CancellationToken ,
157177 ) ) . rejects . toThrow ( 'ToolSearchTool: request-scoped deferred tools are unavailable. Ensure resolveInput is called before invoke.' ) ;
158178 } ) ;
179+
180+ test ( 'uses an empty deferred tool snapshot when promptContext.tools is missing' , async ( ) => {
181+ const searchedToolNames : string [ ] [ ] = [ ] ;
182+ const tool = new ToolSearchTool (
183+ {
184+ searchToolsByQuery : async ( _query : string , tools : readonly vscode . LanguageModelToolInformation [ ] ) => {
185+ searchedToolNames . push ( tools . map ( candidate => candidate . name ) ) ;
186+ return tools . map ( candidate => candidate . name ) ;
187+ } ,
188+ } as any ,
189+ {
190+ isNonDeferredTool : ( ) => false ,
191+ } as any ,
192+ { trace ( ) { } } as any ,
193+ ) ;
194+
195+ const resolvedInput = await tool . resolveInput (
196+ { query : 'anything' } ,
197+ { } as any ,
198+ 0 ,
199+ ) ;
200+
201+ const result = await tool . invoke (
202+ { input : resolvedInput } as vscode . LanguageModelToolInvocationOptions < { query : string } > ,
203+ { isCancellationRequested : false } as vscode . CancellationToken ,
204+ ) ;
205+
206+ expect ( searchedToolNames ) . toEqual ( [ [ ] ] ) ;
207+ expect ( result . content ) . toEqual ( [ expect . objectContaining ( { value : '[]' } ) ] ) ;
208+ } ) ;
159209} ) ;
160210
161211suite ( 'TestToolsService getEnabledTools' , ( ) => {
0 commit comments