diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index f6859f79..014e4dd0 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -420,7 +420,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { } return false } - private _updateLatestToolTo = (threadId: string, tool: ChatMessage & { role: 'tool' }) => { + private _updateLatestTool = (threadId: string, tool: ChatMessage & { role: 'tool' }) => { const swapped = this._swapOutLatestStreamingToolWithResult(threadId, tool) if (swapped) return this._addMessageToThread(threadId, tool) @@ -430,7 +430,6 @@ class ChatThreadService extends Disposable implements IChatThreadService { const thread = this.state.allThreads[threadId] if (!thread) return // should never happen - const lastMsg = thread.messages[thread.messages.length - 1] if (!( lastMsg.role === 'tool' && (lastMsg.type === 'tool_request') @@ -438,15 +437,6 @@ class ChatThreadService extends Disposable implements IChatThreadService { const callThisToolFirst: ToolMessage = lastMsg - this._updateLatestToolTo(threadId, { - role: 'tool', - type: 'running_now', - name: lastMsg.name, - params: lastMsg.params, - content: '(value not received yet...)', // this typically shouldn't ever get read - result: null - }) - this._wrapRunAgentToNotify( this._runChatAgent({ callThisToolFirst, threadId, ...this._currentModelSelectionProps() }) , threadId @@ -467,7 +457,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { const { name } = lastMsg const errorMessage = this.errMsgs.rejected - this._updateLatestToolTo(threadId, { role: 'tool', type: 'rejected', params: params, name: name, content: errorMessage, result: null }) + this._updateLatestTool(threadId, { role: 'tool', type: 'rejected', params: params, name: name, content: errorMessage, result: null }) this._setStreamState(threadId, {}, 'set') } @@ -588,8 +578,6 @@ class ChatThreadService extends Disposable implements IChatThreadService { if (!opts.preapproved) { // skip this if pre-approved // 1. validate tool params try { - console.log('VALIDATING PARAMS!!!', opts.unvalidatedToolParams) - const params = await this._toolsService.validateParams[toolName](opts.unvalidatedToolParams) toolParams = params } catch (error) { @@ -601,8 +589,8 @@ class ChatThreadService extends Disposable implements IChatThreadService { if (toolName === 'edit_file') { this._addToolEditCheckpoint({ threadId, uri: (toolParams as ToolCallParams['edit_file']).uri }) } // 2. if tool requires approval, break from the loop, awaiting approval - const requiresApproval = toolNamesThatRequireApproval.has(toolName) - if (requiresApproval) { + const toolRequiresApproval = toolNamesThatRequireApproval.has(toolName) + if (toolRequiresApproval) { const autoApprove = this._settingsService.state.globalSettings.autoApprove // add a tool_request because we use it for UI if a tool is loading (this should be improved in the future) this._addMessageToThread(threadId, { role: 'tool', type: 'tool_request', content: '(never)', result: null, name: toolName, params: toolParams }) @@ -617,6 +605,8 @@ class ChatThreadService extends Disposable implements IChatThreadService { // 3. call the tool this._setStreamState(threadId, { isRunning: 'tool' }, 'merge') + this._updateLatestTool(threadId, { role: 'tool', type: 'running_now', name: toolName, params: toolParams, content: '(value not received yet...)', result: null }) + let interrupted = false try { const { result, interruptTool } = await this._toolsService.callTool[toolName](toolParams as any) @@ -633,7 +623,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { return { interrupted: true } } const errorMessage = getErrorMessage(error) - this._updateLatestToolTo(threadId, { role: 'tool', type: 'tool_error', params: toolParams, result: errorMessage, name: toolName, content: errorMessage, }) + this._updateLatestTool(threadId, { role: 'tool', type: 'tool_error', params: toolParams, result: errorMessage, name: toolName, content: errorMessage, }) return {} } @@ -642,12 +632,12 @@ class ChatThreadService extends Disposable implements IChatThreadService { toolResultStr = this._toolsService.stringOfResult[toolName](toolParams as any, toolResult as any) } catch (error) { const errorMessage = this.errMsgs.errWhenStringifying(error) - this._updateLatestToolTo(threadId, { role: 'tool', type: 'tool_error', params: toolParams, result: errorMessage, name: toolName, content: errorMessage, }) + this._updateLatestTool(threadId, { role: 'tool', type: 'tool_error', params: toolParams, result: errorMessage, name: toolName, content: errorMessage, }) return {} } // 5. add to history and keep going - this._updateLatestToolTo(threadId, { role: 'tool', type: 'success', params: toolParams, result: toolResult, name: toolName, content: toolResultStr, }) + this._updateLatestTool(threadId, { role: 'tool', type: 'success', params: toolParams, result: toolResult, name: toolName, content: toolResultStr, }) return {} }; @@ -704,7 +694,6 @@ class ChatThreadService extends Disposable implements IChatThreadService { ...llmMessages ] - console.log('SENDING!!', messages) const llmCancelToken = this._llmMessageService.sendLLMMessage({ messagesType: 'chatMessages', chatMode, @@ -718,7 +707,6 @@ class ChatThreadService extends Disposable implements IChatThreadService { onFinalMessage: async ({ fullText, fullReasoning, toolCall, anthropicReasoning, }) => { this._addMessageToThread(threadId, { role: 'assistant', displayContent: fullText, reasoning: fullReasoning, toolCall, anthropicReasoning }) this._setStreamState(threadId, { displayContentSoFar: undefined, reasoningSoFar: undefined, streamingToken: undefined, toolCallSoFar: undefined }, 'merge') - console.log('tool call!!', JSON.stringify(toolCall)) resMessageIsDonePromise(toolCall) // resolve with tool calls }, onError: (error) => { diff --git a/src/vs/workbench/contrib/void/browser/directoryStrService.ts b/src/vs/workbench/contrib/void/browser/directoryStrService.ts index f51298f2..bbfa4b8e 100644 --- a/src/vs/workbench/contrib/void/browser/directoryStrService.ts +++ b/src/vs/workbench/contrib/void/browser/directoryStrService.ts @@ -303,7 +303,6 @@ class DirectoryStrService extends Disposable implements IDirectoryStrService { // Use our new approach with direct explorer service const dirTree = await computeDirectoryTree(eRoot, this.explorerService); - console.log('dirtree', dirTree) const { content, wasCutOff } = stringifyDirectoryTree(dirTree, MAX_DIRSTR_CHARS_TOTAL_BEGINNING - str.length); str += content; if (wasCutOff) { diff --git a/src/vs/workbench/contrib/void/browser/editCodeService.ts b/src/vs/workbench/contrib/void/browser/editCodeService.ts index 7806cfca..10effb06 100644 --- a/src/vs/workbench/contrib/void/browser/editCodeService.ts +++ b/src/vs/workbench/contrib/void/browser/editCodeService.ts @@ -44,7 +44,6 @@ import { IEditCodeService, AddCtrlKOpts, StartApplyingOpts, CallBeforeStartApply import { IVoidSettingsService } from '../common/voidSettingsService.js'; import { FeatureName } from '../common/voidSettingsTypes.js'; import { IVoidModelService } from '../common/voidModelService.js'; -import { ITextFileService } from '../../../services/textfile/common/textfiles.js'; import { deepClone } from '../../../../base/common/objects.js'; import { acceptBg, acceptBorder, buttonFontSize, buttonTextColor, rejectBg, rejectBorder } from '../common/helpers/colors.js'; import { DiffArea, Diff, CtrlKZone, VoidFileSnapshot, DiffAreaSnapshotEntry, diffAreaSnapshotKeys, DiffZone, TrackingZone, ComputedDiff } from '../common/editCodeServiceTypes.js'; @@ -190,7 +189,6 @@ class EditCodeService extends Disposable implements IEditCodeService { @IVoidSettingsService private readonly _settingsService: IVoidSettingsService, // @IFileService private readonly _fileService: IFileService, @IVoidModelService private readonly _voidModelService: IVoidModelService, - @ITextFileService private readonly _textFileService: ITextFileService, ) { super(); @@ -720,16 +718,14 @@ class EditCodeService extends Disposable implements IEditCodeService { resource: uri, label: 'Void Agent', code: 'undoredo.editCode', - undo: () => { opts?.onWillUndo?.(); this._restoreVoidFileSnapshot(uri, beforeSnapshot); }, - redo: () => { if (afterSnapshot) this._restoreVoidFileSnapshot(uri, afterSnapshot) } + undo: async () => { opts?.onWillUndo?.(); await this._restoreVoidFileSnapshot(uri, beforeSnapshot) }, + redo: async () => { if (afterSnapshot) await this._restoreVoidFileSnapshot(uri, afterSnapshot) } } this._undoRedoService.pushElement(elt) const onFinishEdit = async () => { afterSnapshot = this._getCurrentVoidFileSnapshot(uri) - await this._textFileService.save(uri, { // we want [our change] -> [save] so it's all treated as one change. - skipSaveParticipants: true // avoid triggering extensions etc (if they reformat the page, it will add another item to the undo stack) - }) + await this._voidModelService.saveModel(uri) } return { onFinishEdit } } @@ -1105,6 +1101,7 @@ class EditCodeService extends Disposable implements IEditCodeService { const uri = this._getURIBeforeStartApplying(opts) if (!uri) return await this._voidModelService.initializeModel(uri) + await this._voidModelService.saveModel(uri) // save the URI } @@ -1878,6 +1875,8 @@ class EditCodeService extends Disposable implements IEditCodeService { interruptURIStreaming({ uri }: { uri: URI }) { + if (!this._uriIsStreaming(uri)) return + this._undoHistory(uri) // brute force for now is OK for (const diffareaid of this.diffAreasOfURI[uri.fsPath] || []) { const diffArea = this.diffAreaOfId[diffareaid] @@ -1885,7 +1884,6 @@ class EditCodeService extends Disposable implements IEditCodeService { if (!diffArea._streamState.isStreaming) continue this._stopIfStreaming(diffArea) } - this._undoHistory(uri) } diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx index c2ef204e..72f963da 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx @@ -216,7 +216,10 @@ export const ApplyButtonsHTML = ({ codeStr, applyBoxId, reapplyIcon, uri }: { co const [newApplyingUri, applyDonePromise] = editCodeService.startApplying(opts) ?? [] // catch any errors by interrupting the stream - applyDonePromise?.catch(e => { if (newApplyingUri) editCodeService.interruptURIStreaming({ uri: newApplyingUri }) }) + applyDonePromise?.catch(e => { + const uri = getUriBeingApplied(applyBoxId) + if (uri) editCodeService.interruptURIStreaming({ uri: uri }) + }) applyingURIOfApplyBoxIdRef.current[applyBoxId] = newApplyingUri ?? undefined 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 1a26e5c1..4eaed58c 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 @@ -773,7 +773,7 @@ const SimplifiedToolHeader = ({ -const UserMessageComponent = ({ chatMessage, messageIdx, isCheckpointGhost, _scrollToBottom }: { chatMessage: ChatMessage & { role: 'user' }, messageIdx: number, isCheckpointGhost: boolean, _scrollToBottom: (() => void) | null }) => { +const UserMessageComponent = ({ chatMessage, messageIdx, isCheckpointGhost, currCheckpointIdx, _scrollToBottom }: { chatMessage: ChatMessage & { role: 'user' }, messageIdx: number, currCheckpointIdx: number | undefined, isCheckpointGhost: boolean, _scrollToBottom: (() => void) | null }) => { const accessor = useAccessor() const chatThreadsService = accessor.get('IChatThreadService') @@ -924,7 +924,7 @@ const UserMessageComponent = ({ chatMessage, messageIdx, isCheckpointGhost, _scr } - + const isMsgAfterCheckpoint = currCheckpointIdx !== undefined && currCheckpointIdx === messageIdx - 1 return
setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} @@ -1850,19 +1850,20 @@ type ChatBubbleProps = { isCommitted: boolean, chatIsRunning: IsRunningType, threadId: string, - currCheckpointIdx: number, + currCheckpointIdx: number | undefined, _scrollToBottom: (() => void) | null, } const ChatBubble = ({ threadId, chatMessage, currCheckpointIdx, isCommitted, messageIdx, chatIsRunning, _scrollToBottom }: ChatBubbleProps) => { const role = chatMessage.role - const isCheckpointGhost = messageIdx > currCheckpointIdx && !chatIsRunning // whether to show as gray (if chat is running, for good measure just dont show any ghosts) + const isCheckpointGhost = messageIdx > (currCheckpointIdx ?? Infinity) && !chatIsRunning // whether to show as gray (if chat is running, for good measure just dont show any ghosts) if (role === 'user') { return @@ -2015,7 +2016,8 @@ export const SidebarChat = () => { const toolCallSoFar = currThreadStreamState?.toolCallSoFar const reasoningSoFar = currThreadStreamState?.reasoningSoFar - const toolIsGenerating = !!toolCallSoFar && toolCallSoFar.name === 'edit_file' // show loading for slow tools (right now just edit) + // this is just if it's currently being generated, NOT if it's currently running + const toolIsGenerating = toolCallSoFar && !toolCallSoFar.isDone && toolCallSoFar.name === 'edit_file' // show loading for slow tools (right now just edit) // ----- SIDEBAR CHAT state (local) ----- @@ -2067,11 +2069,10 @@ export const SidebarChat = () => { const threadId = currentThread.id - const currCheckpointIdx = chatThreadsState.allThreads[threadId]?.state?.currCheckpointIdx ?? Infinity // if not exist, treat like checkpoint is last message (infinity) + const currCheckpointIdx = chatThreadsState.allThreads[threadId]?.state?.currCheckpointIdx ?? undefined // if not exist, treat like checkpoint is last message (infinity) const previousMessagesHTML = useMemo(() => { const lastMessageIdx = previousMessages.findLastIndex(v => v.role !== 'checkpoint') - // tool request shows up as Editing... if in progress return previousMessages.map((message, i) => { return { _scrollToBottom={() => scrollToBottom(scrollContainerRef)} /> }) - }, [previousMessages, isRunning, threadId]) + }, [previousMessages, threadId, currCheckpointIdx, isRunning]) const streamingChatIdx = previousMessagesHTML.length const currStreamingMessageHTML = reasoningSoFar || displayContentSoFar || isRunning ? { /> : null - const generatingToolTitle = toolCallSoFar && toolNames.includes(toolCallSoFar.name as ToolName) ? titleOfToolName[toolCallSoFar.name as ToolName]?.proposed : toolCallSoFar?.name - const messagesHTML = { > {/* previous messages */} {previousMessagesHTML} - - {currStreamingMessageHTML} - {toolIsGenerating ? - Generating} /> + Generating} + /> : null} {isRunning === 'LLM' && !toolIsGenerating ? diff --git a/src/vs/workbench/contrib/void/common/prompt/prompts.ts b/src/vs/workbench/contrib/void/common/prompt/prompts.ts index 252e8b2d..ded53adc 100644 --- a/src/vs/workbench/contrib/void/common/prompt/prompts.ts +++ b/src/vs/workbench/contrib/void/common/prompt/prompts.ts @@ -339,11 +339,11 @@ ${details.map((d, i) => `${i + 1}. ${d}`).join('\n\n')}`) } -// log all prompts -for (const chatMode of ['agent', 'gather', 'normal'] satisfies ChatMode[]) { - console.log(`========================================= SYSTEM MESSAGE FOR ${chatMode} ===================================\n`, - chat_systemMessage({ chatMode, workspaceFolders: [], openedURIs: [], activeURI: 'pee', runningTerminalIds: [], directoryStr: 'lol', })) -} +// // log all prompts +// for (const chatMode of ['agent', 'gather', 'normal'] satisfies ChatMode[]) { +// console.log(`========================================= SYSTEM MESSAGE FOR ${chatMode} ===================================\n`, +// chat_systemMessage({ chatMode, workspaceFolders: [], openedURIs: [], activeURI: 'pee', runningTerminalIds: [], directoryStr: 'lol', })) +// } export const chat_userMessageContent = async (instructions: string, currSelns: StagingSelectionItem[] | null,