diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 3bcb5d5c..0ca8d299 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -29,6 +29,34 @@ } } }, + { // Void added this + "type": "npm", + "script": "watchreactd", + "label": "React - Build", + "isBackground": true, + "presentation": { + "reveal": "never", + "group": "buildWatchers", + "close": false + }, + "problemMatcher": { + "owner": "typescript", + "applyTo": "closedDocuments", + "fileLocation": [ + "absolute" + ], + "pattern": { + "regexp": "Error: ([^(]+)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\): (.*)$", + "file": 1, + "location": 2, + "message": 3 + }, + "background": { + "beginsPattern": "Starting compilation", + "endsPattern": "Finished compilation" + } + } + }, { "type": "npm", "script": "watch-extensionsd", @@ -61,6 +89,7 @@ "label": "VS Code - Build", "dependsOn": [ "Core - Build", + "React - Build", "Ext - Build" ], "group": { diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index e43b3527..2cf6569c 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -137,6 +137,7 @@ export type IsRunningType = | 'LLM' // the LLM is currently streaming | 'tool' // whether a tool is currently running | 'awaiting_user' // awaiting user call + | 'idle' // nothing is running now, but the chat should still appear like it's going (used in-between calls) | undefined export type ThreadStreamState = { @@ -174,6 +175,12 @@ export type ThreadStreamState = { llmInfo?: undefined; toolInfo?: undefined; interrupt?: undefined; + } | { + isRunning: 'idle'; + error?: undefined; + llmInfo?: undefined; + toolInfo?: undefined; + interrupt?: undefined; } } @@ -625,7 +632,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { const { chatMode } = this._settingsService.state.globalSettings // should not change as we loop even if user changes it, so it goes here // not running at start, clear state - this._setStreamState(threadId, undefined) + this._setStreamState(threadId, { isRunning: 'idle' }) let nMessagesSent = 0 let shouldSendAnotherMessage = true @@ -652,20 +659,20 @@ class ChatThreadService extends Disposable implements IChatThreadService { chatMode }) - let aborted = false - let shouldRetry = true + let shouldRetryLLM = true let nAttempts = 0 - while (shouldRetry) { + while (shouldRetryLLM) { if (this.streamState[threadId]?.isRunning) { // if already streaming, stop console.log('returning...', this.streamState[threadId]) return } - shouldRetry = false + shouldRetryLLM = false let resMessageIsDonePromise: (toolCall?: RawToolCallObj | undefined) => void // resolves when user approves this tool use (or if tool doesn't require approval) const messageIsDonePromise = new Promise((res, rej) => { resMessageIsDonePromise = res }) + let aborted = false const llmCancelToken = this._llmMessageService.sendLLMMessage({ messagesType: 'chatMessages', chatMode, @@ -691,7 +698,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { if (nAttempts < CHAT_RETRIES) { nAttempts += 1 - shouldRetry = true + shouldRetryLLM = true this._setStreamState(threadId, undefined) // clear later so can be interrupted resMessageIsDonePromise() } @@ -707,7 +714,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { onAbort: () => { // stop the loop to free up the promise, but don't modify state (already handled by whatever stopped it) aborted = true - this._setStreamState(threadId, undefined) + this._setStreamState(threadId, { isRunning: 'idle' }) resMessageIsDonePromise() this._metricsService.capture('Agent Loop Done (Aborted)', { nMessagesSent, chatMode }) }, @@ -721,27 +728,31 @@ class ChatThreadService extends Disposable implements IChatThreadService { this._setStreamState(threadId, { isRunning: 'LLM', llmInfo: { displayContentSoFar: '', reasoningSoFar: '', toolCallSoFar: null }, interrupt: Promise.resolve(() => this._llmMessageService.abort(llmCancelToken)) }) const toolCall = await messageIsDonePromise // wait for message to complete - // ending _setStreamStates are all handled above before resMessageIsDonePromise() - if (aborted) { return } - if (shouldRetry) { + if (aborted) { + this._setStreamState(threadId, undefined) + return + } + if (shouldRetryLLM) { + this._setStreamState(threadId, { isRunning: 'idle' }) await timeout(RETRY_DELAY) continue } + this._setStreamState(threadId, { isRunning: 'idle' }) // call tool if there is one - const tool: RawToolCallObj | undefined = toolCall - if (!tool) continue // skip the next part if no tool + if (toolCall) { + const { awaitingUserApproval, interrupted } = await this._runToolCall(threadId, toolCall.name, toolCall.id, { preapproved: false, unvalidatedToolParams: toolCall.rawParams }) - const { awaitingUserApproval, interrupted } = await this._runToolCall(threadId, tool.name, tool.id, { preapproved: false, unvalidatedToolParams: tool.rawParams }) + if (interrupted) { + this._setStreamState(threadId, undefined) + return + } + this._setStreamState(threadId, { isRunning: 'idle' }) - this._setStreamState(threadId, undefined) - - if (aborted) { return } - if (interrupted) { return } - - if (awaitingUserApproval) { isRunningWhenEnd = 'awaiting_user' } - else { shouldSendAnotherMessage = true } + if (awaitingUserApproval) { isRunningWhenEnd = 'awaiting_user' } + else { shouldSendAnotherMessage = true } + } } // end while (attempts) } // end while (send message) diff --git a/src/vs/workbench/contrib/void/browser/toolsService.ts b/src/vs/workbench/contrib/void/browser/toolsService.ts index da5e7005..ffd74a46 100644 --- a/src/vs/workbench/contrib/void/browser/toolsService.ts +++ b/src/vs/workbench/contrib/void/browser/toolsService.ts @@ -504,7 +504,7 @@ export class ToolsService implements IToolsService { } // normal command if (resolveReason.type === 'timeout') { - return `${result_}\nTerminal command ran, but was interrupted by Void after ${MAX_TERMINAL_INACTIVE_TIME}s of inactivity and did not necessarily finish successfully.` + return `${result_}\nTerminal command ran, but was interrupted by Void after ${MAX_TERMINAL_INACTIVE_TIME}s of inactivity and did not necessarily finish successfully. To try with more time, open a persistent terminal and run the command there.` } throw new Error(`Unexpected internal error: Terminal command did not resolve with a valid reason.`) },