From d668f8317b771972e0719be431b2671e5088cf8b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sat, 30 Aug 2025 14:42:07 -0700 Subject: [PATCH] Fixes for tool invocations/confirmations to work better with external agents (#264180) For #262756 --- .../api/common/extHostLanguageModelTools.ts | 4 ++-- .../api/common/extHostTypeConverters.ts | 16 ++++++++-------- .../chat/browser/actions/chatToolPicker.ts | 2 ++ .../chatToolInvocationPart.ts | 12 ++++++++---- .../contrib/chat/browser/chatListRenderer.ts | 2 +- .../chat/browser/contrib/chatInputCompletions.ts | 4 ++-- .../contrib/chat/common/chatServiceImpl.ts | 12 +++++++++++- .../chat/common/languageModelToolsService.ts | 14 +++++++++++++- .../chat/common/tools/confirmationTool.ts | 11 ++++++----- .../contrib/chat/common/tools/editFileTool.ts | 4 ++-- .../browser/tools/runInTerminalTool.ts | 4 ++-- 11 files changed, 57 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/api/common/extHostLanguageModelTools.ts b/src/vs/workbench/api/common/extHostLanguageModelTools.ts index 1646f8ee1b9..9f2dc36a420 100644 --- a/src/vs/workbench/api/common/extHostLanguageModelTools.ts +++ b/src/vs/workbench/api/common/extHostLanguageModelTools.ts @@ -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 }; } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index b7df447eb90..d8fb60663c8 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -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 }; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatToolPicker.ts b/src/vs/workbench/contrib/chat/browser/actions/chatToolPicker.ts index 9bbbffef278..f6a92eb77f0 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatToolPicker.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatToolPicker.ts @@ -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); } diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/toolInvocationParts/chatToolInvocationPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/toolInvocationParts/chatToolInvocationPart.ts index 6c6b3ffbccb..f81cb2c3a80 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/toolInvocationParts/chatToolInvocationPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/toolInvocationParts/chatToolInvocationPart.ts @@ -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())); diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 66aa8f7ee00..8e6f58c688d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -759,7 +759,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer part.kind === 'toolInvocation' && !part.isComplete)) || (lastPart.kind === 'progressTask' && lastPart.deferred.isSettled) || lastPart.kind === 'prepareToolInvocation' diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts index 54962290d23..9e2ce853d1b 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts @@ -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; } } diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 8a86c871e63..2cfb585da70 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -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); diff --git a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts index 9f6b213c3f5..a2e7176fc26 100644 --- a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts @@ -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; } diff --git a/src/vs/workbench/contrib/chat/common/tools/confirmationTool.ts b/src/vs/workbench/contrib/chat/common/tools/confirmationTool.ts index e5abc5e7c30..2347b34df91 100644 --- a/src/vs/workbench/contrib/chat/common/tools/confirmationTool.ts +++ b/src/vs/workbench/contrib/chat/common/tools/confirmationTool.ts @@ -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 { - 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 }] }; } diff --git a/src/vs/workbench/contrib/chat/common/tools/editFileTool.ts b/src/vs/workbench/contrib/chat/common/tools/editFileTool.ts index d2a7fb6ba02..3b94293123a 100644 --- a/src/vs/workbench/contrib/chat/common/tools/editFileTool.ts +++ b/src/vs/workbench/contrib/chat/common/tools/editFileTool.ts @@ -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 { return { - presentation: 'hidden' + presentation: ToolInvocationPresentation.Hidden }; } } diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts index 20f1222f2c8..3194c3799bf 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -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();