diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index 7b0fe613..93a683a2 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -11,12 +11,12 @@ import { IStorageService, StorageScope, StorageTarget } from '../../../../platfo import { URI } from '../../../../base/common/uri.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { ILLMMessageService } from '../common/sendLLMMessageService.js'; -import { chat_userMessageContent, ToolName, } from '../common/prompt/prompts.js'; +import { chat_userMessageContent } from '../common/prompt/prompts.js'; import { AnthropicReasoning, getErrorMessage, RawToolCallObj, RawToolParamsObj } from '../common/sendLLMMessageTypes.js'; import { generateUuid } from '../../../../base/common/uuid.js'; import { FeatureName, ModelSelection, ModelSelectionOptions } from '../common/voidSettingsTypes.js'; import { IVoidSettingsService } from '../common/voidSettingsService.js'; -import { approvalTypeOfToolName, BuiltinToolCallParams, BuiltinToolResultType } from '../common/toolsServiceTypes.js'; +import { approvalTypeOfBuiltinToolName, BuiltinToolCallParams, isABuiltinToolName, ToolCallParams, ToolName, ToolResult } from '../common/toolsServiceTypes.js'; import { IToolsService } from './toolsService.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; @@ -37,6 +37,8 @@ import { deepClone } from '../../../../base/common/objects.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { IDirectoryStrService } from '../common/directoryStrService.js'; import { IFileService } from '../../../../platform/files/common/files.js'; +import { IMCPService } from '../common/mcpService.js'; +import { RawMCPToolCall } from '../common/mcpServiceTypes.js'; // related to retrying when LLM message has error @@ -181,7 +183,7 @@ export type ThreadStreamState = { llmInfo?: undefined; toolInfo: { toolName: ToolName; - toolParams: BuiltinToolCallParams[ToolName]; + toolParams: ToolCallParams; id: string; content: string; rawParams: RawToolParamsObj; @@ -323,6 +325,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, @IDirectoryStrService private readonly _directoryStringService: IDirectoryStrService, @IFileService private readonly _fileService: IFileService, + @IMCPService private readonly _mcpService: IMCPService, ) { super() this.state = { allThreads: {}, currentThreadId: null as unknown as string } // default state @@ -532,7 +535,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { const lastMsg = thread.messages[thread.messages.length - 1] - let params: BuiltinToolCallParams[ToolName] + let params: ToolCallParams if (lastMsg.role === 'tool' && lastMsg.type !== 'invalid_params') { params = lastMsg.params } @@ -597,20 +600,30 @@ class ChatThreadService extends Disposable implements IChatThreadService { threadId: string, toolName: ToolName, toolId: string, - opts: { preapproved: true, unvalidatedToolParams: RawToolParamsObj, validatedParams: BuiltinToolCallParams[ToolName] } | { preapproved: false, unvalidatedToolParams: RawToolParamsObj }, + opts: { preapproved: true, unvalidatedToolParams: RawToolParamsObj, validatedParams: ToolCallParams } | { preapproved: false, unvalidatedToolParams: RawToolParamsObj }, ): Promise<{ awaitingUserApproval?: boolean, interrupted?: boolean }> => { // compute these below - let toolParams: BuiltinToolCallParams[ToolName] - let toolResult: Awaited + let toolParams: ToolCallParams + let toolResult: ToolResult let toolResultStr: string + // Check if it's a built-in tool + const isBuiltInTool = isABuiltinToolName(toolName) + + if (!opts.preapproved) { // skip this if pre-approved // 1. validate tool params try { - const params = this._toolsService.validateParams[toolName](opts.unvalidatedToolParams) - toolParams = params - } catch (error) { + if (isBuiltInTool) { + const params = this._toolsService.validateParams[toolName](opts.unvalidatedToolParams) + toolParams = params + } + else { + toolParams = opts.unvalidatedToolParams + } + } + catch (error) { const errorMessage = getErrorMessage(error) this._addMessageToThread(threadId, { role: 'tool', type: 'invalid_params', rawParams: opts.unvalidatedToolParams, result: null, name: toolName, content: errorMessage, id: toolId, }) return {} @@ -621,8 +634,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { // 2. if tool requires approval, break from the loop, awaiting approval - - const approvalType = approvalTypeOfToolName[toolName] + const approvalType = isBuiltInTool ? approvalTypeOfBuiltinToolName[toolName] : 'mcp-tools' if (approvalType) { const autoApprove = this._settingsService.state.globalSettings.autoApprove[approvalType] // add a tool_request because we use it for UI if a tool is loading (this should be improved in the future) @@ -638,30 +650,6 @@ class ChatThreadService extends Disposable implements IChatThreadService { - // TOOL_TODO!!!!!!!!! call the builtin versus the MCP tool here (finish filling in the comment below and replace it out with the tool call and stringify functions further below) - // const isBuiltInTool = (toolNames as string[]).includes(toolName) - // const callToolFn = (toolName: string, toolParams: BuiltinToolCallParams[ToolName]) => { - // if (isBuiltInTool) { - - - // } - // else { - - // } - - // } - - // const stringifyToolFn = (toolName: string, toolResult: Awaited) => { - // if (isBuiltInTool) { - - - // } - // else { - // if (result.event === 'error' || result.event === 'text') { - // return result.text; - // } - // } - // } @@ -679,11 +667,26 @@ class ChatThreadService extends Disposable implements IChatThreadService { // set stream state this._setStreamState(threadId, { isRunning: 'tool', interrupt: interruptorPromise, toolInfo: { toolName, toolParams, id: toolId, content: 'interrupted...', rawParams: opts.unvalidatedToolParams } }) - const { result, interruptTool } = await this._toolsService.callTool[toolName](toolParams as any) - const interruptor = () => { interrupted = true; interruptTool?.() } - resolveInterruptor(interruptor) + if (isBuiltInTool) { + const { result, interruptTool } = await this._toolsService.callTool[toolName](toolParams as any) + const interruptor = () => { interrupted = true; interruptTool?.() } + resolveInterruptor(interruptor) - toolResult = await result + toolResult = await result + } + else { + const mcpTools = this._mcpService.getMCPTools() + const mcpTool = mcpTools[toolName] + if (!mcpTool) { throw new Error(`MCP tool ${toolName} not found`) } + + resolveInterruptor(() => { }) + + toolResult = (await this._mcpService.callMCPTool({ + serverName: mcpTool.mcpServerName ?? 'unknown_mcp_server', + toolName: toolName, + params: toolParams + })).result + } if (interrupted) { return { interrupted: true } } // the tool result is added where we interrupt, not here } @@ -698,7 +701,26 @@ class ChatThreadService extends Disposable implements IChatThreadService { // 4. stringify the result to give to the LLM try { - toolResultStr = this._toolsService.stringOfResult[toolName](toolParams as any, toolResult as any) + if (isBuiltInTool) { + toolResultStr = this._toolsService.stringOfResult[toolName](toolParams as any, toolResult as any) + } + // For MCP tools, handle the result based on its type + else { + const toolResult_ = toolResult as RawMCPToolCall + if (toolResult_.event === 'text') { + toolResultStr = toolResult_.text + } else if (toolResult_.event === 'error') { + toolResultStr = `Error: ${toolResult_.text}` + } else if (toolResult_.event === 'image') { + toolResultStr = `[Image: ${toolResult_.image.mimeType}]` + } else if (toolResult_.event === 'audio') { + toolResultStr = `[Audio content]` + } else if (toolResult_.event === 'resource') { + toolResultStr = `[Resource content]` + } else { + toolResultStr = JSON.stringify(toolResult) + } + } } catch (error) { const errorMessage = this.toolErrMsgs.errWhenStringifying(error) this._updateLatestTool(threadId, { role: 'tool', type: 'tool_error', params: toolParams, result: errorMessage, name: toolName, content: errorMessage, id: toolId, rawParams: opts.unvalidatedToolParams }) diff --git a/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts b/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts index 01fd58aa..f27828c2 100644 --- a/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts +++ b/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts @@ -7,7 +7,7 @@ import { IWorkspaceContextService } from '../../../../platform/workspace/common/ import { IEditorService } from '../../../services/editor/common/editorService.js'; import { ChatMessage } from '../common/chatThreadServiceTypes.js'; import { getIsReasoningEnabledState, getReservedOutputTokenSpace, getModelCapabilities } from '../common/modelCapabilities.js'; -import { reParsedToolXMLString, chat_systemMessage, ToolName } from '../common/prompt/prompts.js'; +import { reParsedToolXMLString, chat_systemMessage } from '../common/prompt/prompts.js'; import { AnthropicLLMChatMessage, AnthropicReasoning, GeminiLLMChatMessage, LLMChatMessage, LLMFIMMessage, OpenAILLMChatMessage, RawToolParamsObj } from '../common/sendLLMMessageTypes.js'; import { IVoidSettingsService } from '../common/voidSettingsService.js'; import { ChatMode, FeatureName, ModelSelection, ProviderName } from '../common/voidSettingsTypes.js'; @@ -16,6 +16,7 @@ import { ITerminalToolService } from './terminalToolService.js'; import { IVoidModelService } from '../common/voidModelService.js'; import { URI } from '../../../../base/common/uri.js'; import { EndOfLinePreference } from '../../../../editor/common/model.js'; +import { ToolName } from '../common/toolsServiceTypes.js'; export const EMPTY_MESSAGE = '(empty message)' @@ -455,8 +456,8 @@ const prepareGeminiMessages = (messages: AnthropicLLMChatMessage[]) => { return { text: c.text } } else if (c.type === 'tool_use') { - latestToolName = c.name as ToolName - return { functionCall: { id: c.id, name: c.name as ToolName, args: c.input } } + latestToolName = c.name + return { functionCall: { id: c.id, name: c.name, args: c.input } } } else return null }).filter(m => !!m) diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index 9852d5c9..16c9c2ee 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -13,7 +13,7 @@ import { ChatMarkdownRender, ChatMessageLocation, getApplyBoxId } from '../markd import { URI } from '../../../../../../../base/common/uri.js'; import { IDisposable } from '../../../../../../../base/common/lifecycle.js'; import { ErrorDisplay } from './ErrorDisplay.js'; -import { BlockCode, TextAreaFns, VoidCustomDropdownBox, VoidInputBox2, VoidSlider, VoidSwitch } from '../util/inputs.js'; +import { BlockCode, TextAreaFns, VoidCustomDropdownBox, VoidInputBox2, VoidSlider, VoidSwitch, VoidDiffEditor } from '../util/inputs.js'; import { ModelDropdown, } from '../void-settings-tsx/ModelDropdown.js'; import { PastThreadsList } from './SidebarThreadSelector.js'; import { VOID_CTRL_L_ACTION_ID } from '../../../actionIDs.js'; @@ -24,11 +24,11 @@ import { WarningBox } from '../void-settings-tsx/WarningBox.js'; import { getModelCapabilities, getIsReasoningEnabledState } from '../../../../common/modelCapabilities.js'; import { AlertTriangle, File, Ban, Check, ChevronRight, Dot, FileIcon, Pencil, Undo, Undo2, X, Flag, Copy as CopyIcon, Info, CirclePlus, Ellipsis, CircleEllipsis, Folder, ALargeSmall, TypeOutline, Text } from 'lucide-react'; import { ChatMessage, CheckpointEntry, StagingSelectionItem, ToolMessage } from '../../../../common/chatThreadServiceTypes.js'; -import { approvalTypeOfToolName, BuiltinToolCallParams, LintErrorItem, ToolApprovalType, toolApprovalTypes } from '../../../../common/toolsServiceTypes.js'; +import { approvalTypeOfBuiltinToolName, BuiltinToolCallParams, BuiltinToolName, builtinToolNames, isABuiltinToolName, ToolName, LintErrorItem, ToolApprovalType, toolApprovalTypes } from '../../../../common/toolsServiceTypes.js'; import { CopyButton, EditToolAcceptRejectButtonsHTML, IconShell1, JumpToFileButton, JumpToTerminalButton, StatusIndicator, StatusIndicatorForApplyButton, useApplyStreamState, useEditToolStreamState } from '../markdown/ApplyBlockHoverButtons.js'; import { IsRunningType } from '../../../chatThreadService.js'; import { acceptAllBg, acceptBorder, buttonFontSize, buttonTextColor, rejectAllBg, rejectBg, rejectBorder } from '../../../../common/helpers/colors.js'; -import { MAX_FILE_CHARS_PAGE, MAX_TERMINAL_INACTIVE_TIME, ToolName, toolNames } from '../../../../common/prompt/prompts.js'; +import { MAX_FILE_CHARS_PAGE, MAX_TERMINAL_INACTIVE_TIME } from '../../../../common/prompt/prompts.js'; import { RawToolCallObj } from '../../../../common/sendLLMMessageTypes.js'; import ErrorBoundary from './ErrorBoundary.js'; import { ToolApprovalTypeSwitch } from '../void-settings-tsx/Settings.js'; @@ -36,6 +36,7 @@ import { ToolApprovalTypeSwitch } from '../void-settings-tsx/Settings.js'; import { persistentTerminalNameOfId } from '../../../terminalToolService.js'; + export const IconX = ({ size, className = '', ...props }: { size: number, className?: string } & React.SVGProps) => { return ( voidOpenFileFn(params.uri, accessor) const componentParams: ToolHeaderParams = { title, desc1, desc1OnClick, desc1Info, isError, icon, isRejected, } + + const editToolType = toolMessage.name === 'edit_file' ? 'diff' : 'rewrite' if (toolMessage.type === 'running_now' || toolMessage.type === 'tool_request') { componentParams.children = // JumpToFileButton removed in favor of FileLinkText @@ -936,6 +940,7 @@ const EditTool = ({ toolMessage, threadId, messageIdx, content }: Parameters @@ -1388,7 +1393,7 @@ const loadingTitleWrapper = (item: React.ReactNode): React.ReactNode => { } -const titleOfToolName = { +const titleOfBuiltinToolName = { 'read_file': { done: 'Read file', proposed: 'Read file', running: loadingTitleWrapper('Reading file') }, 'ls_dir': { done: 'Inspected folder', proposed: 'Inspect folder', running: loadingTitleWrapper('Inspecting folder') }, 'get_dir_tree': { done: 'Inspected folder tree', proposed: 'Inspect folder tree', running: loadingTitleWrapper('Inspecting folder tree') }, @@ -1406,21 +1411,21 @@ const titleOfToolName = { 'read_lint_errors': { done: `Read lint errors`, proposed: 'Read lint errors', running: loadingTitleWrapper('Reading lint errors') }, 'search_in_file': { done: 'Searched in file', proposed: 'Search in file', running: loadingTitleWrapper('Searching in file') }, -} as const satisfies Record +} as const satisfies Record const getTitle = (toolMessage: Pick): React.ReactNode => { const t = toolMessage - if (!toolNames.includes(t.name as ToolName)) return t.name // good measure + if (!builtinToolNames.includes(t.name as BuiltinToolName)) return t.name // good measure - const toolName = t.name as ToolName - if (t.type === 'success') return titleOfToolName[toolName].done - if (t.type === 'running_now') return titleOfToolName[toolName].running - return titleOfToolName[toolName].proposed + const toolName = t.name as BuiltinToolName + if (t.type === 'success') return titleOfBuiltinToolName[toolName].done + if (t.type === 'running_now') return titleOfBuiltinToolName[toolName].running + return titleOfBuiltinToolName[toolName].proposed } -const toolNameToDesc = (toolName: ToolName, _toolParams: BuiltinToolCallParams[ToolName] | undefined, accessor: ReturnType): { +const toolNameToDesc = (toolName: BuiltinToolName, _toolParams: BuiltinToolCallParams[BuiltinToolName] | undefined, accessor: ReturnType): { desc1: React.ReactNode, desc1Info?: string, } => { @@ -1590,7 +1595,7 @@ const ToolRequestAcceptRejectButtons = ({ toolName }: { toolName: ToolName }) => ) - const approvalType = approvalTypeOfToolName[toolName] + const approvalType = isABuiltinToolName(toolName) ? approvalTypeOfBuiltinToolName[toolName] : 'mcp-tools' const approvalToggle = approvalType ?
: null @@ -1604,7 +1609,7 @@ const ToolRequestAcceptRejectButtons = ({ toolName }: { toolName: ToolName }) => export const ToolChildrenWrapper = ({ children, className }: { children: React.ReactNode, className?: string }) => { return
-
+
{children}
@@ -1633,12 +1638,18 @@ export const ListableToolItem = ({ name, onClick, isSmall, className, showDot }: -const EditToolChildren = ({ uri, code }: { uri: URI | undefined, code: string }) => { +const EditToolChildren = ({ uri, code, type }: { uri: URI | undefined, code: string, type: 'diff' | 'rewrite' }) => { + + const content = type === 'diff' ? + + : + return
- + {content}
+ } @@ -1819,9 +1830,57 @@ const CommandTool = ({ toolMessage, type, threadId }: { threadId: string } & ({ } +type WrapperProps = { toolMessage: Exclude, { type: 'invalid_params' }>, messageIdx: number, threadId: string } +const MCPToolWrapper = ({ toolMessage }: WrapperProps) => { + const accessor = useAccessor() + const commandService = accessor.get('ICommandService') -type ResultWrapper = (props: { toolMessage: Exclude, { type: 'invalid_params' }>, messageIdx: number, threadId: string }) => React.ReactNode -const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, } } = { + const title = getTitle(toolMessage) + const desc1 = toolMessage.name + const icon = null + + if (toolMessage.type === 'tool_request') return null // do not show past requests + if (toolMessage.type === 'running_now') return null // do not show running + + const isError = false + const isRejected = toolMessage.type === 'rejected' + const { rawParams, params } = toolMessage + const componentParams: ToolHeaderParams = { title, desc1, isError, icon, isRejected, } + + if (toolMessage.type === 'success') { + const { result } = toolMessage + componentParams.children = + + + + + } + else if (toolMessage.type === 'tool_error') { + const { result } = toolMessage + componentParams.bottomChildren = + + {result} + + + } + + return + +} + +type ResultWrapper = (props: WrapperProps) => React.ReactNode + +const builtinToolNameToComponent: { [T in BuiltinToolName]: { resultWrapper: ResultWrapper, } } = { 'read_file': { resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() @@ -2257,12 +2316,12 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, }, 'rewrite_file': { resultWrapper: (params) => { - return + return } }, 'edit_file': { resultWrapper: (params) => { - return + return } }, @@ -2446,7 +2505,11 @@ const _ChatBubble = ({ threadId, chatMessage, currCheckpointIdx, isCommitted, me
} - const ToolResultWrapper = toolNameToComponent[chatMessage.name]?.resultWrapper as ResultWrapper + const toolName = chatMessage.name + const isBuiltInTool = isABuiltinToolName(toolName) + const ToolResultWrapper = isBuiltInTool ? builtinToolNameToComponent[toolName]?.resultWrapper as ResultWrapper + : MCPToolWrapper as ResultWrapper + if (ToolResultWrapper) return <>
@@ -2746,12 +2809,13 @@ const CommandBarInChat = () => { const EditToolSoFar = ({ toolCallSoFar, }: { toolCallSoFar: RawToolCallObj }) => { + if (!isABuiltinToolName( toolCallSoFar.name)) return null const accessor = useAccessor() const uri = toolCallSoFar.rawParams.uri ? URI.file(toolCallSoFar.rawParams.uri) : undefined - const title = titleOfToolName[toolCallSoFar.name].proposed + const title = titleOfBuiltinToolName[toolCallSoFar.name].proposed const uriDone = toolCallSoFar.doneParams.includes('uri') const desc1 = @@ -2772,12 +2836,11 @@ const EditToolSoFar = ({ toolCallSoFar, }: { toolCallSoFar: RawToolCallObj }) => - - } diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx index d2b838e1..a8e411ec 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx @@ -20,6 +20,11 @@ import { URI } from '../../../../../../../base/common/uri.js'; import { getBasename, getFolderName } from '../sidebar-tsx/SidebarChat.js'; import { ChevronRight, File, Folder, FolderClosed, LucideProps } from 'lucide-react'; import { StagingSelectionItem } from '../../../../common/chatThreadServiceTypes.js'; +import { DiffEditorWidget } from '../../../../../../../editor/browser/widget/diffEditor/diffEditorWidget.js'; +import { extractSearchReplaceBlocks } from '../../../../common/helpers/extractCodeFromResult.js'; +import { IAccessibilitySignalService } from '../../../../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; +import { IEditorProgressService } from '../../../../../../../platform/progress/common/progress.js'; +import { detectLanguage } from '../../../../common/helpers/languageHelpers.js'; // type guard @@ -951,11 +956,11 @@ export const VoidInputBox = ({ onChangeText, onCreateInstance, inputBoxRef, plac const contextViewProvider = accessor.get('IContextViewService') return [ container, contextViewProvider, @@ -991,8 +996,7 @@ export const VoidInputBox = ({ onChangeText, onCreateInstance, inputBoxRef, plac inputBoxRef.current = instance; return disposables - }, [onChangeText, onCreateInstance, inputBoxRef]) - } + }, [onChangeText, onCreateInstance, inputBoxRef])} /> }; @@ -1840,4 +1844,93 @@ export const VoidButtonBgDarken = ({ children, disabled, onClick, className }: { // return
; // }; +/** + * ToolDiffEditor mounts a native VSCode DiffEditorWidget to show a diff between original and modified code blocks. + * Props: + * - uri: URI of the file (for language detection, etc) + * - searchReplaceBlocks: string in search/replace format (from LLM) + * - language?: string (optional, fallback to 'plaintext') + */ +export const VoidDiffEditor = ({ uri, searchReplaceBlocks, language }: { uri?: any, searchReplaceBlocks: string, language?: string }) => { + const accessor = useAccessor(); + const modelService = accessor.get('IModelService'); + const instantiationService = accessor.get('IInstantiationService'); + const languageService = accessor.get('ILanguageService'); + const contextKeyService = accessor.get('IContextKeyService'); + const codeEditorService = accessor.get('ICodeEditorService'); + + // Extract the first block (if present) + const blocks = extractSearchReplaceBlocks(searchReplaceBlocks); + const block = blocks[0] || { orig: '', final: '' }; + + // Use detectLanguage for language detection if not provided + let lang = language; + if (!lang) { + lang = detectLanguage(languageService, { uri: uri ?? null, fileContents: block.orig }); + } + + // Use ILanguageSelection for model creation + const languageSelection = useMemo(() => languageService.createById(lang!), [lang, languageService]); + + // Create models for original and modified + const originalModel = useMemo(() => + modelService.createModel(block.orig, languageSelection), + [block.orig, languageSelection, modelService] + ); + const modifiedModel = useMemo(() => + modelService.createModel(block.final, languageSelection), + [block.final, languageSelection, modelService] + ); + + // Clean up models on unmount + useEffect(() => { + return () => { + originalModel.dispose(); + modifiedModel.dispose(); + }; + }, [originalModel, modifiedModel]); + + // Imperatively mount the DiffEditorWidget + const divRef = useRef(null); + const editorRef = useRef(null); + + useEffect(() => { + if (!divRef.current) return; + // Create the diff editor instance + const editor = instantiationService.createInstance( + DiffEditorWidget, + divRef.current, + { + automaticLayout: true, + readOnly: true, + renderSideBySide: true, + minimap: { enabled: false }, + lineNumbers: 'off', + scrollbar: { vertical: 'auto', horizontal: 'auto', verticalScrollbarSize: 8, horizontalScrollbarSize: 8 }, + hover: { enabled: false }, + folding: false, + selectionHighlight: false, + renderLineHighlight: 'none', + overviewRulerLanes: 0, + hideCursorInOverviewRuler: true, + overviewRulerBorder: false, + glyphMargin: false, + stickyScroll: { enabled: false }, + }, + { originalEditor: { isSimpleWidget: true }, modifiedEditor: { isSimpleWidget: true } } + ); + editor.setModel({ original: originalModel, modified: modifiedModel }); + editor.layout(); + editorRef.current = editor; + return () => { + editor.dispose(); + editorRef.current = null; + }; + }, [originalModel, modifiedModel, instantiationService]); + + return ( +
+ ); +}; + diff --git a/src/vs/workbench/contrib/void/browser/toolsService.ts b/src/vs/workbench/contrib/void/browser/toolsService.ts index 1c07ceb8..6d56bd46 100644 --- a/src/vs/workbench/contrib/void/browser/toolsService.ts +++ b/src/vs/workbench/contrib/void/browser/toolsService.ts @@ -8,7 +8,7 @@ import { QueryBuilder } from '../../../services/search/common/queryBuilder.js' import { ISearchService } from '../../../services/search/common/search.js' import { IEditCodeService } from './editCodeServiceInterface.js' import { ITerminalToolService } from './terminalToolService.js' -import { LintErrorItem, BuiltinToolCallParams, BuiltinToolResultType } from '../common/toolsServiceTypes.js' +import { LintErrorItem, BuiltinToolCallParams, BuiltinToolResultType, BuiltinToolName } from '../common/toolsServiceTypes.js' import { IVoidModelService } from '../common/voidModelService.js' import { EndOfLinePreference } from '../../../../editor/common/model.js' import { IVoidCommandBarService } from './voidCommandBarService.js' @@ -16,15 +16,15 @@ import { computeDirectoryTree1Deep, IDirectoryStrService, stringifyDirectoryTree import { IMarkerService, MarkerSeverity } from '../../../../platform/markers/common/markers.js' import { timeout } from '../../../../base/common/async.js' import { RawToolParamsObj } from '../common/sendLLMMessageTypes.js' -import { MAX_CHILDREN_URIs_PAGE, MAX_FILE_CHARS_PAGE, MAX_TERMINAL_BG_COMMAND_TIME, MAX_TERMINAL_INACTIVE_TIME, ToolName } from '../common/prompt/prompts.js' +import { MAX_CHILDREN_URIs_PAGE, MAX_FILE_CHARS_PAGE, MAX_TERMINAL_BG_COMMAND_TIME, MAX_TERMINAL_INACTIVE_TIME } from '../common/prompt/prompts.js' import { IVoidSettingsService } from '../common/voidSettingsService.js' import { generateUuid } from '../../../../base/common/uuid.js' // tool use for AI -type ValidateBuiltinParams = { [T in ToolName]: (p: RawToolParamsObj) => BuiltinToolCallParams[T] } -type CallBuiltinTool = { [T in ToolName]: (p: BuiltinToolCallParams[T]) => Promise<{ result: BuiltinToolResultType[T] | Promise, interruptTool?: () => void }> } -type BuiltinToolResultToString = { [T in ToolName]: (p: BuiltinToolCallParams[T], result: Awaited) => string } +type ValidateBuiltinParams = { [T in BuiltinToolName]: (p: RawToolParamsObj) => BuiltinToolCallParams[T] } +type CallBuiltinTool = { [T in BuiltinToolName]: (p: BuiltinToolCallParams[T]) => Promise<{ result: BuiltinToolResultType[T] | Promise, interruptTool?: () => void }> } +type BuiltinToolResultToString = { [T in BuiltinToolName]: (p: BuiltinToolCallParams[T], result: Awaited) => string } const isFalsy = (u: unknown) => { diff --git a/src/vs/workbench/contrib/void/common/chatThreadServiceTypes.ts b/src/vs/workbench/contrib/void/common/chatThreadServiceTypes.ts index ef2fb127..2c6e7da2 100644 --- a/src/vs/workbench/contrib/void/common/chatThreadServiceTypes.ts +++ b/src/vs/workbench/contrib/void/common/chatThreadServiceTypes.ts @@ -5,9 +5,8 @@ import { URI } from '../../../../base/common/uri.js'; import { VoidFileSnapshot } from './editCodeServiceTypes.js'; -import { ToolName } from './prompt/prompts.js'; import { AnthropicReasoning, RawToolParamsObj } from './sendLLMMessageTypes.js'; -import { BuiltinToolCallParams, BuiltinToolResultType } from './toolsServiceTypes.js'; +import { ToolCallParams, ToolName, ToolResult } from './toolsServiceTypes.js'; export type ToolMessage = { role: 'tool'; @@ -18,13 +17,13 @@ export type ToolMessage = { // in order of events: | { type: 'invalid_params', result: null, name: T, } - | { type: 'tool_request', result: null, name: T, params: BuiltinToolCallParams[T], } // params were validated, awaiting user + | { type: 'tool_request', result: null, name: T, params: ToolCallParams, } // params were validated, awaiting user - | { type: 'running_now', result: null, name: T, params: BuiltinToolCallParams[T], } + | { type: 'running_now', result: null, name: T, params: ToolCallParams, } - | { type: 'tool_error', result: string, name: T, params: BuiltinToolCallParams[T], } // error when tool was running - | { type: 'success', result: Awaited, name: T, params: BuiltinToolCallParams[T], } - | { type: 'rejected', result: null, name: T, params: BuiltinToolCallParams[T] } + | { type: 'tool_error', result: string, name: T, params: ToolCallParams, } // error when tool was running + | { type: 'success', result: Awaited>, name: T, params: ToolCallParams, } + | { type: 'rejected', result: null, name: T, params: ToolCallParams } ) // user rejected export type DecorativeCanceledTool = { diff --git a/src/vs/workbench/contrib/void/common/mcpService.ts b/src/vs/workbench/contrib/void/common/mcpService.ts index 71677a74..ad2215dc 100644 --- a/src/vs/workbench/contrib/void/common/mcpService.ts +++ b/src/vs/workbench/contrib/void/common/mcpService.ts @@ -14,7 +14,7 @@ import { IProductService } from '../../../../platform/product/common/productServ import { VSBuffer } from '../../../../base/common/buffer.js'; import { IChannel } from '../../../../base/parts/ipc/common/ipc.js'; import { IMainProcessService } from '../../../../platform/ipc/common/mainProcessService.js'; -import { MCPServerOfName, MCPConfigFileJSON, MCPServer, MCPToolCallParams, MCPGenericToolResponse, MCPServerEventResponse } from './mcpServiceTypes.js'; +import { MCPServerOfName, MCPConfigFileJSON, MCPServer, MCPToolCallParams, RawMCPToolCall, MCPServerEventResponse } from './mcpServiceTypes.js'; import { Event, Emitter } from '../../../../base/common/event.js'; import { InternalToolInfo } from './prompt/prompts.js'; import { IVoidSettingsService } from './voidSettingsService.js'; @@ -36,9 +36,7 @@ export interface IMCPService { getMCPTools(): Record; - // TOOL_TODO!!!! implement getMCPTools here, which gets merged with builtins in prompts.ts. Should generally be the same shape as voidTools in prompts.ts. - - callMCPTool(toolData: MCPToolCallParams): Promise<{ result: MCPGenericToolResponse }>; + callMCPTool(toolData: MCPToolCallParams): Promise<{ result: RawMCPToolCall }>; // this is outdated: @@ -304,8 +302,8 @@ class MCPService extends Disposable implements IMCPService { } - public async callMCPTool(toolData: MCPToolCallParams): Promise<{ result: MCPGenericToolResponse }> { - const result = await this.channel.call('callTool', toolData); + public async callMCPTool(toolData: MCPToolCallParams): Promise<{ result: RawMCPToolCall }> { + const result = await this.channel.call('callTool', toolData); return { result }; } diff --git a/src/vs/workbench/contrib/void/common/mcpServiceTypes.ts b/src/vs/workbench/contrib/void/common/mcpServiceTypes.ts index 541dc443..170cb843 100644 --- a/src/vs/workbench/contrib/void/common/mcpServiceTypes.ts +++ b/src/vs/workbench/contrib/void/common/mcpServiceTypes.ts @@ -227,7 +227,7 @@ export type MCPToolErrorResponse = MCPToolEventResponse<'error'>; export type MCPToolImageResponse = MCPToolEventResponse<'image'>; export type MCPToolAudioResponse = MCPToolEventResponse<'audio'>; export type MCPToolResourceResponse = MCPToolEventResponse<'resource'>; -export type MCPGenericToolResponse = MCPToolTextResponse | MCPToolErrorResponse | MCPToolImageResponse | MCPToolAudioResponse | MCPToolResourceResponse; +export type RawMCPToolCall = MCPToolTextResponse | MCPToolErrorResponse | MCPToolImageResponse | MCPToolAudioResponse | MCPToolResourceResponse; export interface MCPToolCallParams { serverName: string; diff --git a/src/vs/workbench/contrib/void/common/prompt/prompts.ts b/src/vs/workbench/contrib/void/common/prompt/prompts.ts index fc3fad15..2ddaa369 100644 --- a/src/vs/workbench/contrib/void/common/prompt/prompts.ts +++ b/src/vs/workbench/contrib/void/common/prompt/prompts.ts @@ -9,7 +9,7 @@ import { IDirectoryStrService } from '../directoryStrService.js'; import { StagingSelectionItem } from '../chatThreadServiceTypes.js'; import { os } from '../helpers/systemInfo.js'; import { RawToolParamsObj } from '../sendLLMMessageTypes.js'; -import { approvalTypeOfToolName, BuiltinToolCallParams, BuiltinToolResultType } from '../toolsServiceTypes.js'; +import { approvalTypeOfBuiltinToolName, BuiltinToolCallParams, BuiltinToolName, BuiltinToolResultType, ToolName } from '../toolsServiceTypes.js'; import { ChatMode } from '../voidSettingsTypes.js'; // Triple backtick wrapper used throughout the prompts for code blocks @@ -184,7 +184,7 @@ export type SnakeCaseKeys> = { // export const voidTools = { -export const voidTools +export const builtinTools : { [T in keyof BuiltinToolCallParams]: { name: string; @@ -348,32 +348,21 @@ export const voidTools } satisfies { [T in keyof BuiltinToolResultType]: InternalToolInfo } -export type ToolName = keyof BuiltinToolResultType -export const toolNames = Object.keys(voidTools) as ToolName[] - -type ToolParamNameOfTool = keyof (typeof voidTools)[T]['params'] -export type ToolParamName = { [T in ToolName]: ToolParamNameOfTool }[ToolName] - -const toolNamesSet = new Set(toolNames) - -export const isAToolName = (toolName: string): toolName is ToolName => { - const isAToolName = toolNamesSet.has(toolName) - return isAToolName -} export const availableTools = (chatMode: ChatMode) => { // TOOL_TODO!!!! // merge MCP tools with these builtin tools + // Note: This requires refactoring the messaging architecture to pass MCP tools from the renderer process + // to the electron-main process through the IPC channel. For now, MCP tools are handled separately + // in the chatThreadService where both built-in and MCP tools are called appropriately. - - - const toolNames: ToolName[] | undefined = chatMode === 'normal' ? undefined - : chatMode === 'gather' ? (Object.keys(voidTools) as ToolName[]).filter(toolName => !(toolName in approvalTypeOfToolName)) - : chatMode === 'agent' ? Object.keys(voidTools) as ToolName[] + const toolNames: BuiltinToolName[] | undefined = chatMode === 'normal' ? undefined + : chatMode === 'gather' ? (Object.keys(builtinTools) as BuiltinToolName[]).filter(toolName => !(toolName in approvalTypeOfBuiltinToolName)) + : chatMode === 'agent' ? Object.keys(builtinTools) as BuiltinToolName[] : undefined - const tools: InternalToolInfo[] | undefined = toolNames?.map(toolName => voidTools[toolName]) + const tools: InternalToolInfo[] | undefined = toolNames?.map(toolName => builtinTools[toolName]) return tools } @@ -390,7 +379,7 @@ const toolCallDefinitionsXMLString = (tools: InternalToolInfo[]) => { } export const reParsedToolXMLString = (toolName: ToolName, toolParams: RawToolParamsObj) => { - const params = Object.keys(toolParams).map(paramName => `<${paramName}>${toolParams[paramName as ToolParamName]}`).join('\n') + const params = Object.keys(toolParams).map(paramName => `<${paramName}>${toolParams[paramName]}`).join('\n') return `\ <${toolName}>${!params ? '' : `\n${params}`} ` diff --git a/src/vs/workbench/contrib/void/common/sendLLMMessageTypes.ts b/src/vs/workbench/contrib/void/common/sendLLMMessageTypes.ts index f6e634ee..79140ebb 100644 --- a/src/vs/workbench/contrib/void/common/sendLLMMessageTypes.ts +++ b/src/vs/workbench/contrib/void/common/sendLLMMessageTypes.ts @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { ToolName, ToolParamName } from './prompt/prompts.js' +import { ToolName, ToolParamName } from './toolsServiceTypes.js' import { ChatMode, ModelSelection, ModelSelectionOptions, OverridesOfModel, ProviderName, RefreshableProviderName, SettingsOfProvider } from './voidSettingsTypes.js' @@ -78,12 +78,12 @@ export type LLMFIMMessage = { export type RawToolParamsObj = { - [paramName in ToolParamName]?: string; + [paramName in ToolParamName]?: string; } export type RawToolCallObj = { name: ToolName; rawParams: RawToolParamsObj; - doneParams: ToolParamName[]; + doneParams: ToolParamName[]; id: string; isDone: boolean; }; diff --git a/src/vs/workbench/contrib/void/common/toolsServiceTypes.ts b/src/vs/workbench/contrib/void/common/toolsServiceTypes.ts index 57457366..413baef7 100644 --- a/src/vs/workbench/contrib/void/common/toolsServiceTypes.ts +++ b/src/vs/workbench/contrib/void/common/toolsServiceTypes.ts @@ -1,5 +1,7 @@ import { URI } from '../../../../base/common/uri.js' -import { ToolName } from './prompt/prompts.js'; +import { RawMCPToolCall } from './mcpServiceTypes.js'; +import { builtinTools } from './prompt/prompts.js'; +import { RawToolParamsObj } from './sendLLMMessageTypes.js'; @@ -16,7 +18,7 @@ export type ShallowDirectoryItem = { } -export const approvalTypeOfToolName: Partial<{ [T in ToolName]?: 'edits' | 'terminal' }> = { +export const approvalTypeOfBuiltinToolName: Partial<{ [T in BuiltinToolName]?: 'edits' | 'terminal' | 'mcp-tools' }> = { 'create_file_or_folder': 'edits', 'delete_file_or_folder': 'edits', 'rewrite_file': 'edits', @@ -28,13 +30,16 @@ export const approvalTypeOfToolName: Partial<{ [T in ToolName]?: 'edits' | 'term } +export type ToolApprovalType = NonNullable<(typeof approvalTypeOfBuiltinToolName)[keyof typeof approvalTypeOfBuiltinToolName]>; + + +export const toolApprovalTypes = new Set([ + ...Object.values(approvalTypeOfBuiltinToolName), + 'mcp-tools', +]) + -// {{add: define new type for approval types}} -export type ToolApprovalType = NonNullable<(typeof approvalTypeOfToolName)[keyof typeof approvalTypeOfToolName]>; -export const toolApprovalTypes = new Set( - Object.values(approvalTypeOfToolName).filter((v): v is ToolApprovalType => v !== undefined) -) // PARAMS OF TOOL CALL export type BuiltinToolCallParams = { @@ -78,3 +83,25 @@ export type BuiltinToolResultType = { 'kill_persistent_terminal': {}, } + +export type ToolCallParams = T extends BuiltinToolName ? BuiltinToolCallParams[T] : RawToolParamsObj +export type ToolResult = T extends BuiltinToolName ? BuiltinToolResultType[T] : RawMCPToolCall + + +export type BuiltinToolName = keyof BuiltinToolResultType +export const builtinToolNames = Object.keys(builtinTools) as BuiltinToolName[] + +type BuiltinToolParamNameOfTool = keyof (typeof builtinTools)[T]['params'] +export type BuiltinToolParamName = { [T in BuiltinToolName]: BuiltinToolParamNameOfTool }[BuiltinToolName] + +const toolNamesSet = new Set(builtinToolNames) + +export const isABuiltinToolName = (toolName: string): toolName is BuiltinToolName => { + const isAToolName = toolNamesSet.has(toolName) + return isAToolName +} + + + +export type ToolName = BuiltinToolName | (string & {}) +export type ToolParamName = T extends BuiltinToolName ? BuiltinToolParamNameOfTool : string diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/extractGrammar.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/extractGrammar.ts index e96117b2..03f07bda 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/extractGrammar.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/extractGrammar.ts @@ -5,8 +5,9 @@ import { generateUuid } from '../../../../../base/common/uuid.js' import { endsWithAnyPrefixOf, SurroundingsRemover } from '../../common/helpers/extractCodeFromResult.js' -import { availableTools, InternalToolInfo, ToolName, ToolParamName } from '../../common/prompt/prompts.js' +import { availableTools, InternalToolInfo } from '../../common/prompt/prompts.js' import { OnFinalMessage, OnText, RawToolCallObj, RawToolParamsObj } from '../../common/sendLLMMessageTypes.js' +import { BuiltinToolName, BuiltinToolParamName } from '../../common/toolsServiceTypes.js' import { ChatMode } from '../../common/voidSettingsTypes.js' @@ -164,15 +165,15 @@ const findIndexOfAny = (fullText: string, matches: string[]) => { type ToolOfToolName = { [toolName: string]: InternalToolInfo | undefined } -const parseXMLPrefixToToolCall = (toolName: ToolName, toolId: string, str: string, toolOfToolName: ToolOfToolName): RawToolCallObj => { +const parseXMLPrefixToToolCall = (toolName: BuiltinToolName, toolId: string, str: string, toolOfToolName: ToolOfToolName): RawToolCallObj => { const paramsObj: RawToolParamsObj = {} - const doneParams: ToolParamName[] = [] + const doneParams: BuiltinToolParamName[] = [] let isDone = false const getAnswer = (): RawToolCallObj => { // trim off all whitespace at and before first \n and after last \n for each param for (const p in paramsObj) { - const paramName = p as ToolParamName + const paramName = p as BuiltinToolParamName const orig = paramsObj[paramName] if (orig === undefined) continue paramsObj[paramName] = trimBeforeAndAfterNewLines(orig) @@ -202,16 +203,16 @@ const parseXMLPrefixToToolCall = (toolName: ToolName, toolId: string, str: strin const pm = new SurroundingsRemover(str) - const allowedParams = Object.keys(toolOfToolName[toolName]?.params ?? {}) as ToolParamName[] + const allowedParams = Object.keys(toolOfToolName[toolName]?.params ?? {}) as BuiltinToolParamName[] if (allowedParams.length === 0) return getAnswer() - let latestMatchedOpenParam: null | ToolParamName = null + let latestMatchedOpenParam: null | BuiltinToolParamName = null let n = 0 while (true) { n += 1 if (n > 10) return getAnswer() // just for good measure as this code is early // find the param name opening tag - let matchedOpenParam: null | ToolParamName = null + let matchedOpenParam: null | BuiltinToolParamName = null for (const paramName of allowedParams) { const removed = pm.removeFromStartUntilFullMatch(`<${paramName}>`, true) if (removed) { @@ -278,7 +279,7 @@ export const extractXMLToolsWrapper = ( let trueFullText = '' let latestToolCall: RawToolCallObj | undefined = undefined - let foundOpenTag: { idx: number, toolName: ToolName } | null = null + let foundOpenTag: { idx: number, toolName: BuiltinToolName } | null = null let openToolTagBuffer = '' // the characters we've seen so far that come after a < with no space afterwards, not yet added to fullText let prevFullTextLen = 0 @@ -308,7 +309,7 @@ export const extractXMLToolsWrapper = ( const i = findIndexOfAny(fullText, toolOpenTags) if (i !== null) { const [idx, toolTag] = i - const toolName = toolTag.substring(1, toolTag.length - 1) as ToolName + const toolName = toolTag.substring(1, toolTag.length - 1) as BuiltinToolName // console.log('found ', toolName) foundOpenTag = { idx, toolName } diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts index 9bba271d..37d50c9f 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts @@ -18,8 +18,9 @@ import { AnthropicLLMChatMessage, GeminiLLMChatMessage, LLMChatMessage, LLMFIMMe import { ChatMode, displayInfoOfProviderName, ModelSelectionOptions, OverridesOfModel, ProviderName, SettingsOfProvider } from '../../common/voidSettingsTypes.js'; import { getSendableReasoningInfo, getModelCapabilities, getProviderCapabilities, defaultProviderSettings, getReservedOutputTokenSpace } from '../../common/modelCapabilities.js'; import { extractReasoningWrapper, extractXMLToolsWrapper } from './extractGrammar.js'; -import { availableTools, InternalToolInfo, isAToolName, ToolParamName, voidTools } from '../../common/prompt/prompts.js'; +import { availableTools, InternalToolInfo, builtinTools } from '../../common/prompt/prompts.js'; import { generateUuid } from '../../../../../base/common/uuid.js'; +import { isABuiltinToolName, BuiltinToolParamName } from '../../common/toolsServiceTypes.js'; const getGoogleApiKey = async () => { // module‑level singleton @@ -220,7 +221,7 @@ const openAITools = (chatMode: ChatMode) => { // convert LLM tool call to our tool format const rawToolCallObjOf = (name: string, toolParamsStr: string, id: string): RawToolCallObj | null => { - if (!isAToolName(name)) return null + if (!isABuiltinToolName(name)) return null const rawParams: RawToolParamsObj = {} let input: unknown try { @@ -231,10 +232,10 @@ const rawToolCallObjOf = (name: string, toolParamsStr: string, id: string): RawT } if (input === null) return null if (typeof input !== 'object') return null - for (const paramName in voidTools[name].params) { - rawParams[paramName as ToolParamName] = (input as any)[paramName] + for (const paramName in builtinTools[name].params) { + rawParams[paramName as BuiltinToolParamName] = (input as any)[paramName] } - return { id, name, rawParams, doneParams: Object.keys(rawParams) as ToolParamName[], isDone: true } + return { id, name, rawParams, doneParams: Object.keys(rawParams) as BuiltinToolParamName[], isDone: true } } @@ -337,7 +338,7 @@ const _sendOpenAICompatibleChat = async ({ messages, onText, onFinalMessage, onE onText({ fullText: fullTextSoFar, fullReasoning: fullReasoningSoFar, - toolCall: isAToolName(toolName) ? { name: toolName, rawParams: {}, isDone: false, doneParams: [], id: toolId } : undefined, + toolCall: isABuiltinToolName(toolName) ? { name: toolName, rawParams: {}, isDone: false, doneParams: [], id: toolId } : undefined, }) } @@ -425,14 +426,14 @@ const anthropicTools = (chatMode: ChatMode) => { const anthropicToolToRawToolCallObj = (toolBlock: Anthropic.Messages.ToolUseBlock): RawToolCallObj | null => { const { id, name, input } = toolBlock - if (!isAToolName(name)) return null + if (!isABuiltinToolName(name)) return null const rawParams: RawToolParamsObj = {} if (input === null) return null if (typeof input !== 'object') return null - for (const paramName in voidTools[name].params) { - rawParams[paramName as ToolParamName] = (input as any)[paramName] + for (const paramName in builtinTools[name].params) { + rawParams[paramName as BuiltinToolParamName] = (input as any)[paramName] } - return { id, name, rawParams, doneParams: Object.keys(rawParams) as ToolParamName[], isDone: true } + return { id, name, rawParams, doneParams: Object.keys(rawParams) as BuiltinToolParamName[], isDone: true } } // ------------ ANTHROPIC ------------ @@ -494,7 +495,7 @@ const sendAnthropicChat = async ({ messages, providerName, onText, onFinalMessag onText({ fullText, fullReasoning, - toolCall: isAToolName(fullToolName) ? { name: fullToolName, rawParams: {}, isDone: false, doneParams: [], id: 'dummy' } : undefined, + toolCall: isABuiltinToolName(fullToolName) ? { name: fullToolName, rawParams: {}, isDone: false, doneParams: [], id: 'dummy' } : undefined, }) } // there are no events for tool_use, it comes in at the end @@ -788,7 +789,7 @@ const sendGeminiChat = async ({ onText({ fullText: fullTextSoFar, fullReasoning: fullReasoningSoFar, - toolCall: isAToolName(toolName) ? { name: toolName, rawParams: {}, isDone: false, doneParams: [], id: toolId } : undefined, + toolCall: isABuiltinToolName(toolName) ? { name: toolName, rawParams: {}, isDone: false, doneParams: [], id: toolId } : undefined, }) } diff --git a/src/vs/workbench/contrib/void/electron-main/mcpChannel.ts b/src/vs/workbench/contrib/void/electron-main/mcpChannel.ts index 4ed0d65f..7e727131 100644 --- a/src/vs/workbench/contrib/void/electron-main/mcpChannel.ts +++ b/src/vs/workbench/contrib/void/electron-main/mcpChannel.ts @@ -13,7 +13,7 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'; -import { MCPConfigFileJSON, MCPConfigFileEntryJSON, MCPServer, MCPGenericToolResponse, MCPToolErrorResponse, MCPServerEventResponse, MCPToolCallParams } from '../common/mcpServiceTypes.js'; +import { MCPConfigFileJSON, MCPConfigFileEntryJSON, MCPServer, RawMCPToolCall, MCPToolErrorResponse, MCPServerEventResponse, MCPToolCallParams } from '../common/mcpServiceTypes.js'; import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import { MCPUserStateOfName } from '../common/voidSettingsTypes.js'; @@ -294,7 +294,7 @@ export class MCPChannel implements IServerChannel { // tool call functions - private async _callTool(serverName: string, toolName: string, params: any): Promise { + private async _callTool(serverName: string, toolName: string, params: any): Promise { const server = this.infoOfClientId[serverName] if (!server) throw new Error(`Server ${serverName} not found`) const { _client: client } = server @@ -341,7 +341,7 @@ export class MCPChannel implements IServerChannel { } // tool call error wrapper - private async _safeCallTool(serverName: string, toolName: string, params: any): Promise { + private async _safeCallTool(serverName: string, toolName: string, params: any): Promise { try { const response = await this._callTool(serverName, toolName, params) return response