sessions: add @ and # completion providers for new chat input#307738
sessions: add @ and # completion providers for new chat input#307738
Conversation
Register dedicated completion providers for the sessions-chat editor scheme so that @ (agent) and # (file) completions work in the NewChatViewPane welcome widget. - Add ChatInputCompletions class with agent and file search providers - Add public addAttachment() to NewChatContextAttachments for the file completion command - Wire ChatInputCompletions into NewChatWidget alongside SlashCommandHandler Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds sessions-layer completion support for the new-chat input so that @ (agent) and # (file/context) completions work in the Sessions window despite using a non-workbench chat input URI scheme.
Changes:
- Added
ChatInputCompletionsto register@agent and#file completion providers for the sessions new-chat editor. - Exposed a public
addAttachment(...)API onNewChatContextAttachmentsto allow completion accept to create attachment pills. - Wired up the new completion registration in
NewChatViewPane.
Show a summary per file
| File | Description |
|---|---|
| src/vs/sessions/contrib/chat/browser/newChatViewPane.ts | Instantiates the new completions registrar alongside the existing slash command handler. |
| src/vs/sessions/contrib/chat/browser/newChatContextAttachments.ts | Adds a public method to append a single attachment entry and re-render. |
| src/vs/sessions/contrib/chat/browser/chatInputCompletions.ts | New sessions-layer completion providers for @ agents and #file search with an accept command to add attachment pills. |
Copilot's findings
- Files reviewed: 3/3 changed files
- Comments generated: 4
| const text = `${typedLeader}file:${name}`; | ||
| const uriLabel = this.labelService.getUriLabel(resource, { relative: true }); | ||
|
|
||
| return { | ||
| label: { label: name, description: uriLabel }, | ||
| filterText: `${name} ${typedLeader}${name} ${uriLabel}`, | ||
| insertText: range.varWord?.endColumn === range.replace.endColumn ? `${text} ` : text, |
There was a problem hiding this comment.
Accepting a file completion both inserts #file:… into the editor and also adds the file into attachedContext via the command. Since query is later sent as plain text alongside attachedContext, this can lead to duplicated/incorrect context (the text token may be parsed again by the workbench chat service). Consider removing the #file:… token from the input on accept (or using empty insertText and relying on the pill), similar to how the workbench reference command converts the typed token into an attachment.
| const text = `${typedLeader}file:${name}`; | |
| const uriLabel = this.labelService.getUriLabel(resource, { relative: true }); | |
| return { | |
| label: { label: name, description: uriLabel }, | |
| filterText: `${name} ${typedLeader}${name} ${uriLabel}`, | |
| insertText: range.varWord?.endColumn === range.replace.endColumn ? `${text} ` : text, | |
| const uriLabel = this.labelService.getUriLabel(resource, { relative: true }); | |
| return { | |
| label: { label: name, description: uriLabel }, | |
| filterText: `${name} ${typedLeader}${name} ${uriLabel}`, | |
| insertText: '', |
| this._register(this.languageFeaturesService.completionProvider.register({ scheme: uri.scheme, hasAccessToAllModels: true }, { | ||
| _debugDisplayName: 'sessionsFileCompletions', | ||
| triggerCharacters: [chatVariableLeader, chatAgentLeader], | ||
| provideCompletionItems: async (model: ITextModel, position: Position, _context: CompletionContext, token: CancellationToken) => { | ||
| const range = this._computeCompletionRanges(model, position, FileWord); |
There was a problem hiding this comment.
The file completion provider currently triggers for both # and @ and will run workspace file search for any @word (e.g. @w), mixing file results into agent completions and doing potentially expensive searches unnecessarily. If the intent is #file:… only, restrict triggerCharacters/regex to # and early-return unless the current word starts with #file:.
| const agents = this.chatAgentService.getAgents() | ||
| .filter(a => !a.isDefault); | ||
|
|
There was a problem hiding this comment.
Agent completions are built from chatAgentService.getAgents().filter(a => !a.isDefault) without filtering by agent locations/modes or handling restricted agent names. The workbench completion logic filters by widget location/mode and uses getFullyQualifiedId(...) when agent.name isn’t allowed; otherwise the inserted @${agent.name} can be unresolvable in chat input. Consider aligning with src/vs/workbench/contrib/chat/browser/widget/input/editor/chatInputCompletions.ts (AgentCompletions) for these filters/labels.
| private _getOrCreateCacheKey(): { key: string; time: number } { | ||
| if (this._cacheKey && Date.now() - this._cacheKey.time > 60_000) { | ||
| this.searchService.clearCache(this._cacheKey.key); | ||
| this._cacheKey = undefined; | ||
| } |
There was a problem hiding this comment.
_getOrCreateCacheKey clears the search cache only when the key expires and another completion request happens. Since this class is instantiated per new-chat widget, consider clearing the cache key in dispose() as well to avoid leaving search caches around after the widget is destroyed.
| private _getOrCreateCacheKey(): { key: string; time: number } { | |
| if (this._cacheKey && Date.now() - this._cacheKey.time > 60_000) { | |
| this.searchService.clearCache(this._cacheKey.key); | |
| this._cacheKey = undefined; | |
| } | |
| override dispose(): void { | |
| this._clearCacheKey(); | |
| super.dispose(); | |
| } | |
| private _clearCacheKey(): void { | |
| if (this._cacheKey) { | |
| this.searchService.clearCache(this._cacheKey.key); | |
| this._cacheKey = undefined; | |
| } | |
| } | |
| private _getOrCreateCacheKey(): { key: string; time: number } { | |
| if (this._cacheKey && Date.now() - this._cacheKey.time > 60_000) { | |
| this._clearCacheKey(); | |
| } |
The sessions window doesn't have a VS Code workspace context, so IWorkspaceContextService returns no folders. Instead, use the same selected project repo URI that the + button picker uses via _getContextFolderUri(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tual filesystems - Filter out chatSessionContributions (delegation agents) from @ completions, matching the workbench's AgentCompletions behavior - Allow bare # to show files without requiring a typed pattern - Add IFileService fallback for virtual filesystems (e.g. github-remote-file://) that ISearchService cannot query, walking the directory tree directly Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When typing # in the sessions chat input, the file search may return empty results (e.g., no workspace selected, virtual filesystem slow, or search service unavailable). Previously this resulted in zero suggestions, causing Monaco to not show the completion dropdown at all. Now: - Always show a '#file:' fallback entry that re-triggers suggest - Only trigger file completions on # (not @, which is handled by the agent provider) - Simplified pattern extraction to only strip # leader (not @) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Adds
@(agent) and#(file) completion providers to the sessions new-chat welcome widget (NewChatViewPane).Problem
The workbench chat completions (
AgentCompletions,BuiltinDynamicCompletions) register exclusively forscheme: Schemas.vscodeChatInput. The sessions new-chat input uses a different URI scheme (sessions-chat), so@and#completions never fire in the sessions window.Since the sessions layer cannot directly depend on the workbench chat completion infrastructure, dedicated providers are needed.
Changes
New file:
chatInputCompletions.ts@agent completions — queriesIChatAgentService.getAgents(), shows non-default agents and their slash commands. Only allowed at the start of input (matching workbench behavior).#file completions — searches workspace files viaISearchServicewhen the user types#file:pattern. On accept, adds the file as a context attachment pill via a registered command.SlashCommandHandlerpattern: registers onuri.scheme, uses_computeCompletionRanges()helper.newChatContextAttachments.tsaddAttachment(entry)method so the file completion command can add context pills (was previously only private_addAttachments).newChatViewPane.tsChatInputCompletionsalongside the existingSlashCommandHandler, passing the editor and context attachments.