{
{/* Slice of Void image */}
-
+ {!isLinux && }
diff --git a/src/vs/workbench/contrib/void/browser/terminalToolService.ts b/src/vs/workbench/contrib/void/browser/terminalToolService.ts
index 96e9ec76..cc1b3e21 100644
--- a/src/vs/workbench/contrib/void/browser/terminalToolService.ts
+++ b/src/vs/workbench/contrib/void/browser/terminalToolService.ts
@@ -9,7 +9,7 @@ import { registerSingleton, InstantiationType } from '../../../../platform/insta
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
import { TerminalExitReason, TerminalLocation } from '../../../../platform/terminal/common/terminal.js';
import { ITerminalService, ITerminalInstance } from '../../../../workbench/contrib/terminal/browser/terminal.js';
-import { MAX_TERMINAL_CHARS, MAX_TERMINAL_INACTIVE_TIME } from '../common/prompt/prompts.js';
+import { MAX_TERMINAL_BG_COMMAND_TIME, MAX_TERMINAL_CHARS, MAX_TERMINAL_INACTIVE_TIME } from '../common/prompt/prompts.js';
import { TerminalResolveReason } from '../common/toolsServiceTypes.js';
@@ -39,11 +39,11 @@ function isCommandComplete(output: string) {
}
-const nameOfId = (id: string) => {
+export const terminalNameOfId = (id: string) => {
if (id === '1') return 'Void Agent'
return `Void Agent (${id})`
}
-const idOfName = (name: string) => {
+export const idOfTerminalName = (name: string) => {
if (name === 'Void Agent') return '1'
const match = name.match(/Void Agent \((\d+)\)/)
@@ -66,7 +66,7 @@ export class TerminalToolService extends Disposable implements ITerminalToolServ
const initializeTerminal = (terminal: ITerminalInstance) => {
// when exit, remove
const d = terminal.onExit(() => {
- const terminalId = idOfName(terminal.title)
+ const terminalId = idOfTerminalName(terminal.title)
if (terminalId !== null && (terminalId in this.terminalInstanceOfId)) delete this.terminalInstanceOfId[terminalId]
d.dispose()
})
@@ -75,7 +75,7 @@ export class TerminalToolService extends Disposable implements ITerminalToolServ
// initialize any terminals that are already open
for (const terminal of terminalService.instances) {
- const proposedTerminalId = idOfName(terminal.title)
+ const proposedTerminalId = idOfTerminalName(terminal.title)
if (proposedTerminalId) this.terminalInstanceOfId[proposedTerminalId] = terminal
initializeTerminal(terminal)
@@ -111,7 +111,7 @@ export class TerminalToolService extends Disposable implements ITerminalToolServ
const terminalId = this.getValidNewTerminalId();
const terminal = await this.terminalService.createTerminal({
location: TerminalLocation.Panel,
- config: { name: nameOfId(terminalId), title: nameOfId(terminalId) },
+ config: { name: terminalNameOfId(terminalId), title: terminalNameOfId(terminalId) },
})
@@ -207,26 +207,34 @@ export class TerminalToolService extends Disposable implements ITerminalToolServ
// send the command here
await terminal.sendText(command, true)
- // inactivity-based timeout
- const waitUntilInactive = new Promise
(res => {
- let globalTimeoutId: ReturnType;
- const resetTimer = () => {
- clearTimeout(globalTimeoutId);
- globalTimeoutId = setTimeout(() => {
- if (resolveReason) return
-
+ const waitUntilInterrupt = isBG ?
+ // timeout after X seconds
+ new Promise((res) => {
+ setTimeout(() => {
resolveReason = { type: 'timeout' };
- res();
- }, MAX_TERMINAL_INACTIVE_TIME * 1000);
- };
+ res()
+ }, MAX_TERMINAL_BG_COMMAND_TIME * 1000)
+ })
+ // inactivity-based timeout
+ : new Promise(res => {
+ let globalTimeoutId: ReturnType;
+ const resetTimer = () => {
+ clearTimeout(globalTimeoutId);
+ globalTimeoutId = setTimeout(() => {
+ if (resolveReason) return
- const dTimeout = terminal.onData(() => { resetTimer(); });
- disposables.push(dTimeout, toDisposable(() => clearTimeout(globalTimeoutId)));
- resetTimer();
- });
+ resolveReason = { type: 'timeout' };
+ res();
+ }, MAX_TERMINAL_INACTIVE_TIME * 1000);
+ };
+
+ const dTimeout = terminal.onData(() => { resetTimer(); });
+ disposables.push(dTimeout, toDisposable(() => clearTimeout(globalTimeoutId)));
+ resetTimer();
+ })
// wait for result
- await Promise.any([waitUntilDone, waitUntilInactive,])
+ await Promise.any([waitUntilDone, waitUntilInterrupt])
disposables.forEach(d => d.dispose())
if (!isBG) {
diff --git a/src/vs/workbench/contrib/void/browser/toolsService.ts b/src/vs/workbench/contrib/void/browser/toolsService.ts
index 8c77a001..fa4b6787 100644
--- a/src/vs/workbench/contrib/void/browser/toolsService.ts
+++ b/src/vs/workbench/contrib/void/browser/toolsService.ts
@@ -16,7 +16,7 @@ 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_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, ToolName } from '../common/prompt/prompts.js'
import { IVoidSettingsService } from '../common/voidSettingsService.js'
@@ -93,7 +93,7 @@ const validateRecursiveParamStr = (paramsUnknown: unknown) => {
}
const validateProposedTerminalId = (terminalIdUnknown: unknown) => {
- if (!terminalIdUnknown) return '1'
+ if (!terminalIdUnknown) throw new Error(`A value for terminalID must be specified, but the value was "${terminalIdUnknown}"`)
const terminalId = terminalIdUnknown + ''
return terminalId
}
@@ -259,19 +259,19 @@ export class ToolsService implements IToolsService {
// ---
run_command: (params: RawToolParamsObj) => {
- const { command: commandUnknown, terminal_id: terminalIdUnknown } = params;
+ const { command: commandUnknown, persistent_terminal_id: terminalIdUnknown } = params;
const command = validateStr('command', commandUnknown);
- const proposedTerminalId = terminalIdUnknown ? validateProposedTerminalId(terminalIdUnknown) : null;
- return { command, bgTerminalId: proposedTerminalId };
+ const persistentTerminalId = terminalIdUnknown ? validateProposedTerminalId(terminalIdUnknown) : null;
+ return { command, persistentTerminalId };
},
open_persistent_terminal: (_params: RawToolParamsObj) => {
// No parameters needed; will open a new background terminal
return {};
},
kill_persistent_terminal: (params: RawToolParamsObj) => {
- const { terminal_id: terminalIdUnknown } = params;
- const terminalId = validateProposedTerminalId(terminalIdUnknown);
- return { terminalId };
+ const { persistent_terminal_id: terminalIdUnknown } = params;
+ const persistentTerminalId = validateProposedTerminalId(terminalIdUnknown);
+ return { persistentTerminalId };
},
}
@@ -425,21 +425,20 @@ export class ToolsService implements IToolsService {
return { result: lintErrorsPromise }
},
// ---
- run_command: async ({ command, bgTerminalId }) => {
- const { terminalId, resPromise } = await this.terminalToolService.runCommand(command, bgTerminalId)
+ run_command: async ({ command, persistentTerminalId }) => {
+ const { terminalId, resPromise } = await this.terminalToolService.runCommand(command, persistentTerminalId)
const interruptTool = () => {
this.terminalToolService.killTerminal(terminalId)
}
return { result: resPromise, interruptTool }
},
open_persistent_terminal: async () => {
- // Open a new background terminal without waiting for completion
- const terminalId = await this.terminalToolService.createTerminal()
- return { result: { terminalId } }
+ const persistentTerminalId = await this.terminalToolService.createTerminal()
+ return { result: { persistentTerminalId } }
},
- kill_persistent_terminal: async ({ terminalId }) => {
+ kill_persistent_terminal: async ({ persistentTerminalId }) => {
// Close the background terminal by sending exit
- await this.terminalToolService.killTerminal(terminalId)
+ await this.terminalToolService.killTerminal(persistentTerminalId)
return { result: {} }
},
@@ -517,18 +516,18 @@ export class ToolsService implements IToolsService {
resolveReason,
result: result_,
} = result
- const { bgTerminalId } = params
+ const { persistentTerminalId } = params
// success
if (resolveReason.type === 'done') {
- const desc = bgTerminalId ? ` in terminal ${bgTerminalId}` : ''
+ const desc = persistentTerminalId ? ` in terminal ${persistentTerminalId}` : ''
return `Terminal command executed and finished${desc}. Result (exit code ${resolveReason.exitCode}):\n${result_}`
}
// bg command
- if (bgTerminalId !== null) {
+ if (persistentTerminalId !== null) {
if (resolveReason.type === 'timeout') {
- return `Terminal command is running in the background in terminal ${bgTerminalId}. Here were the outputs after ${MAX_TERMINAL_INACTIVE_TIME} seconds:\n${result_}`
+ return `Terminal command is running in terminal ${persistentTerminalId}. Here are the current outputs (after ${MAX_TERMINAL_BG_COMMAND_TIME} seconds):\n${result_}`
}
}
// normal command
@@ -541,11 +540,11 @@ export class ToolsService implements IToolsService {
throw new Error(`Unexpected internal error: Terminal command did not resolve with a valid reason.`)
},
open_persistent_terminal: (_params, result) => {
- const { terminalId } = result;
- return `Successfully created background terminal with ID ${terminalId}`;
+ const { persistentTerminalId } = result;
+ return `Successfully created persistent terminal. persistentTerminalId="${persistentTerminalId}"`;
},
kill_persistent_terminal: (params, _result) => {
- return `Successfully closed terminal ${params.terminalId}.`;
+ return `Successfully closed terminal "${params.persistentTerminalId}".`;
},
}
diff --git a/src/vs/workbench/contrib/void/common/prompt/prompts.ts b/src/vs/workbench/contrib/void/common/prompt/prompts.ts
index 3820c871..a69e87a1 100644
--- a/src/vs/workbench/contrib/void/common/prompt/prompts.ts
+++ b/src/vs/workbench/contrib/void/common/prompt/prompts.ts
@@ -27,6 +27,7 @@ export const MAX_CHILDREN_URIs_PAGE = 500
// terminal tool info
export const MAX_TERMINAL_CHARS = 100_000
export const MAX_TERMINAL_INACTIVE_TIME = 8 // seconds
+export const MAX_TERMINAL_BG_COMMAND_TIME = 5
// Maximum character limits for prefix and suffix context
@@ -133,12 +134,12 @@ const changesExampleContent = `\
// {{change 3}}
// ... existing code ...`
-// const editToolDescriptionExample = `\
-// ${tripleTick[0]}
-// ${changesExampleContent}
-// ${tripleTick[1]}`
+const editToolDescriptionExample = `\
+${tripleTick[0]}
+${changesExampleContent}
+${tripleTick[1]}`
-const fileNameEditExample = `${tripleTick[0]}typescript
+const chatSuggestionDiffExample = `${tripleTick[0]}typescript
/Users/username/Dekstop/my_project/app.ts
${changesExampleContent}
${tripleTick[1]}`
@@ -180,6 +181,13 @@ const paginationParam = {
// [K in keyof T as SnakeCase>]: T[K]
// };
+const applyToolDescription = (type: 'edit tool' | 'chat suggestion') => `\
+${type === 'edit tool' ? 'A' : 'a'} code diff describing the change to make to the file. \
+Your DIFF is the only context that will be given to another LLM to apply the change, so it must be accurate and complete. \
+Your DIFF MUST be wrapped in triple backticks. \
+NEVER re-write the whole file. Always bias towards writing as little as possible. \
+Use comments like "// ... existing code ..." to condense your writing. \
+Here's an example of a good output:\n${type === 'edit tool' ? editToolDescriptionExample : chatSuggestionDiffExample}`
export const voidTools = {
@@ -313,7 +321,7 @@ export const voidTools = {
description: `Runs a terminal command and waits for the result (times out after ${MAX_TERMINAL_INACTIVE_TIME}s of inactivity). You can use this tool to run any command: sed, grep, etc. Do not edit any files with this tool; use edit_file instead. When working with git and other tools that open an editor (e.g. git diff), you should pipe to cat to get all results and not get stuck in vim.`,
params: {
command: { description: 'The terminal command to run.' },
- bg_terminal_id: { description: 'Optional. This only applies to terminals that have been opened with open_persistent_terminal. Runs the command in the terminal with the specified ID.' },
+ persistent_terminal_id: { description: 'Optional. Runs the command in the persistent terminal that you created with open_persistent_terminal.' },
},
},
@@ -324,8 +332,8 @@ export const voidTools = {
},
kill_persistent_terminal: {
name: 'kill_persistent_terminal',
- description: `Closes a BG terminal with the given ID.`,
- params: { terminal_id: { description: `The terminal ID to interrupt and close.` } }
+ description: `Interrupts and closes a persistent terminal that you opened with open_persistent_terminal.`,
+ params: { persistent_terminal_id: { description: `The ID of the persistent terminal.` } }
}
@@ -421,14 +429,14 @@ Please assist the user with their query.`)
- ${os}
-- Open workspaces:
-${workspaceFolders.join('\n') || 'NO WORKSPACE OPEN'}
+- The user's workspace contains these folders:
+${workspaceFolders.join('\n') || 'NO FOLDERS OPEN'}
- Active file:
${activeURI}
- Open files:
-${openedURIs.join('\n') || 'NO OPENED EDITORS'}${''/* separator */}${mode === 'agent' && persistentTerminalIDs.length !== 0 ? `
+${openedURIs.join('\n') || 'NO OPENED FILES'}${''/* separator */}${mode === 'agent' && persistentTerminalIDs.length !== 0 ? `
- Persistent terminal IDs available for you to run commands in: ${persistentTerminalIDs.join(', ')}` : ''}
`)
@@ -460,7 +468,7 @@ ${directoryStr}
details.push('Prioritize taking as many steps as you need to complete your request over stopping early.')
details.push(`You will OFTEN need to gather context before making a change. Do not immediately make a change unless you have ALL relevant context.`)
details.push(`ALWAYS have maximal certainty in a change BEFORE you make it. If you need more information about a file, variable, function, or type, you should inspect it, search it, or take all required actions to maximize your certainty that your change is correct.`)
- details.push(`NEVER modify a file outside the user's workspace(s) without permission from the user.`)
+ details.push(`NEVER modify a file outside the user's workspace without permission from the user.`)
}
if (mode === 'gather') {
@@ -475,12 +483,8 @@ ${directoryStr}
- The remaining contents of the file should proceed as usual.`)
details.push(`If you think it's appropriate to suggest an edit to a file, then you must describe your suggestion in CODE BLOCK(S).
-- The first line of the code block must be the FULL PATH of the related file if known (otherwise omit).
-- The remaining contents should be \
-a brief code description of the change you want to make, with comments like "// ... existing code ..." to condense your writing. \
-NEVER re-write the whole file. Instead, use comments like "// ... existing code ...". Bias towards writing as little as possible. \
-Here's an example of a good edit suggestion:
-${fileNameEditExample}.`)
+- The first line of the code block must be the FULL PATH of the related file.
+- The remaining contents should be ${applyToolDescription('chat suggestion')}`)
}
details.push(`NEVER write the FULL PATH of a file when speaking with the user. Just write the file name ONLY.`)
diff --git a/src/vs/workbench/contrib/void/common/toolsServiceTypes.ts b/src/vs/workbench/contrib/void/common/toolsServiceTypes.ts
index 0f48992f..c43f157a 100644
--- a/src/vs/workbench/contrib/void/common/toolsServiceTypes.ts
+++ b/src/vs/workbench/contrib/void/common/toolsServiceTypes.ts
@@ -48,9 +48,9 @@ export type ToolCallParams = {
'create_file_or_folder': { uri: URI, isFolder: boolean },
'delete_file_or_folder': { uri: URI, isRecursive: boolean, isFolder: boolean },
// ---
- 'run_command': { command: string; bgTerminalId: string | null },
+ 'run_command': { command: string; persistentTerminalId: string | null },
'open_persistent_terminal': {},
- 'kill_persistent_terminal': { terminalId: string },
+ 'kill_persistent_terminal': { persistentTerminalId: string },
}
// RESULT OF TOOL CALL
@@ -69,7 +69,7 @@ export type ToolResultType = {
'delete_file_or_folder': {},
// ---
'run_command': { result: string; resolveReason: TerminalResolveReason; },
- 'open_persistent_terminal': { terminalId: string },
+ 'open_persistent_terminal': { persistentTerminalId: string },
'kill_persistent_terminal': {},
}