From dcbf57d2f3fcdb1afb78231727b7d35c9950b6c0 Mon Sep 17 00:00:00 2001 From: Alex Ross <38270282+alexr00@users.noreply.github.com> Date: Wed, 11 Feb 2026 12:41:57 +0100 Subject: [PATCH 1/2] Chat context API changes --- .../vscode.proposed.chatContextProvider.d.ts | 150 ++++++++++++++++-- src/extension.ts | 15 +- src/lm/issueContextProvider.ts | 16 +- src/lm/pullRequestContextProvider.ts | 42 +++-- 4 files changed, 186 insertions(+), 37 deletions(-) diff --git a/src/@types/vscode.proposed.chatContextProvider.d.ts b/src/@types/vscode.proposed.chatContextProvider.d.ts index ee971b9adb..e7cd493ae5 100644 --- a/src/@types/vscode.proposed.chatContextProvider.d.ts +++ b/src/@types/vscode.proposed.chatContextProvider.d.ts @@ -11,13 +11,47 @@ declare module 'vscode' { export namespace chat { /** - * Register a chat context provider. Chat context can be provided: - * - For a resource. Make sure to pass a selector that matches the resource you want to provide context for. - * Providers registered without a selector will not be called for resource-based context. - * - Explicitly. These context items are shown as options when the user explicitly attaches context. + * Register a chat workspace context provider. Workspace context is automatically included in all chat requests. + * + * To ensure your extension is activated when chat context is requested, make sure to include the following activations events: + * - If your extension implements `provideWorkspaceChatContext` or `provideChatContextForResource`, find an activation event which is a good signal to activate. + * Ex: `onLanguage:`, `onWebviewPanel:`, etc.` + * - If your extension implements `provideChatContextExplicit`, your extension will be automatically activated when the user requests explicit context. + * + * @param id Unique identifier for the provider. + * @param provider The chat workspace context provider. + */ + export function registerChatWorkspaceContextProvider(id: string, provider: ChatWorkspaceContextProvider): Disposable; + + /** + * Register a chat explicit context provider. Explicit context items are shown as options when the user explicitly attaches context use the "Attache Context" action in the chat input box. + * + * Explicit context providers should also be statically contributed in package.json using the `chatContext` contribution point. * * To ensure your extension is activated when chat context is requested, make sure to include the `onChatContextProvider:` activation event in your `package.json`. * + * @param id Unique identifier for the provider. + * @param provider The chat explicit context provider. + */ + export function registerChatExplicitContextProvider(id: string, provider: ChatExplicitContextProvider): Disposable; + + /** + * Register a chat resource context provider. Resource context is provided for a specific resource. + * Make sure to pass a selector that matches the resource you want to provide context for. + * + * To ensure your extension is activated when chat context is requested, make sure to include the `onChatContextProvider:` activation event in your `package.json`. + * + * @param selector Document selector to filter which resources the provider is called for. + * @param id Unique identifier for the provider. + * @param provider The chat resource context provider. + */ + export function registerChatResourceContextProvider(selector: DocumentSelector, id: string, provider: ChatResourceContextProvider): Disposable; + + /** + * Register a chat context provider. + * + * @deprecated Use {@link registerChatWorkspaceContextProvider}, {@link registerChatExplicitContextProvider}, or {@link registerChatResourceContextProvider} instead. + * * @param selector Optional document selector to filter which resources the provider is called for. If omitted, the provider will only be called for explicit context requests. * @param id Unique identifier for the provider. * @param provider The chat context provider. @@ -29,12 +63,21 @@ declare module 'vscode' { export interface ChatContextItem { /** * Icon for the context item. + * - If `icon` is not defined, no icon is shown. + * - If `icon` is defined and is a file or folder icon, the icon is derived from {@link resourceUri} if `resourceUri` is defined. + * - Otherwise, `icon` is used. */ - icon: ThemeIcon; + icon?: ThemeIcon; /** * Human readable label for the context item. + * If not set, the label is derived from {@link resourceUri}. */ - label: string; + label?: string; + /** + * A resource URI for the context item. + * Used to derive the {@link label} and {@link icon} if they are not set. + */ + resourceUri?: Uri; /** * An optional description of the context item, e.g. to describe the item to the language model. */ @@ -49,12 +92,12 @@ declare module 'vscode' { value?: string; /** * An optional command that is executed when the context item is clicked. - * The original context item will be passed as an argument to the command. + * The original context item will be passed as the first argument to the command. */ command?: Command; } - export interface ChatContextProvider { + export interface ChatWorkspaceContextProvider { /** * An optional event that should be fired when the workspace chat context has changed. @@ -62,11 +105,21 @@ declare module 'vscode' { onDidChangeWorkspaceChatContext?: Event; /** - * Provide a list of chat context items to be included as workspace context for all chat sessions. + * Provide a list of chat context items to be included as workspace context for all chat requests. + * This should be used very sparingly to avoid providing useless context and to avoid using up the context window. + * A good example use case is to provide information about which branch the user is working on in a source control context. * * @param token A cancellation token. */ - provideWorkspaceChatContext?(token: CancellationToken): ProviderResult; + provideWorkspaceChatContext(token: CancellationToken): ProviderResult; + + /** + * @deprecated + */ + provideChatContext?(token: CancellationToken): ProviderResult; + } + + export interface ChatExplicitContextProvider { /** * Provide a list of chat context items that a user can choose from. These context items are shown as options when the user explicitly attaches context. @@ -75,27 +128,94 @@ declare module 'vscode' { * * @param token A cancellation token. */ - provideChatContextExplicit?(token: CancellationToken): ProviderResult; + provideExplicitChatContext(token: CancellationToken): ProviderResult; + + /** + * @deprecated + */ + provideChatContext?(token: CancellationToken): ProviderResult; + + /** + * If a chat context item is provided without a `value`, this method is called to resolve the `value` for the item. + * + * @param context The context item to resolve. + * @param token A cancellation token. + */ + resolveExplicitChatContext(context: T, token: CancellationToken): ProviderResult; + + /** + * @deprecated + */ + resolveChatContext?(context: T, token: CancellationToken): ProviderResult; + } + + export interface ChatResourceContextProvider { /** * Given a particular resource, provide a chat context item for it. This is used for implicit context (see the settings `chat.implicitContext.enabled` and `chat.implicitContext.suggestedContext`). * Chat context items can be provided without a `value`, as the `value` can be resolved later using `resolveChatContext`. * `resolveChatContext` is only called for items that do not have a `value`. * - * Currently only called when the resource is a webview. + * Called when the resource is a webview or a text editor. * * @param options Options include the resource for which to provide context. * @param token A cancellation token. */ - provideChatContextForResource?(options: { resource: Uri }, token: CancellationToken): ProviderResult; + provideResourceChatContext(options: { resource: Uri }, token: CancellationToken): ProviderResult; + + /** + * @deprecated + */ + provideChatContext?(options: { resource: Uri }, token: CancellationToken): ProviderResult; /** - * If a chat context item is provided without a `value`, from either of the `provide` methods, this method is called to resolve the `value` for the item. + * If a chat context item is provided without a `value`, this method is called to resolve the `value` for the item. * * @param context The context item to resolve. * @param token A cancellation token. */ - resolveChatContext(context: T, token: CancellationToken): ProviderResult; + resolveResourceChatContext(context: T, token: CancellationToken): ProviderResult; + + /** + * @deprecated + */ + resolveChatContext?(context: T, token: CancellationToken): ProviderResult; + } + + /** + * @deprecated Use {@link ChatWorkspaceContextProvider}, {@link ChatExplicitContextProvider}, or {@link ChatResourceContextProvider} instead. + */ + export interface ChatContextProvider { + + /** + * An optional event that should be fired when the workspace chat context has changed. + * @deprecated Use {@link ChatWorkspaceContextProvider.onDidChangeWorkspaceChatContext} instead. + */ + onDidChangeWorkspaceChatContext?: Event; + + /** + * Provide a list of chat context items to be included as workspace context for all chat requests. + * @deprecated Use {@link ChatWorkspaceContextProvider.provideWorkspaceChatContext} instead. + */ + provideWorkspaceChatContext?(token: CancellationToken): ProviderResult; + + /** + * Provide a list of chat context items that a user can choose from. + * @deprecated Use {@link ChatExplicitContextProvider.provideExplicitChatContext} instead. + */ + provideChatContextExplicit?(token: CancellationToken): ProviderResult; + + /** + * Given a particular resource, provide a chat context item for it. + * @deprecated Use {@link ChatResourceContextProvider.provideResourceChatContext} instead. + */ + provideChatContextForResource?(options: { resource: Uri }, token: CancellationToken): ProviderResult; + + /** + * If a chat context item is provided without a `value`, this method is called to resolve the `value` for the item. + * @deprecated Use the `resolveChatContext` method on the specific provider type instead. + */ + resolveChatContext?(context: T, token: CancellationToken): ProviderResult; } } diff --git a/src/extension.ts b/src/extension.ts index a65dfd4232..fcc0be3859 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -34,7 +34,7 @@ import { IssueFeatureRegistrar } from './issues/issueFeatureRegistrar'; import { StateManager } from './issues/stateManager'; import { IssueContextProvider } from './lm/issueContextProvider'; import { ChatParticipant, ChatParticipantState } from './lm/participants'; -import { PullRequestContextProvider } from './lm/pullRequestContextProvider'; +import { PullRequestContextProvider, WorkspaceContextProvider } from './lm/pullRequestContextProvider'; import { registerTools } from './lm/tools/tools'; import { migrate } from './migrations'; import { NotificationsFeatureRegister } from './notifications/notificationsFeatureRegistar'; @@ -269,10 +269,15 @@ async function init( context.subscriptions.push(issuesFeatures); await issuesFeatures.initialize(); - const pullRequestContextProvider = new PullRequestContextProvider(prsTreeModel, reposManager, git, context); - vscode.chat.registerChatContextProvider({ scheme: 'webview-panel', pattern: '**/webview-PullRequestOverview**' }, 'githubpr', pullRequestContextProvider); - vscode.chat.registerChatContextProvider({ scheme: 'webview-panel', pattern: '**/webview-IssueOverview**' }, 'githubissue', new IssueContextProvider(issueStateManager, reposManager, context)); - pullRequestContextProvider.initialize(); + const workspaceContextProvider = new WorkspaceContextProvider(reposManager, git); + vscode.chat.registerChatWorkspaceContextProvider('githubpr', workspaceContextProvider); + workspaceContextProvider.initialize(); + const pullRequestContextProvider = new PullRequestContextProvider(prsTreeModel, reposManager, context); + vscode.chat.registerChatExplicitContextProvider('githubpr', pullRequestContextProvider); + vscode.chat.registerChatResourceContextProvider({ scheme: 'webview-panel', pattern: '**/webview-PullRequestOverview**' }, 'githubpr', pullRequestContextProvider); + const issueContextProvider = new IssueContextProvider(issueStateManager, reposManager, context); + vscode.chat.registerChatExplicitContextProvider('githubissue', issueContextProvider); + vscode.chat.registerChatResourceContextProvider({ scheme: 'webview-panel', pattern: '**/webview-IssueOverview**' }, 'githubissue', issueContextProvider); const notificationsFeatures = new NotificationsFeatureRegister(credentialStore, reposManager, telemetry, notificationsManager); context.subscriptions.push(notificationsFeatures); diff --git a/src/lm/issueContextProvider.ts b/src/lm/issueContextProvider.ts index 83a36c5067..046de90fcb 100644 --- a/src/lm/issueContextProvider.ts +++ b/src/lm/issueContextProvider.ts @@ -22,27 +22,35 @@ export namespace IssueChatContextItem { } } -export class IssueContextProvider implements vscode.ChatContextProvider { +export class IssueContextProvider implements vscode.ChatExplicitContextProvider, vscode.ChatResourceContextProvider { constructor(private readonly _stateManager: StateManager, private readonly _reposManager: RepositoriesManager, private readonly _context: vscode.ExtensionContext ) { } - async provideChatContextForResource(_options: { resource: vscode.Uri }, _token: vscode.CancellationToken): Promise { + async provideResourceChatContext(_options: { resource: vscode.Uri; }, _token: vscode.CancellationToken): Promise { const item = IssueOverviewPanel.currentPanel?.getCurrentItem(); if (item) { return this._issueToUnresolvedContext(item); } } - async resolveChatContext(context: IssueChatContextItem, _token: vscode.CancellationToken): Promise { + resolveExplicitChatContext(context: IssueChatContextItem, token: vscode.CancellationToken): vscode.ProviderResult { + return this._resolveChatContext(context, token); + } + + resolveResourceChatContext(context: IssueChatContextItem, token: vscode.CancellationToken): vscode.ProviderResult { + return this._resolveChatContext(context, token); + } + + private async _resolveChatContext(context: IssueChatContextItem, _token: vscode.CancellationToken): Promise { context.value = await this._resolvedIssueValue(context.issue); context.modelDescription = 'All the information about the GitHub issue the user is viewing, including comments.'; context.tooltip = await issueMarkdown(context.issue, this._context, this._reposManager); return context; } - async provideChatContextExplicit(_token: vscode.CancellationToken): Promise { + async provideExplicitChatContext(_token: vscode.CancellationToken): Promise { const contextItems: IssueChatContextItem[] = []; const seenIssues: Set = new Set(); for (const folderManager of this._reposManager.folderManagers) { diff --git a/src/lm/pullRequestContextProvider.ts b/src/lm/pullRequestContextProvider.ts index f8963f5229..2f0deed8ee 100644 --- a/src/lm/pullRequestContextProvider.ts +++ b/src/lm/pullRequestContextProvider.ts @@ -23,14 +23,13 @@ export namespace PRChatContextItem { } } -export class PullRequestContextProvider extends Disposable implements vscode.ChatContextProvider { +export class WorkspaceContextProvider extends Disposable implements vscode.ChatWorkspaceContextProvider { private readonly _onDidChangeWorkspaceChatContext = new vscode.EventEmitter(); readonly onDidChangeWorkspaceChatContext = this._onDidChangeWorkspaceChatContext.event; - constructor(private readonly _prsTreeModel: PrsTreeModel, + constructor( private readonly _reposManager: RepositoriesManager, - private readonly _git: GitApiImpl, - private readonly _context: vscode.ExtensionContext + private readonly _git: GitApiImpl ) { super(); } @@ -90,15 +89,39 @@ Active pull request (may not be the same as open pull request): ${folderManager. } return contexts; } +} + +export class PullRequestContextProvider extends Disposable implements vscode.ChatExplicitContextProvider, vscode.ChatResourceContextProvider { + constructor(private readonly _prsTreeModel: PrsTreeModel, + private readonly _reposManager: RepositoriesManager, + private readonly _context: vscode.ExtensionContext + ) { + super(); + } + + async provideExplicitChatContext(_token: vscode.CancellationToken): Promise { + const prs = await this._prsTreeModel.getAllPullRequests(this._reposManager.folderManagers[0], false); + return prs.items.map(pr => { + return this._prToUnresolvedContext(pr); + }); + } - async provideChatContextForResource(_options: { resource: vscode.Uri }, _token: vscode.CancellationToken): Promise { + async provideResourceChatContext(_options: { resource: vscode.Uri; }, _token: vscode.CancellationToken): Promise { const item = PullRequestOverviewPanel.currentPanel?.getCurrentItem(); if (item) { return this._prToUnresolvedContext(item); } } - async resolveChatContext(context: PRChatContextItem, _token: vscode.CancellationToken): Promise { + async resolveExplicitChatContext(context: PRChatContextItem, token: vscode.CancellationToken): Promise { + return this._resolveChatContext(context, token); + } + + async resolveResourceChatContext(context: PRChatContextItem, token: vscode.CancellationToken): Promise { + return this._resolveChatContext(context, token); + } + + private async _resolveChatContext(context: PRChatContextItem, _token: vscode.CancellationToken): Promise { if (!context.pr) { return context; } @@ -108,13 +131,6 @@ Active pull request (may not be the same as open pull request): ${folderManager. return context; } - async provideChatContextExplicit(_token: vscode.CancellationToken): Promise { - const prs = await this._prsTreeModel.getAllPullRequests(this._reposManager.folderManagers[0], false); - return prs.items.map(pr => { - return this._prToUnresolvedContext(pr); - }); - } - private _prToUnresolvedContext(pr: PullRequestModel): PRChatContextItem { return { icon: new vscode.ThemeIcon('git-pull-request'), From 71c5ef08c8c1027bb08e6e40f9accbc2f47590f2 Mon Sep 17 00:00:00 2001 From: Alex Ross <38270282+alexr00@users.noreply.github.com> Date: Wed, 11 Feb 2026 12:59:00 +0100 Subject: [PATCH 2/2] Copilot PR feedback --- src/extension.ts | 12 +++++++----- src/lm/pullRequestContextProvider.ts | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index fcc0be3859..058cb9395c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -270,14 +270,16 @@ async function init( await issuesFeatures.initialize(); const workspaceContextProvider = new WorkspaceContextProvider(reposManager, git); - vscode.chat.registerChatWorkspaceContextProvider('githubpr', workspaceContextProvider); + context.subscriptions.push(workspaceContextProvider); + context.subscriptions.push(vscode.chat.registerChatWorkspaceContextProvider('githubpr', workspaceContextProvider)); workspaceContextProvider.initialize(); const pullRequestContextProvider = new PullRequestContextProvider(prsTreeModel, reposManager, context); - vscode.chat.registerChatExplicitContextProvider('githubpr', pullRequestContextProvider); - vscode.chat.registerChatResourceContextProvider({ scheme: 'webview-panel', pattern: '**/webview-PullRequestOverview**' }, 'githubpr', pullRequestContextProvider); + context.subscriptions.push(pullRequestContextProvider); + context.subscriptions.push(vscode.chat.registerChatExplicitContextProvider('githubpr', pullRequestContextProvider)); + context.subscriptions.push(vscode.chat.registerChatResourceContextProvider({ scheme: 'webview-panel', pattern: '**/webview-PullRequestOverview**' }, 'githubpr', pullRequestContextProvider)); const issueContextProvider = new IssueContextProvider(issueStateManager, reposManager, context); - vscode.chat.registerChatExplicitContextProvider('githubissue', issueContextProvider); - vscode.chat.registerChatResourceContextProvider({ scheme: 'webview-panel', pattern: '**/webview-IssueOverview**' }, 'githubissue', issueContextProvider); + context.subscriptions.push(vscode.chat.registerChatExplicitContextProvider('githubissue', issueContextProvider)); + context.subscriptions.push(vscode.chat.registerChatResourceContextProvider({ scheme: 'webview-panel', pattern: '**/webview-IssueOverview**' }, 'githubissue', issueContextProvider)); const notificationsFeatures = new NotificationsFeatureRegister(credentialStore, reposManager, telemetry, notificationsManager); context.subscriptions.push(notificationsFeatures); diff --git a/src/lm/pullRequestContextProvider.ts b/src/lm/pullRequestContextProvider.ts index 2f0deed8ee..0bc8e89d0b 100644 --- a/src/lm/pullRequestContextProvider.ts +++ b/src/lm/pullRequestContextProvider.ts @@ -24,7 +24,7 @@ export namespace PRChatContextItem { } export class WorkspaceContextProvider extends Disposable implements vscode.ChatWorkspaceContextProvider { - private readonly _onDidChangeWorkspaceChatContext = new vscode.EventEmitter(); + private readonly _onDidChangeWorkspaceChatContext = this._register(new vscode.EventEmitter()); readonly onDidChangeWorkspaceChatContext = this._onDidChangeWorkspaceChatContext.event; constructor(