mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-18 09:29:42 +01:00
Fixes for tool invocations/confirmations to work better with external agents (#264180)
For #262756
This commit is contained in:
@@ -11,7 +11,7 @@ import { IDisposable, toDisposable } from '../../../base/common/lifecycle.js';
|
||||
import { revive } from '../../../base/common/marshalling.js';
|
||||
import { generateUuid } from '../../../base/common/uuid.js';
|
||||
import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
|
||||
import { IPreparedToolInvocation, isToolInvocationContext, IToolInvocation, IToolInvocationContext, IToolInvocationPreparationContext, IToolResult } from '../../contrib/chat/common/languageModelToolsService.js';
|
||||
import { IPreparedToolInvocation, isToolInvocationContext, IToolInvocation, IToolInvocationContext, IToolInvocationPreparationContext, IToolResult, ToolInvocationPresentation } from '../../contrib/chat/common/languageModelToolsService.js';
|
||||
import { ExtensionEditToolId, InternalEditToolId } from '../../contrib/chat/common/tools/editFileTool.js';
|
||||
import { InternalFetchWebPageToolId } from '../../contrib/chat/common/tools/tools.js';
|
||||
import { checkProposedApiEnabled, isProposedApiEnabled } from '../../services/extensions/common/extensions.js';
|
||||
@@ -264,7 +264,7 @@ export class ExtHostLanguageModelTools implements ExtHostLanguageModelToolsShape
|
||||
} : undefined,
|
||||
invocationMessage: typeConvert.MarkdownString.fromStrict(result.invocationMessage),
|
||||
pastTenseMessage: typeConvert.MarkdownString.fromStrict(result.pastTenseMessage),
|
||||
presentation: result.presentation
|
||||
presentation: result.presentation as ToolInvocationPresentation | undefined
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -34,24 +34,28 @@ import * as languages from '../../../editor/common/languages.js';
|
||||
import { EndOfLineSequence, TrackedRangeStickiness } from '../../../editor/common/model.js';
|
||||
import { ITextEditorOptions } from '../../../platform/editor/common/editor.js';
|
||||
import { IExtensionDescription, IRelaxedExtensionDescription } from '../../../platform/extensions/common/extensions.js';
|
||||
import { ILogService } from '../../../platform/log/common/log.js';
|
||||
import { IMarkerData, IRelatedInformation, MarkerSeverity, MarkerTag } from '../../../platform/markers/common/markers.js';
|
||||
import { ProgressLocation as MainProgressLocation } from '../../../platform/progress/common/progress.js';
|
||||
import { DEFAULT_EDITOR_ASSOCIATION, SaveReason } from '../../common/editor.js';
|
||||
import { IViewBadge } from '../../common/views.js';
|
||||
import { IChatAgentRequest, IChatAgentResult } from '../../contrib/chat/common/chatAgents.js';
|
||||
import { IChatRequestDraft } from '../../contrib/chat/common/chatEditingService.js';
|
||||
import { IChatAgentMarkdownContentWithVulnerability, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatExtensionsContent, IChatFollowup, IChatMarkdownContent, IChatMoveMessage, IChatMultiDiffData, IChatPrepareToolInvocationPart, IChatProgressMessage, IChatPullRequestContent, IChatResponseCodeblockUriPart, IChatTaskDto, IChatTaskResult, IChatTextEdit, IChatThinkingPart, IChatToolInvocationSerialized, IChatTreeData, IChatUserActionEvent, IChatWarningMessage } from '../../contrib/chat/common/chatService.js';
|
||||
import { IChatRequestVariableEntry, isImageVariableEntry } from '../../contrib/chat/common/chatVariableEntries.js';
|
||||
import { IChatAgentMarkdownContentWithVulnerability, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatExtensionsContent, IChatFollowup, IChatMarkdownContent, IChatMoveMessage, IChatMultiDiffData, IChatPrepareToolInvocationPart, IChatProgressMessage, IChatPullRequestContent, IChatResponseCodeblockUriPart, IChatTaskDto, IChatTaskResult, IChatTextEdit, IChatThinkingPart, IChatTreeData, IChatUserActionEvent, IChatWarningMessage } from '../../contrib/chat/common/chatService.js';
|
||||
import { ChatAgentLocation } from '../../contrib/chat/common/constants.js';
|
||||
import { IToolResult, IToolResultInputOutputDetails, IToolResultOutputDetails, ToolDataSource } from '../../contrib/chat/common/languageModelToolsService.js';
|
||||
import * as chatProvider from '../../contrib/chat/common/languageModels.js';
|
||||
import { IChatMessageDataPart, IChatResponseDataPart, IChatResponsePromptTsxPart, IChatResponseTextPart } from '../../contrib/chat/common/languageModels.js';
|
||||
import { DebugTreeItemCollapsibleState, IDebugVisualizationTreeItem } from '../../contrib/debug/common/debug.js';
|
||||
import { McpServerLaunch, McpServerTransportType } from '../../contrib/mcp/common/mcpTypes.js';
|
||||
import * as notebooks from '../../contrib/notebook/common/notebookCommon.js';
|
||||
import { CellEditType } from '../../contrib/notebook/common/notebookCommon.js';
|
||||
import { ICellRange } from '../../contrib/notebook/common/notebookRange.js';
|
||||
import * as search from '../../contrib/search/common/search.js';
|
||||
import { TestId } from '../../contrib/testing/common/testId.js';
|
||||
import { CoverageDetails, DetailType, ICoverageCount, IFileCoverage, ISerializedTestResults, ITestErrorMessage, ITestItem, ITestRunProfileReference, ITestTag, TestMessageType, TestResultItem, TestRunProfileBitset, denamespaceTestTag, namespaceTestTag } from '../../contrib/testing/common/testTypes.js';
|
||||
import { AiSettingsSearchResult, AiSettingsSearchResultKind } from '../../services/aiSettingsSearch/common/aiSettingsSearch.js';
|
||||
import { EditorGroupColumn } from '../../services/editor/common/editorGroupColumn.js';
|
||||
import { ACTIVE_GROUP, SIDE_GROUP } from '../../services/editor/common/editorService.js';
|
||||
import { checkProposedApiEnabled, isProposedApiEnabled } from '../../services/extensions/common/extensions.js';
|
||||
@@ -61,10 +65,6 @@ import { CommandsConverter } from './extHostCommands.js';
|
||||
import { getPrivateApiFor } from './extHostTestingPrivateApi.js';
|
||||
import * as types from './extHostTypes.js';
|
||||
import { LanguageModelDataPart, LanguageModelPromptTsxPart, LanguageModelTextPart } from './extHostTypes.js';
|
||||
import { ChatAgentLocation } from '../../contrib/chat/common/constants.js';
|
||||
import { AiSettingsSearchResult, AiSettingsSearchResultKind } from '../../services/aiSettingsSearch/common/aiSettingsSearch.js';
|
||||
import { McpServerLaunch, McpServerTransportType } from '../../contrib/mcp/common/mcpTypes.js';
|
||||
import { ILogService } from '../../../platform/log/common/log.js';
|
||||
|
||||
export namespace Command {
|
||||
|
||||
@@ -2784,7 +2784,7 @@ export namespace ChatPrepareToolInvocationPart {
|
||||
}
|
||||
|
||||
export namespace ChatToolInvocationPart {
|
||||
export function from(part: vscode.ChatToolInvocationPart): any {
|
||||
export function from(part: vscode.ChatToolInvocationPart): IChatToolInvocationSerialized {
|
||||
// Convert extension API ChatToolInvocationPart to internal serialized format
|
||||
return {
|
||||
kind: 'toolInvocationSerialized',
|
||||
@@ -2795,8 +2795,8 @@ export namespace ChatToolInvocationPart {
|
||||
pastTenseMessage: part.pastTenseMessage ? MarkdownString.from(part.pastTenseMessage) : undefined,
|
||||
isConfirmed: part.isConfirmed,
|
||||
isComplete: part.isComplete ?? true,
|
||||
isError: part.isError ?? false,
|
||||
resultDetails: undefined,
|
||||
source: ToolDataSource.External,
|
||||
// isError: part.isError ?? false,
|
||||
toolSpecificData: part.toolSpecificData ? convertToolSpecificData(part.toolSpecificData) : undefined,
|
||||
presentation: undefined
|
||||
};
|
||||
|
||||
@@ -234,6 +234,8 @@ export async function showToolsPicker(
|
||||
return BucketOrdinal.BuiltIn.toString();
|
||||
case 'user':
|
||||
return BucketOrdinal.User.toString();
|
||||
case 'external':
|
||||
throw new Error('should not be reachable');
|
||||
default:
|
||||
assertNever(source);
|
||||
}
|
||||
|
||||
+8
-4
@@ -8,11 +8,12 @@ import { Emitter } from '../../../../../../base/common/event.js';
|
||||
import { markdownCommandLink, MarkdownString } from '../../../../../../base/common/htmlContent.js';
|
||||
import { Disposable, DisposableStore, IDisposable } from '../../../../../../base/common/lifecycle.js';
|
||||
import { MarkdownRenderer } from '../../../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js';
|
||||
import { localize } from '../../../../../../nls.js';
|
||||
import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js';
|
||||
import { IChatToolInvocation, IChatToolInvocationSerialized, ToolConfirmKind } from '../../../common/chatService.js';
|
||||
import { IChatRendererContent } from '../../../common/chatViewModel.js';
|
||||
import { CodeBlockModelCollection } from '../../../common/codeBlockModelCollection.js';
|
||||
import { isToolResultInputOutputDetails, isToolResultOutputDetails } from '../../../common/languageModelToolsService.js';
|
||||
import { isToolResultInputOutputDetails, isToolResultOutputDetails, ToolInvocationPresentation } from '../../../common/languageModelToolsService.js';
|
||||
import { ChatTreeItem, IChatCodeBlockInfo } from '../../chat.js';
|
||||
import { IChatContentPart, IChatContentPartRenderContext } from '../chatContentParts.js';
|
||||
import { EditorPool } from '../chatMarkdownContentPart.js';
|
||||
@@ -20,14 +21,13 @@ import { CollapsibleListPool } from '../chatReferencesContentPart.js';
|
||||
import { ExtensionsInstallConfirmationWidgetSubPart } from './chatExtensionsInstallToolSubPart.js';
|
||||
import { ChatInputOutputMarkdownProgressPart } from './chatInputOutputMarkdownProgressPart.js';
|
||||
import { ChatResultListSubPart } from './chatResultListSubPart.js';
|
||||
import { ChatTerminalToolProgressPart } from './chatTerminalToolProgressPart.js';
|
||||
import { ChatTerminalToolConfirmationSubPart } from './chatTerminalToolConfirmationSubPart.js';
|
||||
import { ChatTerminalToolProgressPart } from './chatTerminalToolProgressPart.js';
|
||||
import { ChatTodoListSubPart } from './chatTodoListSubPart.js';
|
||||
import { ToolConfirmationSubPart } from './chatToolConfirmationSubPart.js';
|
||||
import { BaseChatToolInvocationSubPart } from './chatToolInvocationSubPart.js';
|
||||
import { ChatToolOutputSubPart } from './chatToolOutputPart.js';
|
||||
import { ChatToolProgressSubPart } from './chatToolProgressPart.js';
|
||||
import { localize } from '../../../../../../nls.js';
|
||||
import { ChatTodoListSubPart } from './chatTodoListSubPart.js';
|
||||
|
||||
export class ChatToolInvocationPart extends Disposable implements IChatContentPart {
|
||||
public readonly domNode: HTMLElement;
|
||||
@@ -71,6 +71,10 @@ export class ChatToolInvocationPart extends Disposable implements IChatContentPa
|
||||
dom.clearNode(this.domNode);
|
||||
partStore.clear();
|
||||
|
||||
if (toolInvocation.presentation === ToolInvocationPresentation.HiddenAfterComplete && toolInvocation.isComplete) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.subPart = partStore.add(this.createToolInvocationSubPart());
|
||||
this.domNode.appendChild(this.subPart.domNode);
|
||||
partStore.add(this.subPart.onDidChangeHeight(() => this._onDidChangeHeight.fire()));
|
||||
|
||||
@@ -759,7 +759,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
||||
if (
|
||||
!lastPart ||
|
||||
lastPart.kind === 'references' ||
|
||||
(lastPart.kind === 'toolInvocation' && (lastPart.isComplete || lastPart.presentation === 'hidden')) ||
|
||||
((lastPart.kind === 'toolInvocation' || lastPart.kind === 'toolInvocationSerialized') && (lastPart.isComplete || lastPart.presentation === 'hidden')) ||
|
||||
((lastPart.kind === 'textEditGroup' || lastPart.kind === 'notebookEditGroup') && lastPart.done && !partsToRender.some(part => part.kind === 'toolInvocation' && !part.isComplete)) ||
|
||||
(lastPart.kind === 'progressTask' && lastPart.deferred.isSettled) ||
|
||||
lastPart.kind === 'prepareToolInvocation'
|
||||
|
||||
@@ -758,7 +758,7 @@ class BuiltinDynamicCompletions extends Disposable {
|
||||
// If locked to an agent that doesn't support file attachments, skip
|
||||
if (widget.lockedAgentId) {
|
||||
const agent = this.chatAgentService.getAgent(widget.lockedAgentId);
|
||||
if (agent && agent.capabilities && agent.capabilities.supportsFileAttachments === false) {
|
||||
if (agent && !agent.capabilities?.supportsFileAttachments) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1125,7 +1125,7 @@ class ToolCompletions extends Disposable {
|
||||
// If locked to an agent that doesn't support tool attachments, skip
|
||||
if (widget.lockedAgentId) {
|
||||
const agent = this.chatAgentService.getAgent(widget.lockedAgentId);
|
||||
if (agent && agent.capabilities?.supportsToolAttachments === false) {
|
||||
if (agent && !agent.capabilities?.supportsToolAttachments) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -967,7 +967,17 @@ export class ChatService extends Disposable implements IChatService {
|
||||
} satisfies IChatAgentRequest;
|
||||
};
|
||||
|
||||
if (this.configurationService.getValue('chat.detectParticipant.enabled') !== false && this.chatAgentService.hasChatParticipantDetectionProviders() && !agentPart && !commandPart && !agentSlashCommandPart && enableCommandDetection && options?.modeInfo?.kind !== ChatModeKind.Agent && options?.modeInfo?.kind !== ChatModeKind.Edit) {
|
||||
if (
|
||||
this.configurationService.getValue('chat.detectParticipant.enabled') !== false &&
|
||||
this.chatAgentService.hasChatParticipantDetectionProviders() &&
|
||||
!agentPart &&
|
||||
!commandPart &&
|
||||
!agentSlashCommandPart &&
|
||||
enableCommandDetection &&
|
||||
options?.modeInfo?.kind !== ChatModeKind.Agent &&
|
||||
options?.modeInfo?.kind !== ChatModeKind.Edit &&
|
||||
!options?.agentIdSilent
|
||||
) {
|
||||
// We have no agent or command to scope history with, pass the full history to the participant detection provider
|
||||
const defaultAgentHistory = this.getHistoryEntriesFromModel(requests, model.sessionId, location, defaultAgent.id);
|
||||
|
||||
|
||||
@@ -75,18 +75,25 @@ export type ToolDataSource =
|
||||
| {
|
||||
type: 'internal';
|
||||
label: string;
|
||||
} | {
|
||||
type: 'external';
|
||||
label: string;
|
||||
};
|
||||
|
||||
export namespace ToolDataSource {
|
||||
|
||||
export const Internal: ToolDataSource = { type: 'internal', label: 'Built-In' };
|
||||
|
||||
/** External tools may not be contributed or invoked, but may be invoked externally and described in an IChatToolInvocationSerialized */
|
||||
export const External: ToolDataSource = { type: 'external', label: 'External' };
|
||||
|
||||
export function toKey(source: ToolDataSource): string {
|
||||
switch (source.type) {
|
||||
case 'extension': return `extension:${source.extensionId.value}`;
|
||||
case 'mcp': return `mcp:${source.collectionId}:${source.definitionId}`;
|
||||
case 'user': return `user:${source.file.toString()}`;
|
||||
case 'internal': return 'internal';
|
||||
case 'external': return 'external';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,12 +229,17 @@ export interface IToolConfirmationAction {
|
||||
|
||||
export type ToolConfirmationAction = IToolConfirmationAction | Separator;
|
||||
|
||||
export enum ToolInvocationPresentation {
|
||||
Hidden = 'hidden',
|
||||
HiddenAfterComplete = 'hiddenAfterComplete'
|
||||
}
|
||||
|
||||
export interface IPreparedToolInvocation {
|
||||
invocationMessage?: string | IMarkdownString;
|
||||
pastTenseMessage?: string | IMarkdownString;
|
||||
originMessage?: string | IMarkdownString;
|
||||
confirmationMessages?: IToolConfirmationMessages;
|
||||
presentation?: 'hidden' | undefined;
|
||||
presentation?: ToolInvocationPresentation;
|
||||
toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatTodoListContent;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { CancellationToken } from '../../../../../base/common/cancellation.js';
|
||||
import { MarkdownString } from '../../../../../base/common/htmlContent.js';
|
||||
import { IChatTerminalToolInvocationData } from '../chatService.js';
|
||||
import { CountTokensCallback, IPreparedToolInvocation, IToolData, IToolImpl, IToolInvocation, IToolInvocationPreparationContext, IToolResult, ToolDataSource, ToolProgress } from '../languageModelToolsService.js';
|
||||
import { CountTokensCallback, IPreparedToolInvocation, IToolData, IToolImpl, IToolInvocation, IToolInvocationPreparationContext, IToolResult, ToolDataSource, ToolInvocationPresentation, ToolProgress } from '../languageModelToolsService.js';
|
||||
|
||||
export const ConfirmationToolId = 'vscode_get_confirmation';
|
||||
|
||||
@@ -41,7 +41,7 @@ export const ConfirmationToolData: IToolData = {
|
||||
}
|
||||
};
|
||||
|
||||
export interface ConfirmationToolParams {
|
||||
export interface IConfirmationToolParams {
|
||||
title: string;
|
||||
message: string;
|
||||
confirmationType?: 'basic' | 'terminal';
|
||||
@@ -50,7 +50,7 @@ export interface ConfirmationToolParams {
|
||||
|
||||
export class ConfirmationTool implements IToolImpl {
|
||||
async prepareToolInvocation(context: IToolInvocationPreparationContext, token: CancellationToken): Promise<IPreparedToolInvocation | undefined> {
|
||||
const parameters = context.parameters as ConfirmationToolParams;
|
||||
const parameters = context.parameters as IConfirmationToolParams;
|
||||
if (!parameters.title || !parameters.message) {
|
||||
throw new Error('Missing required parameters for ConfirmationTool');
|
||||
}
|
||||
@@ -80,7 +80,8 @@ export class ConfirmationTool implements IToolImpl {
|
||||
message: new MarkdownString(parameters.message),
|
||||
allowAutoConfirm: false
|
||||
},
|
||||
toolSpecificData
|
||||
toolSpecificData,
|
||||
presentation: ToolInvocationPresentation.HiddenAfterComplete
|
||||
};
|
||||
}
|
||||
|
||||
@@ -89,7 +90,7 @@ export class ConfirmationTool implements IToolImpl {
|
||||
return {
|
||||
content: [{
|
||||
kind: 'text',
|
||||
value: `Confirmed`
|
||||
value: 'yes' // Consumers should check for this label to know whether the tool was confirmed or skipped
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { INotebookService } from '../../../notebook/common/notebookService.js';
|
||||
import { ICodeMapperService } from '../../common/chatCodeMapperService.js';
|
||||
import { ChatModel } from '../../common/chatModel.js';
|
||||
import { IChatService } from '../../common/chatService.js';
|
||||
import { CountTokensCallback, IPreparedToolInvocation, IToolData, IToolImpl, IToolInvocation, IToolInvocationPreparationContext, IToolResult, ToolDataSource, ToolProgress } from '../../common/languageModelToolsService.js';
|
||||
import { CountTokensCallback, IPreparedToolInvocation, IToolData, IToolImpl, IToolInvocation, IToolInvocationPreparationContext, IToolResult, ToolDataSource, ToolInvocationPresentation, ToolProgress } from '../../common/languageModelToolsService.js';
|
||||
|
||||
export const ExtensionEditToolId = 'vscode_editFile';
|
||||
export const InternalEditToolId = 'vscode_editFile_internal';
|
||||
@@ -138,7 +138,7 @@ export class EditTool implements IToolImpl {
|
||||
|
||||
async prepareToolInvocation(context: IToolInvocationPreparationContext, token: CancellationToken): Promise<IPreparedToolInvocation | undefined> {
|
||||
return {
|
||||
presentation: 'hidden'
|
||||
presentation: ToolInvocationPresentation.Hidden
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ import { TerminalCapability } from '../../../../../../platform/terminal/common/c
|
||||
import { ITerminalLogService } from '../../../../../../platform/terminal/common/terminal.js';
|
||||
import { IRemoteAgentService } from '../../../../../services/remote/common/remoteAgentService.js';
|
||||
import { IChatService, type IChatTerminalToolInvocationData } from '../../../../chat/common/chatService.js';
|
||||
import { CountTokensCallback, ILanguageModelToolsService, IPreparedToolInvocation, IToolData, IToolImpl, IToolInvocation, IToolInvocationPreparationContext, IToolResult, ToolDataSource, ToolProgress, type IToolConfirmationMessages, type ToolConfirmationAction } from '../../../../chat/common/languageModelToolsService.js';
|
||||
import { CountTokensCallback, ILanguageModelToolsService, IPreparedToolInvocation, IToolData, IToolImpl, IToolInvocation, IToolInvocationPreparationContext, IToolResult, ToolDataSource, ToolInvocationPresentation, ToolProgress, type IToolConfirmationMessages, type ToolConfirmationAction } from '../../../../chat/common/languageModelToolsService.js';
|
||||
import { ITerminalService, type ITerminalInstance } from '../../../../terminal/browser/terminal.js';
|
||||
import type { XtermTerminal } from '../../../../terminal/browser/xterm/xtermTerminal.js';
|
||||
import { ITerminalProfileResolverService } from '../../../../terminal/common/terminal.js';
|
||||
@@ -211,7 +211,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl {
|
||||
const args = context.parameters as IRunInTerminalInputParams;
|
||||
|
||||
const alternativeRecommendation = getRecommendedToolsOverRunInTerminal(args.command, this._languageModelToolsService);
|
||||
const presentation = alternativeRecommendation ? 'hidden' : undefined;
|
||||
const presentation = alternativeRecommendation ? ToolInvocationPresentation.Hidden : undefined;
|
||||
|
||||
const os = await this._osBackend;
|
||||
const shell = await this._getCopilotShell();
|
||||
|
||||
Reference in New Issue
Block a user