@@ -54,8 +54,7 @@ import { IChatInputState } from '../common/chatWidgetHistoryService.js';
5454import { CodeBlockModelCollection } from '../common/codeBlockModelCollection.js' ;
5555import { ChatAgentLocation , ChatMode } from '../common/constants.js' ;
5656import { ILanguageModelToolsService } from '../common/languageModelToolsService.js' ;
57- import { IPromptMetadata } from '../common/promptSyntax/parsers/types.js' ;
58- import { IMetadata , IPromptsService } from '../common/promptSyntax/service/types.js' ;
57+ import { IPromptsService } from '../common/promptSyntax/service/types.js' ;
5958import { handleModeSwitch } from './actions/chatActions.js' ;
6059import { ChatTreeItem , IChatAcceptInputOptions , IChatAccessibilityService , IChatCodeBlockInfo , IChatFileTreeInfo , IChatListItemRendererOptions , IChatWidget , IChatWidgetService , IChatWidgetViewContext , IChatWidgetViewOptions } from './chat.js' ;
6160import { ChatAccessibilityProvider } from './chatAccessibilityProvider.js' ;
@@ -1185,52 +1184,27 @@ export class ChatWidget extends Disposable implements IChatWidget {
11851184 return inputState ;
11861185 }
11871186
1188- private _findPromptFileInContext ( attachedContext : IChatRequestVariableEntry [ ] ) : URI | undefined {
1189- for ( const item of attachedContext ) {
1190- if ( isPromptFileChatVariable ( item ) && item . isRoot ) {
1191- return toUri ( item ) ;
1192- }
1193- }
1194- return undefined ;
1195- }
1187+ private async _handlePromptSlashCommand ( input : string , attachedContext : IChatRequestVariableEntry [ ] ) : Promise < string > {
11961188
1197- private async _applyPromptFileIfSet ( requestInput : { input : string ; attachedContext : IChatRequestVariableEntry [ ] } ) : Promise < IMetadata | undefined > {
1198-
1199- let metadata : IMetadata | undefined ;
1200-
1201- // frst check if the input has a prompt slash command
12021189 const agentSlashPromptPart = this . parsedInput . parts . find ( ( r ) : r is ChatRequestSlashPromptPart => r instanceof ChatRequestSlashPromptPart ) ;
1203- if ( agentSlashPromptPart ) {
1204- metadata = await this . promptsService . resolvePromptSlashCommand ( agentSlashPromptPart . slashPromptCommand ) ;
1205- if ( metadata ) {
1206- const uri = metadata . uri ;
1207- if ( ! requestInput . attachedContext . some ( variable => isPromptFileChatVariable ( variable ) && isEqual ( toUri ( variable ) , uri ) ) ) {
1208- // not yet attached, so attach it
1209- const variable = toChatVariable ( { uri : metadata . uri , isPromptFile : true } , true ) ;
1210- requestInput . attachedContext . push ( variable ) ;
1211- }
1212- // remove the slash command from the input
1213- requestInput . input = this . parsedInput . parts . filter ( part => ! ( part instanceof ChatRequestSlashPromptPart ) ) . map ( part => part . text ) . join ( '' ) . trim ( ) ;
1214- }
1215- } else {
1216- // if not, check if the context contains a prompt file: This is the old workflow that we still support for legacy reasons
1217- const uri = this . _findPromptFileInContext ( requestInput . attachedContext ) ;
1218- if ( uri ) {
1219- metadata = await this . promptsService . getMetadata ( uri ) ;
1220- }
1190+ if ( ! agentSlashPromptPart ) {
1191+ return input ;
12211192 }
1193+ // remove the slash command from the input
1194+ input = this . parsedInput . parts . filter ( part => ! ( part instanceof ChatRequestSlashPromptPart ) ) . map ( part => part . text ) . join ( '' ) . trim ( ) ;
12221195
1223- if ( ! metadata ) {
1224- return undefined ;
1196+ const promptPath = await this . promptsService . resolvePromptSlashCommand ( agentSlashPromptPart . slashPromptCommand ) ;
1197+ if ( ! promptPath ) {
1198+ return input ;
12251199 }
12261200
1227- if ( ! requestInput . input . trim ( ) ) {
1228- requestInput . input = localize ( 'input.1' , "Follow instructions from {0}." , basename ( metadata . uri ) ) ;
1201+ if ( ! attachedContext . some ( variable => isPromptFileChatVariable ( variable ) && isEqual ( toUri ( variable ) , promptPath . uri ) ) ) {
1202+ // not yet attached, so attach it
1203+ const variable = toChatVariable ( { uri : promptPath . uri , isPromptFile : true } , true ) ;
1204+ attachedContext . push ( variable ) ;
12291205 }
12301206
1231- await this . _applyPromptMetadata ( metadata . metadata ) ;
1232-
1233- return metadata ;
1207+ return input ;
12341208 }
12351209
12361210 private async _acceptInput ( query : { query : string } | undefined , options ?: IChatAcceptInputOptions ) : Promise < IChatResponseModel | undefined > {
@@ -1244,23 +1218,26 @@ export class ChatWidget extends Disposable implements IChatWidget {
12441218
12451219 const editorValue = this . getInput ( ) ;
12461220 const requestId = this . chatAccessibilityService . acceptRequest ( ) ;
1247- const requestInputs = {
1248- input : ! query ? editorValue : query . query ,
1249- attachedContext : await this . inputPart . getAttachedAndImplicitContext ( this . viewModel . sessionId ) ,
1250- } ;
1251-
1221+ let input = ! query ? editorValue : query . query ;
12521222 const isUserQuery = ! query ;
12531223
1224+ let attachedContext = await this . inputPart . getAttachedAndImplicitContext ( this . viewModel . sessionId ) ;
1225+
12541226 const { promptInstructions } = this . inputPart . attachmentModel ;
12551227 const instructionsEnabled = promptInstructions . featureEnabled ;
12561228 if ( instructionsEnabled ) {
1257- await this . _applyPromptFileIfSet ( requestInputs ) ;
1258- await this . autoAttachInstructions ( requestInputs . attachedContext ) ;
1229+ input = await this . _handlePromptSlashCommand ( input , attachedContext ) ;
1230+ await this . autoAttachInstructions ( attachedContext ) ;
1231+ const newInput = await this . setupChatModeAndTools ( input , attachedContext ) ;
1232+ if ( newInput === undefined ) {
1233+ return ;
1234+ }
1235+ input = newInput ;
12591236 }
12601237
12611238 if ( this . viewOptions . enableWorkingSet !== undefined && this . input . currentMode === ChatMode . Edit && ! this . chatService . edits2Enabled ) {
12621239 const uniqueWorkingSetEntries = new ResourceSet ( ) ; // NOTE: this is used for bookkeeping so the UI can avoid rendering references in the UI that are already shown in the working set
1263- const editingSessionAttachedContext : IChatRequestVariableEntry [ ] = requestInputs . attachedContext ;
1240+ const editingSessionAttachedContext : IChatRequestVariableEntry [ ] = attachedContext ;
12641241
12651242 // Collect file variables from previous requests before sending the request
12661243 const previousRequests = this . viewModel . model . getRequests ( ) ;
@@ -1275,7 +1252,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
12751252 }
12761253 }
12771254 }
1278- requestInputs . attachedContext = editingSessionAttachedContext ;
1255+ attachedContext = editingSessionAttachedContext ;
12791256
12801257 type ChatEditingWorkingSetClassification = {
12811258 owner : 'joyceerhl' ;
@@ -1305,13 +1282,13 @@ export class ChatWidget extends Disposable implements IChatWidget {
13051282 }
13061283 }
13071284
1308- const result = await this . chatService . sendRequest ( this . viewModel . sessionId , requestInputs . input , {
1285+ const result = await this . chatService . sendRequest ( this . viewModel . sessionId , input , {
13091286 mode : this . inputPart . currentMode ,
13101287 userSelectedModelId : this . inputPart . currentLanguageModel ,
13111288 location : this . location ,
13121289 locationData : this . _location . resolveData ?.( ) ,
13131290 parserContext : { selectedAgent : this . _lastSelectedAgent , mode : this . inputPart . currentMode } ,
1314- attachedContext : requestInputs . attachedContext ,
1291+ attachedContext,
13151292 noCommandDetection : options ?. noCommandDetection ,
13161293 userSelectedTools,
13171294 userSelectedTools2,
@@ -1528,7 +1505,39 @@ export class ChatWidget extends Disposable implements IChatWidget {
15281505 this . agentInInput . set ( ! ! currentAgent ) ;
15291506 }
15301507
1531- private async _applyPromptMetadata ( metadata : IPromptMetadata ) : Promise < void > {
1508+ /**
1509+ * Set's up the `chat mode` and selects required `tools` based on
1510+ * the metadata defined in headers of attached prompt files.
1511+ */
1512+ private async setupChatModeAndTools (
1513+ input : string ,
1514+ attachedContext : readonly IChatRequestVariableEntry [ ] ,
1515+ ) : Promise < string | undefined > {
1516+ // process prompt files starting from the 'root' ones
1517+ const promptFileVariables = attachedContext
1518+ . filter ( isPromptFileChatVariable )
1519+ . filter ( pick ( 'isRoot' ) ) ;
1520+ const promptUris = promptFileVariables . map ( toUri ) ;
1521+
1522+ if ( promptFileVariables . length === 0 ) {
1523+ return input ;
1524+ }
1525+
1526+ if ( ! input . trim ( ) ) {
1527+ const promptNames = ( promptUris . length === 1 )
1528+ ? `'${ basename ( promptUris [ 0 ] ) } '`
1529+ : `the prompt files` ;
1530+
1531+ input = `Follow instructions from ${ promptNames } .` ;
1532+ }
1533+
1534+
1535+ const metadata = await this . promptsService
1536+ . getCombinedToolsMetadata ( promptUris ) ;
1537+
1538+ if ( metadata === null ) {
1539+ return input ;
1540+ }
15321541
15331542 const { mode, tools } = metadata ;
15341543
@@ -1546,7 +1555,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
15461555
15471556 // if not tools to enable are present, we are done
15481557 if ( tools === undefined ) {
1549- return ;
1558+ return input ;
15501559 }
15511560
15521561 // sanity check on the logic of the `getPromptFilesMetadata` method
@@ -1577,6 +1586,8 @@ export class ChatWidget extends Disposable implements IChatWidget {
15771586 this . inputPart
15781587 . selectedToolsModel
15791588 . selectOnly ( toolIds ) ;
1589+
1590+ return input ;
15801591 }
15811592
15821593 /**
0 commit comments