diff --git a/src/vs/workbench/contrib/void/browser/aiRegexService.ts b/src/vs/workbench/contrib/void/browser/aiRegexService.ts
index 53ed7e4e..b0d02024 100644
--- a/src/vs/workbench/contrib/void/browser/aiRegexService.ts
+++ b/src/vs/workbench/contrib/void/browser/aiRegexService.ts
@@ -34,7 +34,7 @@
// // const result = await new Promise((res, rej) => {
// // sendLLMMessage({
// // messages,
-// // tools: ['search_files'],
+// // tools: ['search_for_files'],
// // onFinalMessage: ({ result: r, }) => {
// // res(r)
// // },
@@ -73,7 +73,7 @@
// // const result = new Promise((res, rej) => {
// // sendLLMMessage({
// // messages,
-// // tools: ['search_files'],
+// // tools: ['search_for_files'],
// // onResult: (r) => {
// // res(r)
// // }
diff --git a/src/vs/workbench/contrib/void/browser/directoryStrService.ts b/src/vs/workbench/contrib/void/browser/directoryStrService.ts
index ff5972bc..8174f63f 100644
--- a/src/vs/workbench/contrib/void/browser/directoryStrService.ts
+++ b/src/vs/workbench/contrib/void/browser/directoryStrService.ts
@@ -417,7 +417,6 @@ class DirectoryStrService extends Disposable implements IDirectoryStrService {
break;
}
}
- console.log('cutoff!!!!!!!', str, cutOffMessage)
if (cutOff) {
return `${str}\n${cutOffMessage}`
diff --git a/src/vs/workbench/contrib/void/browser/editCodeService.ts b/src/vs/workbench/contrib/void/browser/editCodeService.ts
index 6e480048..653cf3af 100644
--- a/src/vs/workbench/contrib/void/browser/editCodeService.ts
+++ b/src/vs/workbench/contrib/void/browser/editCodeService.ts
@@ -1235,7 +1235,6 @@ class EditCodeService extends Disposable implements IEditCodeService {
_removeStylesFns: new Set(),
}
- console.log('FIRING START STREAMING IN DIFFZONE!!!')
const diffZone = this._addDiffArea(adding)
this._onDidChangeStreamingInDiffZone.fire({ uri, diffareaid: diffZone.diffareaid })
this._onDidAddOrDeleteDiffZones.fire({ 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 9f364ba0..7082d59b 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
@@ -162,7 +162,6 @@ export const useApplyButtonState = ({ applyBoxId, uri }: { applyBoxId: string, u
)
if (shouldUpdate) {
rerender(c => c + 1)
- console.log('rerendering....')
}
}, [applyBoxId, uri]))
@@ -322,12 +321,12 @@ export const ApplyButtonsHTML = ({ codeStr, applyBoxId, reapplyIcon, uri }: { co
>
}
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 4892feff..d58b7559 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
@@ -20,14 +20,13 @@ import { VOID_OPEN_SETTINGS_ACTION_ID } from '../../../voidSettingsPane.js';
import { ChatMode, FeatureName, isFeatureNameDisabled } from '../../../../../../../workbench/contrib/void/common/voidSettingsTypes.js';
import { WarningBox } from '../void-settings-tsx/WarningBox.js';
import { getModelCapabilities, getIsReasoningEnabledState } from '../../../../common/modelCapabilities.js';
-import { AlertTriangle, Ban, Check, ChevronRight, Dot, FileIcon, Pencil, Undo, Undo2, X } from 'lucide-react';
+import { AlertTriangle, Ban, Check, ChevronRight, Dot, FileIcon, Pencil, Undo, Undo2, X, Flag } from 'lucide-react';
import { ChatMessage, CheckpointEntry, StagingSelectionItem, ToolMessage } from '../../../../common/chatThreadServiceTypes.js';
import { LintErrorItem, ToolCallParams, ToolNameWithApproval } from '../../../../common/toolsServiceTypes.js';
import { ApplyButtonsHTML, CopyButton, IconShell1, JumpToFileButton, JumpToTerminalButton, StatusIndicator, StatusIndicatorForApplyButton, useApplyButtonState } from '../markdown/ApplyBlockHoverButtons.js';
import { IsRunningType } from '../../../chatThreadService.js';
import { acceptAllBg, acceptBorder, buttonFontSize, buttonTextColor, rejectAllBg, rejectBg, rejectBorder } from '../../../../common/helpers/colors.js';
import { ToolName, toolNames } from '../../../../common/prompt/prompts.js';
-import { error } from 'console';
import { RawToolCallObj } from '../../../../common/sendLLMMessageTypes.js';
import { MAX_FILE_CHARS_PAGE } from '../../../toolsService.js';
@@ -1173,7 +1172,7 @@ const titleOfToolName = {
'ls_dir': { done: 'Inspected folder', proposed: 'Inspect folder', running: loadingTitleWrapper('Inspecting folder') },
'get_dir_structure': { done: 'Inspected folder', proposed: 'Inspect folder', running: loadingTitleWrapper('Inspecting folder') },
'search_pathnames_only': { done: 'Searched by file name', proposed: 'Search by file name', running: loadingTitleWrapper('Searching by file name') },
- 'search_files': { done: 'Searched', proposed: 'Search', running: loadingTitleWrapper('Searching') },
+ 'search_for_files': { done: 'Searched', proposed: 'Search', running: loadingTitleWrapper('Searching') },
'create_file_or_folder': { done: `Created`, proposed: `Create`, running: loadingTitleWrapper(`Creating`) },
'delete_file_or_folder': { done: `Deleted`, proposed: `Delete`, running: loadingTitleWrapper(`Deleting`) },
'edit_file': { done: `Edited file`, proposed: 'Edit file', running: loadingTitleWrapper('Editing file') },
@@ -1207,8 +1206,8 @@ const toolNameToDesc = (toolName: ToolName, _toolParams: ToolCallParams[ToolName
} else if (toolName === 'search_pathnames_only') {
const toolParams = _toolParams as ToolCallParams['search_pathnames_only']
return `"${toolParams.queryStr}"`;
- } else if (toolName === 'search_files') {
- const toolParams = _toolParams as ToolCallParams['search_files']
+ } else if (toolName === 'search_for_files') {
+ const toolParams = _toolParams as ToolCallParams['search_for_files']
return `"${toolParams.queryStr}"`;
} else if (toolName === 'create_file_or_folder') {
const toolParams = _toolParams as ToolCallParams['create_file_or_folder']
@@ -1355,21 +1354,31 @@ const LintErrorChildren = ({ lintErrors }: { lintErrors: LintErrorItem[] }) => {
}
const EditToolLintErrors = ({ lintErrors }: { lintErrors: LintErrorItem[] }) => {
-
if (lintErrors.length === 0) return null;
-
const [isOpen, setIsOpen] = useState(false);
-
return (
-
-
{ setIsOpen(o => !o) }} >
-
-
-
+
+
setIsOpen(o => !o)}
+ style={{ background: 'none' }}
+ >
+
+ Lint errors
+
+
+
+ {lintErrors.map((error, i) => (
+
Lines {error.startLineNumber}-{error.endLineNumber}: {error.message}
+ ))}
+
+
- )
-
-
+ );
}
@@ -1445,7 +1454,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper
,
const { params, result } = toolMessage
componentParams.onClick = () => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }
if (result.hasNextPage && params.pageNumber === 1) // first page
- componentParams.desc2 = `(first ${Math.round(MAX_FILE_CHARS_PAGE) / 1000}k)`
+ componentParams.desc2 = `(truncated after ${Math.round(MAX_FILE_CHARS_PAGE) / 1000}k)`
else if (params.pageNumber > 1) // subsequent pages
componentParams.desc2 = `(part ${params.pageNumber})`
}
@@ -1596,7 +1605,7 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper,
return
}
},
- 'search_files': {
+ 'search_for_files': {
resultWrapper: ({ toolMessage }) => {
const accessor = useAccessor()
const commandService = accessor.get('ICommandService')
@@ -1925,28 +1934,30 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper,
const Checkpoint = ({ message, threadId, messageIdx, isCheckpointGhost, threadIsRunning }: { message: CheckpointEntry, threadId: string; messageIdx: number, isCheckpointGhost: boolean, threadIsRunning: boolean }) => {
const accessor = useAccessor()
const chatThreadService = accessor.get('IChatThreadService')
+ const [showCheckpointIcon, setShowCheckpointIcon] = React.useState(false); // add icon state
return
{
if (threadIsRunning) return
chatThreadService.jumpToCheckpointBeforeMessageIdx({ threadId, messageIdx, jumpToUserModified: true })
}}
>
- Checkpoint
+ Checkpoint
-
}
+
type ChatBubbleMode = 'display' | 'edit'
type ChatBubbleProps = {
chatMessage: ChatMessage,
diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-editor-widgets-tsx/VoidCommandBar.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-editor-widgets-tsx/VoidCommandBar.tsx
index b4940e9e..dcf97f8e 100644
--- a/src/vs/workbench/contrib/void/browser/react/src/void-editor-widgets-tsx/VoidCommandBar.tsx
+++ b/src/vs/workbench/contrib/void/browser/react/src/void-editor-widgets-tsx/VoidCommandBar.tsx
@@ -245,12 +245,12 @@ const VoidCommandBar = ({ uri, editor }: VoidCommandBarProps) => {
//
const acceptAllButton =
const rejectAllButton =
diff --git a/src/vs/workbench/contrib/void/browser/sidebarActions.ts b/src/vs/workbench/contrib/void/browser/sidebarActions.ts
index e1476552..ba257dc4 100644
--- a/src/vs/workbench/contrib/void/browser/sidebarActions.ts
+++ b/src/vs/workbench/contrib/void/browser/sidebarActions.ts
@@ -25,6 +25,7 @@ import { Disposable } from '../../../../base/common/lifecycle.js';
import { localize2 } from '../../../../nls.js';
import { StagingSelectionItem } from '../common/chatThreadServiceTypes.js';
import { IChatThreadService } from './chatThreadService.js';
+import { getActiveWindow } from '../../../../base/browser/dom.js';
// ---------- Register commands and keybindings ----------
@@ -225,9 +226,13 @@ registerAction2(class extends Action2 {
metricsService.capture('Chat Navigation', { type: 'New Chat' })
stateService.setState({ isHistoryOpen: false, currentTab: 'chat' })
- stateService.fireFocusChat()
const chatThreadService = accessor.get(IChatThreadService)
chatThreadService.openNewThread()
+
+ // focus
+ stateService.fireFocusChat()
+ const window = getActiveWindow()
+ window.requestAnimationFrame(() => stateService.fireFocusChat())
}
})
diff --git a/src/vs/workbench/contrib/void/browser/toolsService.ts b/src/vs/workbench/contrib/void/browser/toolsService.ts
index cc37f836..b2288476 100644
--- a/src/vs/workbench/contrib/void/browser/toolsService.ts
+++ b/src/vs/workbench/contrib/void/browser/toolsService.ts
@@ -34,7 +34,7 @@ type ToolResultToString = { [T in ToolName]: (p: ToolCallParams[T], result: Awai
// pagination info
-export const MAX_FILE_CHARS_PAGE = 50_000
+export const MAX_FILE_CHARS_PAGE = Infinity
export const MAX_CHILDREN_URIs_PAGE = 500
export const MAX_TERMINAL_CHARS_PAGE = 20_000
export const TERMINAL_TIMEOUT_TIME = 5 // seconds
@@ -163,8 +163,11 @@ export class ToolsService implements IToolsService {
const uri = validateURI(uriStr)
const pageNumber = validatePageNum(pageNumberUnknown)
- const startLine = validateNumber(startLineUnknown, { default: null })
- const endLine = validateNumber(endLineUnknown, { default: null })
+ let startLine = validateNumber(startLineUnknown, { default: null })
+ let endLine = validateNumber(endLineUnknown, { default: null })
+
+ if (startLine !== null && startLine < 1) startLine = null
+ if (endLine !== null && endLine < 1) endLine = null
return { uri, startLine, endLine, pageNumber }
},
@@ -194,7 +197,7 @@ export class ToolsService implements IToolsService {
return { queryStr, searchInFolder, pageNumber }
},
- search_files: (params: RawToolParamsObj) => {
+ search_for_files: (params: RawToolParamsObj) => {
const {
query: queryUnknown,
search_in_folder: searchInFolderUnknown,
@@ -308,7 +311,7 @@ export class ToolsService implements IToolsService {
return { result: { uris, hasNextPage } }
},
- search_files: async ({ queryStr, isRegex, searchInFolder, pageNumber }) => {
+ search_for_files: async ({ queryStr, isRegex, searchInFolder, pageNumber }) => {
const searchFolders = searchInFolder === null ?
workspaceContextService.getWorkspace().folders.map(f => f.uri)
: [searchInFolder]
@@ -400,7 +403,7 @@ export class ToolsService implements IToolsService {
// given to the LLM after the call
this.stringOfResult = {
read_file: (params, result) => {
- return `${result.fileContents}${nextPageStr(result.hasNextPage)}${result.hasNextPage ? `This file has ${result.totalFileLen} characters, paginated ${MAX_FILE_CHARS_PAGE} at a time.` : ''}`
+ return `${params.uri.fsPath}\n\`\`\`\n${result.fileContents}\n\`\`\`${nextPageStr(result.hasNextPage)}`
},
ls_dir: (params, result) => {
const dirTreeStr = stringifyDirectoryTree1Deep(params, result)
@@ -412,7 +415,7 @@ export class ToolsService implements IToolsService {
search_pathnames_only: (params, result) => {
return result.uris.map(uri => uri.fsPath).join('\n') + nextPageStr(result.hasNextPage)
},
- search_files: (params, result) => {
+ search_for_files: (params, result) => {
return result.uris.map(uri => uri.fsPath).join('\n') + nextPageStr(result.hasNextPage)
},
read_lint_errors: (params, result) => {
diff --git a/src/vs/workbench/contrib/void/common/prompt/prompts.ts b/src/vs/workbench/contrib/void/common/prompt/prompts.ts
index 57383bf3..0c33c7f7 100644
--- a/src/vs/workbench/contrib/void/common/prompt/prompts.ts
+++ b/src/vs/workbench/contrib/void/common/prompt/prompts.ts
@@ -74,8 +74,8 @@ export const voidTools = {
description: `Returns full contents of a given file.`,
params: {
...uriParam('file'),
- start_line: { description: 'Optional. Default is 1. Start reading on this line.' },
- end_line: { description: 'Optional. Default is Infinity. Stop reading after this line.' },
+ start_line: { description: 'Optional. Only fill this in if you already know the line numbers you need to search. Defaults to 1.' },
+ end_line: { description: 'Optional. Only fill this in if you already know the line numbers you need to search. Defaults to Infinity.' },
...paginationParam,
},
},
@@ -84,7 +84,7 @@ export const voidTools = {
name: 'ls_dir',
description: `Lists all files and folders in the given URI.`,
params: {
- ...uriParam('folder'),
+ uri: { description: `Optional. The FULL path to the ${'folder'}. Leave this as empty or "" to search all folders.` },
...paginationParam,
},
},
@@ -106,19 +106,19 @@ export const voidTools = {
description: `Returns all pathnames that match a given query (searches ONLY file names). You should use this when looking for a file with a specific name or path.`,
params: {
query: { description: `Your query for the search.` },
- search_in_folder: { description: 'Optional. Only search files in this given folder glob.' },
+ search_in_folder: { description: 'Optional. Only fill this in if you need to limit your search because there were too many results.' },
...paginationParam,
},
},
- search_files: {
- name: 'search_files',
- description: `Returns all pathnames that match a given query (searches ONLY file contents). The query can be any substring or glob. You can follow this with read_file to view result contents.`,
+ search_for_files: {
+ name: 'search_for_files',
+ description: `Returns a list of file names whose content matches the given query. The query can be any substring or regex.`,
params: {
query: { description: `Your query for the search.` },
- search_in_folder: { description: 'Optional. Only search files in this given folder glob.' },
+ search_in_folder: { description: 'Optional. Only fill this in if you need to limit your search because there were too many results.' },
is_regex: { description: 'Optional. Default is false. Whether query is a regex.' },
...paginationParam,
},
@@ -158,10 +158,9 @@ export const voidTools = {
...uriParam('file'),
change_description: {
description: `\
-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. \
-Your description will be handed to a smaller model to make the change, so it must be clear and concise. \
Your description MUST be wrapped in triple backticks. \
+A code description of the change you want to make, with comments like "// ... existing code ..." to condense your writing. \
+NEVER re-write the whole file. Bias towards writing as little as possible. \
Here's an example of a good description:\n${editToolDescriptionExample}`
}
},
@@ -345,7 +344,6 @@ ${details.map((d, i) => `${i + 1}. ${d}`).join('\n\n')}`)
ansStrs.push(fsInfo)
if (toolDefinitions) ansStrs.push(toolDefinitions)
ansStrs.push(importantDetails)
- ansStrs.push('Now, please assist the user with their query.')
const fullSystemMsgStr = ansStrs
.join('\n\n\n')
@@ -464,13 +462,13 @@ Output SEARCH/REPLACE blocks to edit the file according to the desired change. Y
Directions:
1. Your OUTPUT should consist ONLY of SEARCH/REPLACE blocks. Do NOT output any text or explanations before or after this.
-2. The "ORIGINAL" code in each SEARCH/REPLACE block must EXACTLY match lines in the original file. This includes whitespace, comments, and other details.
-3. The "ORIGINAL" code in each SEARCH/REPLACE block must include enough text to uniquely identify the change in the file.
+2. The "ORIGINAL" code in each SEARCH/REPLACE block must EXACTLY match lines in the original file. The original code must NOT includes any new whitespace, comments, or any other modifications from the original code.
+3. The "ORIGINAL" code in each SEARCH/REPLACE block must include enough text to uniquely identify the change in the file, but please bias towards writing as little as possible.
4. The "ORIGINAL" code in each SEARCH/REPLACE block must be disjoint from all other blocks.
The SEARCH/REPLACE blocks you generate will be applied immediately, and so they **MUST** produce a file that the user can run IMMEDIATELY.
- Make sure you add all necessary imports.
-- Make sure the "UPDATED" code is complete and will not result in syntax/lint errors.
+- Make sure the "UPDATED" code is ready for production as-is, and fix any relevant lint errors.
Follow coding conventions of the user (spaces, semilcolons, comments, etc). If the user spaces or formats things a certain way, CONTINUE formatting it that way, even if you prefer otherwise.
diff --git a/src/vs/workbench/contrib/void/common/toolsServiceTypes.ts b/src/vs/workbench/contrib/void/common/toolsServiceTypes.ts
index a8589e0f..22941826 100644
--- a/src/vs/workbench/contrib/void/common/toolsServiceTypes.ts
+++ b/src/vs/workbench/contrib/void/common/toolsServiceTypes.ts
@@ -27,7 +27,7 @@ export type ToolCallParams = {
'ls_dir': { rootURI: URI, pageNumber: number },
'get_dir_structure': { rootURI: URI },
'search_pathnames_only': { queryStr: string, searchInFolder: string | null, pageNumber: number },
- 'search_files': { queryStr: string, isRegex: boolean, searchInFolder: URI | null, pageNumber: number },
+ 'search_for_files': { queryStr: string, isRegex: boolean, searchInFolder: URI | null, pageNumber: number },
'read_lint_errors': { uri: URI },
// ---
'edit_file': { uri: URI, changeDescription: string },
@@ -43,7 +43,7 @@ export type ToolResultType = {
'ls_dir': { children: ShallowDirectoryItem[] | null, hasNextPage: boolean, hasPrevPage: boolean, itemsRemaining: number },
'get_dir_structure': { str: string, },
'search_pathnames_only': { uris: URI[], hasNextPage: boolean },
- 'search_files': { uris: URI[], hasNextPage: boolean },
+ 'search_for_files': { uris: URI[], hasNextPage: boolean },
'read_lint_errors': { lintErrors: LintErrorItem[] | null },
// ---
'edit_file': Promise<{ lintErrors: LintErrorItem[] | null }>,
diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts
index 687497d0..b3eec6ae 100644
--- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts
+++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts
@@ -256,7 +256,12 @@ const _sendOpenAICompatibleChat = ({ messages, onText, onFinalMessage, onError,
fullReasoningSoFar += newReasoning
}
- onText({ fullText: fullTextSoFar, fullReasoning: fullReasoningSoFar })
+ onText({
+ fullText: fullTextSoFar,
+ fullReasoning: fullReasoningSoFar,
+ toolCall: isAToolName(toolName) ? { name: toolName, rawParams: {}, isDone: false, doneParams: [], id: toolId } : undefined,
+ })
+
}
// on final
if (!fullTextSoFar && !fullReasoningSoFar && !toolName) {
@@ -402,6 +407,14 @@ const sendAnthropicChat = ({ messages, providerName, onText, onFinalMessage, onE
let fullToolName = ''
let fullToolParams = ''
+
+ const runOnText = () => {
+ onText({
+ fullText,
+ fullReasoning,
+ toolCall: isAToolName(fullToolName) ? { name: fullToolName, rawParams: {}, isDone: false, doneParams: [], id: 'dummy' } : undefined,
+ })
+ }
// there are no events for tool_use, it comes in at the end
stream.on('streamEvent', e => {
// start block
@@ -409,22 +422,22 @@ const sendAnthropicChat = ({ messages, providerName, onText, onFinalMessage, onE
if (e.content_block.type === 'text') {
if (fullText) fullText += '\n\n' // starting a 2nd text block
fullText += e.content_block.text
- onText({ fullText, fullReasoning, })
+ runOnText()
}
else if (e.content_block.type === 'thinking') {
if (fullReasoning) fullReasoning += '\n\n' // starting a 2nd reasoning block
fullReasoning += e.content_block.thinking
- onText({ fullText, fullReasoning, })
+ runOnText()
}
else if (e.content_block.type === 'redacted_thinking') {
console.log('delta', e.content_block.type)
if (fullReasoning) fullReasoning += '\n\n' // starting a 2nd reasoning block
fullReasoning += '[redacted_thinking]'
- onText({ fullText, fullReasoning, })
+ runOnText()
}
else if (e.content_block.type === 'tool_use') {
fullToolName += e.content_block.name ?? '' // anthropic gives us the tool name in the start block
- onText({ fullText, fullReasoning, })
+ runOnText()
}
}
@@ -432,15 +445,15 @@ const sendAnthropicChat = ({ messages, providerName, onText, onFinalMessage, onE
else if (e.type === 'content_block_delta') {
if (e.delta.type === 'text_delta') {
fullText += e.delta.text
- onText({ fullText, fullReasoning, })
+ runOnText()
}
else if (e.delta.type === 'thinking_delta') {
fullReasoning += e.delta.thinking
- onText({ fullText, fullReasoning, })
+ runOnText()
}
else if (e.delta.type === 'input_json_delta') { // tool use
fullToolParams += e.delta.partial_json ?? '' // anthropic gives us the partial delta (string) here - https://docs.anthropic.com/en/api/messages-streaming
- onText({ fullText, fullReasoning, })
+ runOnText()
}
}
})