diff --git a/.vscode/settings.json b/.vscode/settings.json index da775f21244f..3e903f646758 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -94,6 +94,7 @@ "**/*.snap": true, }, // --- TypeScript --- + "typescript.experimental.useTsgo": true, "typescript.tsdk": "node_modules/typescript/lib", "typescript.preferences.importModuleSpecifier": "relative", "typescript.preferences.quoteStyle": "single", diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index ff46f210b8ab..a3098e1715c5 100644 Binary files a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf and b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf differ diff --git a/src/vs/workbench/contrib/chat/browser/chatTipService.ts b/src/vs/workbench/contrib/chat/browser/chatTipService.ts index bcc4e30c205e..726d06a3df5a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatTipService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatTipService.ts @@ -105,6 +105,11 @@ export interface ITipDefinition { * The tip won't be shown if the tool it describes has already been used. */ readonly excludeWhenToolsInvoked?: string[]; + /** + * Tool set reference names where at least one must be registered for the tip to be eligible. + * If none of the listed tool sets are registered, the tip is not shown. + */ + readonly requiresAnyToolSetRegistered?: string[]; /** * If set, exclude this tip when prompt files of the specified type exist in the workspace. */ @@ -205,6 +210,7 @@ const TIP_CATALOG: ITipDefinition[] = [ ContextKeyExpr.notEquals('gitOpenRepositoryCount', '0'), ), excludeWhenToolsInvoked: ['github-pull-request_doSearch', 'github-pull-request_issue_fetch', 'github-pull-request_formSearchQuery'], + requiresAnyToolSetRegistered: ['github', 'github-pull-request'], }, { id: 'tip.subagents', @@ -271,7 +277,7 @@ export class TipEligibilityTracker extends Disposable { @ICommandService commandService: ICommandService, @IStorageService private readonly _storageService: IStorageService, @IPromptsService private readonly _promptsService: IPromptsService, - @ILanguageModelToolsService languageModelToolsService: ILanguageModelToolsService, + @ILanguageModelToolsService private readonly _languageModelToolsService: ILanguageModelToolsService, @ILogService private readonly _logService: ILogService, ) { super(); @@ -335,7 +341,7 @@ export class TipEligibilityTracker extends Disposable { // --- Set up tool listener (auto-disposes when all seen) ----------------- if (this._pendingTools.size > 0) { - this._toolListener.value = languageModelToolsService.onDidInvokeTool(e => { + this._toolListener.value = this._languageModelToolsService.onDidInvokeTool(e => { if (this._pendingTools.has(e.toolId)) { this._invokedTools.add(e.toolId); this._persistSet(TipEligibilityTracker._TOOLS_STORAGE_KEY, this._invokedTools); @@ -428,6 +434,13 @@ export class TipEligibilityTracker extends Disposable { this._logService.debug('#ChatTips: tip excluded because prompt files exist', tip.id); return true; } + if (tip.requiresAnyToolSetRegistered) { + const hasAny = tip.requiresAnyToolSetRegistered.some(name => this._languageModelToolsService.getToolSetByName(name)); + if (!hasAny) { + this._logService.debug('#ChatTips: tip excluded because no required tool sets are registered', tip.id); + return true; + } + } return false; } diff --git a/src/vs/workbench/contrib/chat/test/browser/chatTipService.test.ts b/src/vs/workbench/contrib/chat/test/browser/chatTipService.test.ts index 442293c19986..a1f7741a4b70 100644 --- a/src/vs/workbench/contrib/chat/test/browser/chatTipService.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/chatTipService.test.ts @@ -613,6 +613,47 @@ suite('ChatTipService', () => { assert.strictEqual(tracker.isExcluded(tip), false, 'Should not be excluded when no skill files exist'); }); + test('excludes tip when requiresAnyToolSetRegistered tool sets are not registered', () => { + const tip: ITipDefinition = { + id: 'tip.githubRepo', + message: 'test', + requiresAnyToolSetRegistered: ['github', 'github-pull-request'], + }; + + const tracker = testDisposables.add(new TipEligibilityTracker( + [tip], + { onDidExecuteCommand: Event.None, onWillExecuteCommand: Event.None } as Partial as ICommandService, + storageService, + createMockPromptsService() as IPromptsService, + createMockToolsService(), + new NullLogService(), + )); + + assert.strictEqual(tracker.isExcluded(tip), true, 'Should be excluded when no required tool sets are registered'); + }); + + test('does not exclude tip when at least one requiresAnyToolSetRegistered tool set is registered', () => { + const mockToolsService = createMockToolsService(); + mockToolsService.addRegisteredToolSetName('github'); + + const tip: ITipDefinition = { + id: 'tip.githubRepo', + message: 'test', + requiresAnyToolSetRegistered: ['github', 'github-pull-request'], + }; + + const tracker = testDisposables.add(new TipEligibilityTracker( + [tip], + { onDidExecuteCommand: Event.None, onWillExecuteCommand: Event.None } as Partial as ICommandService, + storageService, + createMockPromptsService() as IPromptsService, + mockToolsService, + new NullLogService(), + )); + + assert.strictEqual(tracker.isExcluded(tip), false, 'Should not be excluded when at least one required tool set is registered'); + }); + test('re-checks agent file exclusion when onDidChangeCustomAgents fires', async () => { const agentChangeEmitter = testDisposables.add(new Emitter()); let agentFiles: IPromptPath[] = []; diff --git a/src/vs/workbench/contrib/chat/test/common/tools/mockLanguageModelToolsService.ts b/src/vs/workbench/contrib/chat/test/common/tools/mockLanguageModelToolsService.ts index ea248c48bd24..665bd2a4618b 100644 --- a/src/vs/workbench/contrib/chat/test/common/tools/mockLanguageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/test/common/tools/mockLanguageModelToolsService.ts @@ -27,6 +27,9 @@ export class MockLanguageModelToolsService extends Disposable implements ILangua private readonly _onDidInvokeTool = this._register(new Emitter()); + private readonly _registeredToolIds = new Set(); + private readonly _registeredToolSetNames = new Set(); + constructor() { super(); } @@ -88,7 +91,14 @@ export class MockLanguageModelToolsService extends Disposable implements ILangua return []; } + addRegisteredToolId(id: string): void { + this._registeredToolIds.add(id); + } + getTool(id: string): IToolData | undefined { + if (this._registeredToolIds.has(id)) { + return { id, source: ToolDataSource.Internal, displayName: id, modelDescription: id }; + } return undefined; } @@ -125,7 +135,14 @@ export class MockLanguageModelToolsService extends Disposable implements ILangua return []; } + addRegisteredToolSetName(name: string): void { + this._registeredToolSetNames.add(name); + } + getToolSetByName(name: string): IToolSet | undefined { + if (this._registeredToolSetNames.has(name)) { + return { id: name, referenceName: name, icon: ThemeIcon.fromId(Codicon.tools.id), source: ToolDataSource.Internal, getTools: () => [] }; + } return undefined; } diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index a99f9628499c..e97facbaa354 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -342,7 +342,7 @@ const terminalConfiguration: IStringDictionary = { }, [TerminalSettingId.TerminalTitle]: { 'type': 'string', - 'default': '${process}', + 'default': '${sequence}', 'markdownDescription': terminalTitle }, [TerminalSettingId.TerminalDescription]: {