mirror of
https://github.com/voideditor/void
synced 2026-05-23 17:38:23 +00:00
fix persistent terminal ID
This commit is contained in:
parent
1a45baa23e
commit
71fc2895a8
4 changed files with 59 additions and 55 deletions
|
|
@ -30,6 +30,7 @@ import { MAX_FILE_CHARS_PAGE, MAX_TERMINAL_INACTIVE_TIME, ToolName, toolNames }
|
|||
import { RawToolCallObj } from '../../../../common/sendLLMMessageTypes.js';
|
||||
import ErrorBoundary from './ErrorBoundary.js';
|
||||
import { ToolApprovalTypeSwitch } from '../void-settings-tsx/Settings.js';
|
||||
import { terminalNameOfId } from '../../../terminalToolService.js';
|
||||
|
||||
|
||||
|
||||
|
|
@ -683,6 +684,7 @@ type ToolHeaderParams = {
|
|||
children?: React.ReactNode;
|
||||
bottomChildren?: React.ReactNode;
|
||||
onClick?: () => void;
|
||||
desc2OnClick?: () => void;
|
||||
isOpen?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
|
@ -700,6 +702,7 @@ const ToolHeaderWrapper = ({
|
|||
bottomChildren,
|
||||
isError,
|
||||
onClick,
|
||||
desc2OnClick,
|
||||
isOpen,
|
||||
isRejected,
|
||||
className, // applies to the main content
|
||||
|
|
@ -769,7 +772,7 @@ const ToolHeaderWrapper = ({
|
|||
data-tooltip-content={'Canceled'}
|
||||
data-tooltip-place='top'
|
||||
/>}
|
||||
{desc2 && <span className="text-void-fg-4 text-xs">
|
||||
{desc2 && <span className="text-void-fg-4 text-xs" onClick={desc2OnClick}>
|
||||
{desc2}
|
||||
</span>}
|
||||
{numResults !== undefined && (
|
||||
|
|
@ -1317,7 +1320,7 @@ const toolNameToDesc = (toolName: ToolName, _toolParams: ToolCallParams[ToolName
|
|||
const toolParams = _toolParams as ToolCallParams['run_command']
|
||||
return {
|
||||
desc1: `"${toolParams.command}"`,
|
||||
desc1Info: toolParams.bgTerminalId
|
||||
desc1Info: toolParams.persistentTerminalId
|
||||
}
|
||||
},
|
||||
'open_persistent_terminal': () => {
|
||||
|
|
@ -1326,7 +1329,7 @@ const toolNameToDesc = (toolName: ToolName, _toolParams: ToolCallParams[ToolName
|
|||
},
|
||||
'kill_persistent_terminal': () => {
|
||||
const toolParams = _toolParams as ToolCallParams['kill_persistent_terminal']
|
||||
return { desc1: toolParams.terminalId }
|
||||
return { desc1: toolParams.persistentTerminalId }
|
||||
},
|
||||
'get_dir_tree': () => {
|
||||
const toolParams = _toolParams as ToolCallParams['get_dir_tree']
|
||||
|
|
@ -2053,6 +2056,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
|
|||
const accessor = useAccessor()
|
||||
const commandService = accessor.get('ICommandService')
|
||||
const terminalToolsService = accessor.get('ITerminalToolService')
|
||||
const toolsService = accessor.get('IToolsService')
|
||||
const isError = toolMessage.type === 'tool_error'
|
||||
const title = getTitle(toolMessage)
|
||||
const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor)
|
||||
|
|
@ -2062,19 +2066,21 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
|
|||
const { rawParams, params } = toolMessage
|
||||
const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, }
|
||||
|
||||
const { command, persistentTerminalId } = params
|
||||
if (persistentTerminalId) {
|
||||
componentParams.desc2 = terminalNameOfId(persistentTerminalId)
|
||||
componentParams.desc2OnClick = () => terminalToolsService.focusTerminal(persistentTerminalId)
|
||||
}
|
||||
|
||||
|
||||
if (toolMessage.type === 'success') {
|
||||
const { result } = toolMessage
|
||||
const { command } = params
|
||||
const { resolveReason, result: terminalResult } = result
|
||||
|
||||
// it's unclear that this is a button and not an icon.
|
||||
// componentParams.desc2 = <JumpToTerminalButton
|
||||
// onClick={() => { terminalToolsService.openTerminal(terminalId) }}
|
||||
// />
|
||||
|
||||
const additionalDetailsStr = resolveReason.type === 'done' ? (resolveReason.exitCode !== 0 ? `\nError: exit code ${resolveReason.exitCode}` : null)
|
||||
: resolveReason.type === 'timeout' ? `\n(timed out)`
|
||||
: null
|
||||
const msg = toolsService.stringOfResult['run_command'](params, result)
|
||||
|
||||
componentParams.children = <ToolChildrenWrapper className='whitespace-pre text-nowrap overflow-auto text-sm'>
|
||||
<div className='!select-text cursor-auto'>
|
||||
|
|
@ -2082,25 +2088,14 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
|
|||
<span className="text-void-fg-1 font-sans">{`Ran command: `}</span>
|
||||
<span className="font-mono">{command}</span>
|
||||
</div>
|
||||
{(terminalResult + additionalDetailsStr).length && <div>
|
||||
{msg.length && <div>
|
||||
<span className='text-void-fg-1'>{`Result: `}</span>
|
||||
<span className="font-mono">{terminalResult}</span>
|
||||
<span className="font-mono">{additionalDetailsStr}</span>
|
||||
<span className="font-mono">{msg}</span>
|
||||
</div>}
|
||||
</div>
|
||||
</ToolChildrenWrapper>
|
||||
|
||||
if (params.bgTerminalId)
|
||||
componentParams.desc2 = `(terminal ${params.bgTerminalId})`
|
||||
|
||||
}
|
||||
else if (toolMessage.type === 'rejected' || toolMessage.type === 'tool_error' || toolMessage.type === 'running_now' || toolMessage.type === 'tool_request') {
|
||||
const { bgTerminalId, command } = params
|
||||
if (bgTerminalId) {
|
||||
componentParams.desc2 = '(persistent terminal)'
|
||||
if (terminalToolsService.terminalExists(bgTerminalId))
|
||||
componentParams.onClick = () => terminalToolsService.focusTerminal(bgTerminalId)
|
||||
}
|
||||
if (toolMessage.type === 'tool_error') {
|
||||
const { result } = toolMessage
|
||||
componentParams.children = <ToolChildrenWrapper>{result}</ToolChildrenWrapper>
|
||||
|
|
@ -2130,12 +2125,9 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
|
|||
|
||||
if (toolMessage.type === 'success') {
|
||||
const { result } = toolMessage
|
||||
const { terminalId } = result
|
||||
if (terminalId) {
|
||||
componentParams.desc2 = `(terminal ${terminalId})`
|
||||
if (terminalToolsService.terminalExists(terminalId))
|
||||
componentParams.onClick = () => terminalToolsService.focusTerminal(terminalId)
|
||||
}
|
||||
const { persistentTerminalId } = result
|
||||
componentParams.desc1 = terminalNameOfId(persistentTerminalId)
|
||||
componentParams.onClick = () => terminalToolsService.focusTerminal(persistentTerminalId)
|
||||
}
|
||||
else if (toolMessage.type === 'tool_error') {
|
||||
const { result } = toolMessage
|
||||
|
|
@ -2153,6 +2145,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
|
|||
resultWrapper: ({ toolMessage }) => {
|
||||
const accessor = useAccessor()
|
||||
const commandService = accessor.get('ICommandService')
|
||||
const terminalToolsService = accessor.get('ITerminalToolService')
|
||||
|
||||
const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor)
|
||||
const title = getTitle(toolMessage)
|
||||
|
|
@ -2167,7 +2160,9 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
|
|||
const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, }
|
||||
|
||||
if (toolMessage.type === 'success') {
|
||||
const { result } = toolMessage
|
||||
const { persistentTerminalId } = params
|
||||
componentParams.desc1 = terminalNameOfId(persistentTerminalId)
|
||||
componentParams.onClick = () => terminalToolsService.focusTerminal(persistentTerminalId)
|
||||
}
|
||||
else if (toolMessage.type === 'tool_error') {
|
||||
const { result } = toolMessage
|
||||
|
|
|
|||
|
|
@ -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<void>(res => {
|
||||
let globalTimeoutId: ReturnType<typeof setTimeout>;
|
||||
const resetTimer = () => {
|
||||
clearTimeout(globalTimeoutId);
|
||||
globalTimeoutId = setTimeout(() => {
|
||||
if (resolveReason) return
|
||||
|
||||
const waitUntilInterrupt = isBG ?
|
||||
// timeout after X seconds
|
||||
new Promise<void>((res) => {
|
||||
setTimeout(() => {
|
||||
resolveReason = { type: 'timeout' };
|
||||
res();
|
||||
}, MAX_TERMINAL_INACTIVE_TIME * 1000);
|
||||
};
|
||||
res()
|
||||
}, MAX_TERMINAL_BG_COMMAND_TIME * 1000)
|
||||
})
|
||||
// inactivity-based timeout
|
||||
: new Promise<void>(res => {
|
||||
let globalTimeoutId: ReturnType<typeof setTimeout>;
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,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'
|
||||
|
||||
|
||||
|
|
@ -507,7 +507,7 @@ export class ToolsService implements IToolsService {
|
|||
// bg command
|
||||
if (persistentTerminalId !== null) {
|
||||
if (resolveReason.type === 'timeout') {
|
||||
return `Terminal command is running in the background in terminal ${persistentTerminalId}. 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
|
||||
|
|
@ -521,10 +521,10 @@ export class ToolsService implements IToolsService {
|
|||
},
|
||||
open_persistent_terminal: (_params, result) => {
|
||||
const { persistentTerminalId } = result;
|
||||
return `Successfully created background terminal with ID ${persistentTerminalId}`;
|
||||
return `Successfully created persistent terminal. persistentTerminalId="${persistentTerminalId}"`;
|
||||
},
|
||||
kill_persistent_terminal: (params, _result) => {
|
||||
return `Successfully closed terminal ${params.persistentTerminalId}.`;
|
||||
return `Successfully closed terminal "${params.persistentTerminalId}".`;
|
||||
},
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue