chat: gate agent host input completions on trigger characters#315327
Merged
Conversation
Agent host completion providers were re-invoking `provideChatInputCompletions` on every keystroke, producing one RPC round-trip per character typed because Monaco still consults the provider for filtering / incomplete-result refresh even when the user isn't editing a trigger-led token. - Adds `isAtTriggerCharacterToken` helper in `chatInputCompletionUtils.ts` that returns true only when the cursor sits inside a non-empty token whose first char is one of the host-announced trigger characters. - Wires the gate into both agent host completion providers (workbench chat widget + sessions new-chat input) so the host RPC is only dispatched when the user is actively editing a `@`/`#`/... led token. - Extracts a small `AgentHostInputCompletionsBase` template-method base class that owns the shared plumbing (trigger-char gate, RPC dispatch, range computation, Monaco provider registration). Concrete subclasses now only supply `_resolveContext` (how to find the active session resource for a model) and `_buildItem` (how to build the Monaco completion item + accept command), keeping the registration lifecycle policy in each subclass. (Commit message generated by Copilot)
Contributor
There was a problem hiding this comment.
Pull request overview
This PR reduces unnecessary agent-host completion RPC traffic by gating provideChatInputCompletions calls so they only run when the cursor is within a trigger-led token (e.g. @...). It also factors out shared Monaco-provider plumbing into a reusable base class used by both the workbench chat widget and the sessions (Agents window) chat input.
Changes:
- Added
isAtTriggerCharacterTokenhelper to detect when the cursor is in a trigger-led token. - Introduced
AgentHostInputCompletionsBaseto share provider registration, trigger gating, RPC dispatch, and range computation. - Updated both agent-host completion handlers (workbench chat input + sessions new-chat input) to use the shared base.
Show a summary per file
| File | Description |
|---|---|
| src/vs/workbench/contrib/chat/browser/widget/input/editor/chatInputCompletionUtils.ts | Adds trigger-token detection helper used to gate RPC-backed completions. |
| src/vs/workbench/contrib/chat/browser/widget/input/editor/agentHostInputCompletionsBase.ts | New shared base class for agent-host input completion providers. |
| src/vs/workbench/contrib/chat/browser/widget/input/editor/agentHostInputCompletions.ts | Refactors workbench agent-host completion provider to use the shared base + gating. |
| src/vs/sessions/contrib/chat/browser/agentHostInputCompletions.ts | Refactors sessions new-chat agent-host completion provider to use the shared base + gating. |
Copilot's findings
Comments suppressed due to low confidence (1)
src/vs/workbench/contrib/chat/browser/widget/input/editor/chatInputCompletionUtils.ts:79
isAtTriggerCharacterTokenmisidentifies the token when the cursor is after trailing whitespace. Example: line "@foo " with the cursor after the space yieldstoken="@foo " and returns true, so the host provider can still be consulted even though the user is no longer editing the trigger-led token. Consider extracting the token as the last run of non-whitespace before the cursor (or explicitly return false when the character immediately before the cursor is whitespace).
const line = model.getLineContent(position.lineNumber);
const beforeCursor = line.slice(0, position.column - 1);
// The current token is everything from the last whitespace char (or
// start-of-line) up to the cursor.
const wsIdx = beforeCursor.search(/\s\S*$/);
const token = wsIdx >= 0 ? beforeCursor.slice(wsIdx + 1) : beforeCursor;
if (token.length === 0) {
return false;
}
return triggerCharacters.includes(token[0]);
- Files reviewed: 4/4 changed files
- Comments generated: 2
- Restore the per-scheme match in `AgentHostInputCompletions._resolveContext` by threading the registered scheme through `_registerProvider` as a new `TRegData` generic on `AgentHostInputCompletionsBase`. Without this, multiple agent-host providers sharing trigger characters (e.g. both register `@`) would all fire on a single keystroke and produce duplicate RPCs / suggestions. - Mirror the same scheme-match in the sessions handler so a stale registration (active session changed during the host RPC handshake) silently no-ops instead of querying the wrong host. - Add focused unit tests for the new `isAtTriggerCharacterToken` helper covering start-of-line, mid-line, cursor-in-token, cursor-after-token, cursor-after-whitespace, non-trigger-led tokens, and the empty trigger-list case. (Commit message generated by Copilot)
eleanorjboyd
approved these changes
May 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
chat: gate agent host input completions on trigger characters
Agent host completion providers were re-invoking
provideChatInputCompletionson every keystroke, producing one RPC round-trip per character typed because
Monaco still consults the provider for filtering / incomplete-result refresh
even when the user isn't editing a trigger-led token.
isAtTriggerCharacterTokenhelper inchatInputCompletionUtils.tsthat returns true only when the cursor sits inside a non-empty token whose
first char is one of the host-announced trigger characters.
widget + sessions new-chat input) so the host RPC is only dispatched when
the user is actively editing a
@/#/... led token.AgentHostInputCompletionsBasetemplate-method baseclass that owns the shared plumbing (trigger-char gate, RPC dispatch,
range computation, Monaco provider registration). Concrete subclasses
now only supply
_resolveContext(how to find the active sessionresource for a model) and
_buildItem(how to build the Monacocompletion item + accept command), keeping the registration lifecycle
policy in each subclass.
(Commit message generated by Copilot)