mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
terminal progress + misc
This commit is contained in:
parent
ab81479340
commit
d6d5f77183
8 changed files with 163 additions and 73 deletions
|
|
@ -446,6 +446,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
// if (Math.random() > 0) throw new Error('TESTING')
|
||||
const errorMessage = 'Tool call was rejected by the user.'
|
||||
this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', params: toolParams, value: errorMessage }, })
|
||||
shouldSendAnotherMessage = false // interrupt flow by rejecting
|
||||
res_()
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,10 +50,10 @@ const configOfBG = (color: Color) => {
|
|||
return { dark: color, light: color, hcDark: color, hcLight: color, }
|
||||
}
|
||||
// gets converted to --vscode-void-greenBG, see void.css, asCssVariable
|
||||
const greenBG = new Color(new RGBA(155, 185, 85, .3)); // default is RGBA(155, 185, 85, .2)
|
||||
const greenBG = new Color(new RGBA(155, 185, 85, .1)); // default is RGBA(155, 185, 85, .2)
|
||||
registerColor('void.greenBG', configOfBG(greenBG), '', true);
|
||||
|
||||
const redBG = new Color(new RGBA(255, 0, 0, .3)); // default is RGBA(255, 0, 0, .2)
|
||||
const redBG = new Color(new RGBA(255, 0, 0, .2)); // default is RGBA(255, 0, 0, .2)
|
||||
registerColor('void.redBG', configOfBG(redBG), '', true);
|
||||
|
||||
const sweepBG = new Color(new RGBA(100, 100, 100, .2));
|
||||
|
|
@ -1481,7 +1481,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
onUndo: () => { if (diffZone._streamState.isStreaming) rejApplyDonePromise(new Error('Edit was interrupted by pressing undo.')) }
|
||||
})
|
||||
|
||||
// TODO replace these with whatever block we're on initially if already started (caching apply)
|
||||
// TODO replace these with whatever block we're on initially if already started (if add caching of apply S/R blocks)
|
||||
|
||||
type SearchReplaceDiffAreaMetadata = {
|
||||
originalBounds: [number, number], // 1-indexed
|
||||
|
|
@ -1607,6 +1607,12 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
shouldUpdateOrigStreamStyle = false
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO!!! test this
|
||||
// starting line is at least the number of lines in the generated code minus 1
|
||||
const numLinesInOrig = block.orig.split('\n').length - 1
|
||||
diffZone._streamState.line = Math.max(numLinesInOrig - 1, 1, diffZone._streamState.line ?? 1)
|
||||
}
|
||||
// must be done writing original to move on to writing streamed content
|
||||
continue
|
||||
}
|
||||
|
|
@ -1615,37 +1621,22 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
|
||||
// if this is the first time we're seeing this block, add it as a diffarea so we can start streaming
|
||||
if (!(blockNum in addedTrackingZoneOfBlockNum)) {
|
||||
console.log('finding text in code...', { orig: block.orig })
|
||||
const originalBounds = findTextInCode(block.orig, originalFileCode)
|
||||
|
||||
// if error
|
||||
if (typeof originalBounds === 'string') {
|
||||
console.log('TEXT NOT FOUND')
|
||||
const content = errMsgOfInvalidStr(originalBounds, block.orig)
|
||||
messages.push(
|
||||
{ role: 'assistant', content: fullText, anthropicReasoning: null }, // latest output
|
||||
{ role: 'user', content: content } // user explanation of what's wrong
|
||||
)
|
||||
|
||||
|
||||
// REVERT
|
||||
// TODO!!!!! don't actually revert - we want to change this so it doesn't revert but isntead gives the current file contents
|
||||
const numLines = this._getNumLines(uri)
|
||||
if (numLines !== null) this._writeText(uri, originalFileCode,
|
||||
{ startLineNumber: 1, startColumn: 1, endLineNumber: numLines, endColumn: Number.MAX_SAFE_INTEGER },
|
||||
{ shouldRealignDiffAreas: false }
|
||||
)
|
||||
// reset state
|
||||
diffZone.startLine = 1
|
||||
diffZone.endLine = numLines ?? 1
|
||||
if (diffZone._streamState.isStreaming) {
|
||||
diffZone._streamState.line = 1
|
||||
}
|
||||
|
||||
currStreamingBlockNum = 0
|
||||
// REVERT THIS ONE BLOCK
|
||||
// TODO!!! test this
|
||||
latestStreamLocationMutable = null
|
||||
shouldUpdateOrigStreamStyle = true
|
||||
oldBlocks = []
|
||||
addedTrackingZoneOfBlockNum.splice(0, Infinity) // clear the array
|
||||
blocks.splice(blockNum, Infinity) // remove all blocks at and after this one
|
||||
oldBlocks = blocks
|
||||
|
||||
// abort and resolve
|
||||
shouldSendAnotherMessage = true
|
||||
|
|
|
|||
|
|
@ -914,7 +914,7 @@ const UserMessageComponent = ({ chatMessage, messageIdx, isLoading }: ChatBubble
|
|||
}
|
||||
|
||||
|
||||
const AssistantMessageComponent = ({ chatMessage, isLoading, messageIdx }: ChatBubbleProps & { chatMessage: ChatMessage & { role: 'assistant' } }) => {
|
||||
const AssistantMessageComponent = ({ chatMessage, isLoading, messageIdx, isLast }: ChatBubbleProps & { chatMessage: ChatMessage & { role: 'assistant' } }) => {
|
||||
|
||||
const accessor = useAccessor()
|
||||
const chatThreadsService = accessor.get('IChatThreadService')
|
||||
|
|
@ -930,8 +930,9 @@ const AssistantMessageComponent = ({ chatMessage, isLoading, messageIdx }: ChatB
|
|||
messageIdx: messageIdx,
|
||||
}
|
||||
|
||||
const isEmpty = !chatMessage.content && !chatMessage.reasoning // && !(isLast && isLoading) // TODO!!!!
|
||||
if (isEmpty) return null
|
||||
const isEmpty = !chatMessage.content && !chatMessage.reasoning
|
||||
const isLastAndLoading = isLoading && isLast
|
||||
if (isEmpty && !isLastAndLoading) return null
|
||||
|
||||
return <>
|
||||
<div
|
||||
|
|
@ -1023,7 +1024,7 @@ const toolNameToTitle: Record<ToolName, string> = {
|
|||
'create_uri': 'Create file',
|
||||
'delete_uri': 'Delete file',
|
||||
'edit': 'Edit file',
|
||||
'terminal_command': 'Ran terminal command'
|
||||
'terminal_command': 'Run terminal command'
|
||||
}
|
||||
const toolNameToDesc = (toolName: ToolName, _toolParams: ToolCallParams[ToolName] | undefined): string => {
|
||||
|
||||
|
|
@ -1335,8 +1336,10 @@ const toolNameToComponent: { [T in ToolName]: {
|
|||
const commandService = accessor.get('ICommandService')
|
||||
const title = toolNameToTitle[toolRequest.name]
|
||||
const desc1 = toolNameToDesc(toolRequest.name, toolRequest.params)
|
||||
return <DropdownComponent title={title} desc1={desc1}
|
||||
// TODO!!! open the terminal with that ID
|
||||
|
||||
const { waitForCompletion, command, proposedTerminalId } = toolRequest.params
|
||||
return <DropdownComponent title={title} desc1={desc1} desc2={waitForCompletion ? '(background task)' : null}
|
||||
// TODO!!! open terminal
|
||||
/>
|
||||
},
|
||||
resultWrapper: ({ toolMessage }) => {
|
||||
|
|
@ -1349,19 +1352,32 @@ const toolNameToComponent: { [T in ToolName]: {
|
|||
return <ToolError title={title} desc1={desc1} errorMessage={toolMessage.result.value} />
|
||||
}
|
||||
|
||||
const { params } = toolMessage.result
|
||||
|
||||
const { command } = toolMessage.result.params
|
||||
const { terminalId, resolveReason, result } = toolMessage.result.value
|
||||
|
||||
return (
|
||||
<DropdownComponent
|
||||
title={title}
|
||||
desc1={desc1}
|
||||
desc2={resolveReason.type === 'bgtask' ? '(background task)' : null}
|
||||
>
|
||||
<div
|
||||
className="hover:brightness-125 hover:cursor-pointer transition-all duration-200 flex items-center flex-nowrap"
|
||||
// TODO!!! open terminal
|
||||
>
|
||||
<div className="flex-shrink-0"><svg className="w-1 h-1 opacity-60 mr-1.5 fill-current" viewBox="0 0 100 40"><rect x="0" y="15" width="100" height="10" /></svg></div>
|
||||
<ChatMarkdownRender string={''} chatMessageLocation={undefined} />
|
||||
<div>
|
||||
<div className="flex-shrink-0"><svg className="w-1 h-1 opacity-60 mr-1.5 fill-current" viewBox="0 0 100 40"><rect x="0" y="15" width="100" height="10" /></svg></div>
|
||||
{resolveReason.type === 'bgtask' ? 'Result so far:' : null}
|
||||
<ChatMarkdownRender string={`\`\`\`\n${result}\n\`\`\``} chatMessageLocation={undefined} />
|
||||
{
|
||||
resolveReason.type === 'done' ? (resolveReason.exitCode !== 0 ? `Error: exit code ${resolveReason.exitCode}` : null)
|
||||
: resolveReason.type === 'bgtask' ? null :
|
||||
resolveReason.type === 'timeout' ? `(partial results; request timed out)` :
|
||||
resolveReason.type === 'toofull' ? `(truncated)`
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</DropdownComponent>
|
||||
)
|
||||
|
|
@ -1371,8 +1387,9 @@ const toolNameToComponent: { [T in ToolName]: {
|
|||
|
||||
|
||||
type ChatBubbleMode = 'display' | 'edit'
|
||||
type ChatBubbleProps = { chatMessage: ChatMessage, messageIdx: number, isLoading?: boolean, }
|
||||
const ChatBubble = ({ chatMessage, isLoading, messageIdx }: ChatBubbleProps) => {
|
||||
type ChatBubbleProps = { chatMessage: ChatMessage, messageIdx: number, isLoading?: boolean, isLast: boolean }
|
||||
|
||||
const ChatBubble = ({ chatMessage, isLoading, messageIdx, isLast }: ChatBubbleProps) => {
|
||||
|
||||
const role = chatMessage.role
|
||||
|
||||
|
|
@ -1381,6 +1398,7 @@ const ChatBubble = ({ chatMessage, isLoading, messageIdx }: ChatBubbleProps) =>
|
|||
chatMessage={chatMessage}
|
||||
messageIdx={messageIdx}
|
||||
isLoading={isLoading}
|
||||
isLast={isLast}
|
||||
/>
|
||||
}
|
||||
else if (role === 'assistant') {
|
||||
|
|
@ -1388,6 +1406,7 @@ const ChatBubble = ({ chatMessage, isLoading, messageIdx }: ChatBubbleProps) =>
|
|||
chatMessage={chatMessage}
|
||||
messageIdx={messageIdx}
|
||||
isLoading={isLoading}
|
||||
isLast={isLast}
|
||||
/>
|
||||
}
|
||||
else if (role === 'tool_request') {
|
||||
|
|
@ -1508,7 +1527,7 @@ export const SidebarChat = () => {
|
|||
|
||||
const pastMessagesHTML = useMemo(() => {
|
||||
return previousMessages.map((message, i) =>
|
||||
<ChatBubble key={getChatBubbleId(currentThread.id, i)} chatMessage={message} messageIdx={i} />
|
||||
<ChatBubble key={getChatBubbleId(currentThread.id, i)} chatMessage={message} messageIdx={i} isLast={!isStreaming} />
|
||||
)
|
||||
}, [previousMessages, currentThread])
|
||||
|
||||
|
|
@ -1524,6 +1543,7 @@ export const SidebarChat = () => {
|
|||
anthropicReasoning: null,
|
||||
}}
|
||||
isLoading={isStreaming}
|
||||
isLast={true}
|
||||
/> : null
|
||||
|
||||
const allMessagesHTML = [...pastMessagesHTML, currStreamingMessageHTML]
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export const SidebarThreadSelector = () => {
|
|||
.filter(threadId => allThreads![threadId].messages.length !== 0)
|
||||
|
||||
return (
|
||||
<div className="flex p-2 flex-col gap-y-1 max-h-[400px] overflow-y-auto">
|
||||
<div className="flex p-2 flex-col gap-y-1 max-h-[200px] overflow-y-auto">
|
||||
|
||||
<div className="w-full relative flex justify-center items-center">
|
||||
{/* title */}
|
||||
|
|
|
|||
|
|
@ -3,23 +3,36 @@
|
|||
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js';
|
||||
import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js';
|
||||
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
|
||||
import { TerminalCapability } from '../../../../platform/terminal/common/capabilities/capabilities.js';
|
||||
import { TerminalLocation } from '../../../../platform/terminal/common/terminal.js';
|
||||
import { ITerminalService, ITerminalInstance } from '../../../../workbench/contrib/terminal/browser/terminal.js';
|
||||
import { ITerminalService, ITerminalInstance, ITerminalGroupService } from '../../../../workbench/contrib/terminal/browser/terminal.js';
|
||||
import { ResolveReason } from '../common/toolsServiceTypes.js';
|
||||
import { MAX_TERMINAL_CHARS_PAGE, TERMINAL_BG_WAIT_TIME, TERMINAL_TIMEOUT_TIME } from './toolsService.js';
|
||||
|
||||
|
||||
|
||||
export interface ITerminalToolService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
runCommand(command: string, proposedTerminalId: string, waitForCompletion: boolean): Promise<{ terminalId: string, didCreateTerminal: boolean, contents: string }>;
|
||||
runCommand(command: string, proposedTerminalId: string, waitForCompletion: boolean): Promise<{ terminalId: string, didCreateTerminal: boolean, result: string, resolveReason: ResolveReason }>;
|
||||
listTerminalIds(): string[];
|
||||
}
|
||||
|
||||
export const ITerminalToolService = createDecorator<ITerminalToolService>('TerminalToolService');
|
||||
|
||||
|
||||
|
||||
function isCommandComplete(output: string) {
|
||||
// https://code.visualstudio.com/docs/terminal/shell-integration#_vs-code-custom-sequences-osc-633-st
|
||||
const completionMatch = output.match(/\]633;D(?:;(\d+))?/)
|
||||
if (!completionMatch) { return false }
|
||||
if (completionMatch[1] !== undefined) return { exitCode: parseInt(completionMatch[1]) }
|
||||
return { exitCode: 0 }
|
||||
}
|
||||
|
||||
|
||||
const nameOfId = (id: string) => {
|
||||
if (id === '1') return 'Void Agent'
|
||||
return `Void Agent (${id})`
|
||||
|
|
@ -40,11 +53,11 @@ export class TerminalToolService extends Disposable implements ITerminalToolServ
|
|||
|
||||
constructor(
|
||||
@ITerminalService private readonly terminalService: ITerminalService,
|
||||
@ITerminalGroupService private readonly terminalGroupService: ITerminalGroupService,
|
||||
) {
|
||||
super();
|
||||
|
||||
// initialize any terminals that are already open
|
||||
|
||||
for (const terminal of terminalService.instances) {
|
||||
const proposedTerminalId = idOfName(terminal.title)
|
||||
if (proposedTerminalId) this.terminalInstanceOfId[proposedTerminalId] = terminal
|
||||
|
|
@ -82,7 +95,7 @@ export class TerminalToolService extends Disposable implements ITerminalToolServ
|
|||
const terminal = await this.terminalService.createTerminal({
|
||||
location: TerminalLocation.Panel,
|
||||
config: { name: nameOfId(terminalId), title: nameOfId(terminalId) }
|
||||
});
|
||||
})
|
||||
this.terminalInstanceOfId[terminalId] = terminal
|
||||
return { terminalId, didCreateTerminal: true }
|
||||
}
|
||||
|
|
@ -95,37 +108,71 @@ export class TerminalToolService extends Disposable implements ITerminalToolServ
|
|||
const terminal = this.terminalInstanceOfId[terminalId];
|
||||
if (!terminal) throw new Error(`Unexpected internal error: Terminal with ID ${terminalId} did not exist.`);
|
||||
|
||||
this.terminalGroupService.focusInstance(terminal)
|
||||
|
||||
if (!waitForCompletion) {
|
||||
console.log('NOT WAITING FOR COMPLETION')
|
||||
await terminal.sendText(command, true);
|
||||
return { terminalId, didCreateTerminal, contents: '(command is running in background...)' };
|
||||
}
|
||||
let result: string = ''
|
||||
let resolveReason: ResolveReason | undefined = undefined
|
||||
|
||||
// stream
|
||||
const disposables: IDisposable[] = []
|
||||
|
||||
let data = ''
|
||||
const d1 = terminal.onData(newData => { data += newData })
|
||||
|
||||
// terminal.onExit(() => {
|
||||
// console.log('TERMINALEXIT')
|
||||
// })
|
||||
|
||||
await terminal.sendText(command, true);
|
||||
// wait for the command to finish
|
||||
const commandDetection = terminal.capabilities.get(TerminalCapability.CommandDetection);
|
||||
if (commandDetection) {
|
||||
const d2 = commandDetection.onCommandFinished(() => {
|
||||
console.log('FINISHED', data)
|
||||
d1.dispose()
|
||||
d2.dispose()
|
||||
return { terminalId, didCreateTerminal, contents: data }
|
||||
// onFullPage
|
||||
const waitUntilFullPage = new Promise<void>((res, rej) => {
|
||||
const d1 = terminal.onData(async newData => {
|
||||
if (resolveReason) return
|
||||
result += newData
|
||||
if (result.length > MAX_TERMINAL_CHARS_PAGE) {
|
||||
result = result.substring(0, MAX_TERMINAL_CHARS_PAGE)
|
||||
await terminal.sendText('\x03', true) // interrupt the terminal with Ctrl+C
|
||||
resolveReason = { type: 'toofull' }
|
||||
res()
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
disposables.push(d1)
|
||||
})
|
||||
|
||||
console.log('didnot wait', data)
|
||||
d1.dispose()
|
||||
return { terminalId, didCreateTerminal, contents: 'Could not await data...' }
|
||||
// onDone
|
||||
const waitUntilDone = new Promise<void>((res, rej) => {
|
||||
const d2 = terminal.onData(newData => {
|
||||
if (resolveReason) return
|
||||
const isDone = isCommandComplete(result)
|
||||
if (isDone) {
|
||||
resolveReason = { type: 'done', exitCode: isDone.exitCode }
|
||||
res()
|
||||
return
|
||||
}
|
||||
})
|
||||
disposables.push(d2)
|
||||
})
|
||||
|
||||
|
||||
// send the command here
|
||||
await terminal.sendText(command, true)
|
||||
|
||||
// timeout promise
|
||||
const waitUntilTimeout = new Promise<void>((res, rej) => {
|
||||
setTimeout(async () => {
|
||||
if (resolveReason) return
|
||||
await terminal.sendText('\x03', true) // interrupt the terminal with Ctrl+C
|
||||
resolveReason = { type: waitForCompletion ? 'timeout' : 'bgtask' }
|
||||
res()
|
||||
}, (waitForCompletion ? TERMINAL_TIMEOUT_TIME : TERMINAL_BG_WAIT_TIME) * 1000)
|
||||
})
|
||||
|
||||
await Promise.any([
|
||||
waitUntilDone,
|
||||
waitUntilFullPage,
|
||||
waitUntilTimeout,
|
||||
])
|
||||
|
||||
disposables.forEach(d => d.dispose())
|
||||
|
||||
if (!resolveReason) throw new Error('Unexpected internal error: Promise.any should have resolved with a reason.')
|
||||
|
||||
console.log('res', { terminalId, didCreateTerminal, result, resolveReason })
|
||||
|
||||
|
||||
return { terminalId, didCreateTerminal, result, resolveReason }
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -133,3 +180,5 @@ export class TerminalToolService extends Disposable implements ITerminalToolServ
|
|||
}
|
||||
|
||||
registerSingleton(ITerminalToolService, TerminalToolService, InstantiationType.Delayed);
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ type ToolResultToString = { [T in ToolName]: (p: ToolCallParams[T], result: Tool
|
|||
// pagination info
|
||||
const MAX_FILE_CHARS_PAGE = 50_000
|
||||
const MAX_CHILDREN_URIs_PAGE = 500
|
||||
export const MAX_TERMINAL_CHARS_PAGE = 50_000
|
||||
export const TERMINAL_TIMEOUT_TIME = 15
|
||||
export const TERMINAL_BG_WAIT_TIME = 1
|
||||
|
||||
|
||||
|
||||
|
|
@ -322,8 +325,8 @@ export class ToolsService implements IToolsService {
|
|||
return {}
|
||||
},
|
||||
terminal_command: async ({ command, proposedTerminalId, waitForCompletion }) => {
|
||||
const { terminalId, didCreateTerminal } = await this.terminalToolService.runCommand(command, proposedTerminalId, waitForCompletion)
|
||||
return { terminalId, didCreateTerminal }
|
||||
const { terminalId, didCreateTerminal, result, resolveReason } = await this.terminalToolService.runCommand(command, proposedTerminalId, waitForCompletion)
|
||||
return { terminalId, didCreateTerminal, result, resolveReason }
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -353,10 +356,32 @@ export class ToolsService implements IToolsService {
|
|||
return `URI ${params.uri.fsPath} successfully deleted.`
|
||||
},
|
||||
edit: (params, result) => {
|
||||
return `Change successfully made ${params.uri.fsPath} successfully deleted.`
|
||||
return `Change successfully made to ${params.uri.fsPath}.`
|
||||
},
|
||||
terminal_command: (params, result) => {
|
||||
return `Terminal command "${params.command}" successfully executed in terminal ${result.terminalId}${result.didCreateTerminal ? `(a newly-created terminal)` : ''}.`
|
||||
|
||||
const {
|
||||
terminalId,
|
||||
didCreateTerminal,
|
||||
resolveReason,
|
||||
result: result_,
|
||||
} = result
|
||||
|
||||
const terminalDesc = `terminal ${terminalId}${didCreateTerminal ? ` (a newly-created terminal)` : ''}`
|
||||
|
||||
if (resolveReason.type === 'timeout') {
|
||||
return `Terminal command ran in ${terminalDesc}, but timed out after ${TERMINAL_TIMEOUT_TIME} seconds. Result:\n${result_}`
|
||||
}
|
||||
else if (resolveReason.type === 'bgtask') {
|
||||
return `Terminal command is running in the background in ${terminalDesc}. Here were the outputs after ${TERMINAL_BG_WAIT_TIME} seconds:\n${result_}`
|
||||
}
|
||||
else if (resolveReason.type === 'toofull') {
|
||||
return `Terminal command executed in terminal ${terminalDesc}. Command was interrupted because output was too long. Result:\n${result_}`
|
||||
}
|
||||
else if (resolveReason.type === 'done') {
|
||||
return `Terminal command executed in terminal ${terminalDesc}. Result (exit code ${resolveReason.exitCode}):\n${result_}`
|
||||
}
|
||||
throw new Error(`Unexpected internal error: Terminal command did not resolve with a valid reason.`)
|
||||
},
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ export type ToolDirectoryItem = {
|
|||
}
|
||||
|
||||
|
||||
export type ResolveReason = { type: 'toofull' | 'timeout' | 'bgtask' } | { type: 'done', exitCode: number }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -158,6 +161,6 @@ export type ToolResultType = {
|
|||
'edit': {},
|
||||
'create_uri': {},
|
||||
'delete_uri': {},
|
||||
'terminal_command': { terminalId: string, didCreateTerminal: boolean },
|
||||
'terminal_command': { terminalId: string, didCreateTerminal: boolean, result: string; resolveReason: ResolveReason; },
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -368,6 +368,7 @@ const prepareMessages_noEmptyMessage = ({ messages }: { messages: PrepareMessage
|
|||
else if (c.type === 'tool_use') { }
|
||||
else if (c.type === 'tool_result') { }
|
||||
}
|
||||
if (currMsg.content.length === 0) currMsg.content = [{ type: 'text', text: EMPTY_MESSAGE }]
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue