diff --git a/packages/core/src/amazonq/webview/ui/apps/cwChatConnector.ts b/packages/core/src/amazonq/webview/ui/apps/cwChatConnector.ts index d89d4f65c14..174dfa1a9a1 100644 --- a/packages/core/src/amazonq/webview/ui/apps/cwChatConnector.ts +++ b/packages/core/src/amazonq/webview/ui/apps/cwChatConnector.ts @@ -179,6 +179,33 @@ export class Connector extends BaseConnector { } } + private processToolMessage = async (messageData: any): Promise => { + if (this.onChatAnswerUpdated === undefined) { + return + } + const answer: CWCChatItem = { + type: messageData.messageType, + messageId: messageData.messageID ?? messageData.triggerID, + body: messageData.message, + followUp: messageData.followUps, + canBeVoted: messageData.canBeVoted ?? false, + codeReference: messageData.codeReference, + userIntent: messageData.contextList, + codeBlockLanguage: messageData.codeBlockLanguage, + contextList: messageData.contextList, + title: messageData.title, + buttons: messageData.buttons, + fileList: messageData.fileList, + header: messageData.header ?? undefined, + padding: messageData.padding ?? undefined, + fullWidth: messageData.fullWidth ?? undefined, + codeBlockActions: messageData.codeBlockActions ?? undefined, + rootFolderTitle: messageData.rootFolderTitle, + } + this.onChatAnswerUpdated(messageData.tabID, answer) + return + } + private storeChatItem(tabId: string, messageId: string, item: ChatItem): void { if (!this.chatItems.has(tabId)) { this.chatItems.set(tabId, new Map()) @@ -238,6 +265,11 @@ export class Connector extends BaseConnector { return } + if (messageData.type === 'toolMessage') { + await this.processToolMessage(messageData) + return + } + if (messageData.type === 'editorContextCommandMessage') { await this.processEditorContextCommandMessage(messageData) return @@ -369,45 +401,43 @@ export class Connector extends BaseConnector { } break case 'run-shell-command': - answer.header = { - body: 'shell', - status: { + if (answer.header) { + answer.header.status = { icon: 'ok' as MynahIconsType, text: 'Accepted', status: 'success', - }, + } + answer.header.buttons = [] } break case 'reject-shell-command': - answer.header = { - body: 'shell', - status: { + if (answer.header) { + answer.header.status = { icon: 'cancel' as MynahIconsType, text: 'Rejected', status: 'error', - }, + } + answer.header.buttons = [] } break case 'confirm-tool-use': - answer.header = { - icon: 'shell' as MynahIconsType, - body: 'shell', - status: { + if (answer.header) { + answer.header.status = { icon: 'ok' as MynahIconsType, text: 'Accepted', status: 'success', - }, + } + answer.header.buttons = [] } break case 'reject-tool-use': - answer.header = { - icon: 'shell' as MynahIconsType, - body: 'shell', - status: { + if (answer.header) { + answer.header.status = { icon: 'cancel' as MynahIconsType, text: 'Rejected', status: 'error', - }, + } + answer.header.buttons = [] } break default: diff --git a/packages/core/src/amazonq/webview/ui/connector.ts b/packages/core/src/amazonq/webview/ui/connector.ts index 9ed46f8f58d..04a740624f1 100644 --- a/packages/core/src/amazonq/webview/ui/connector.ts +++ b/packages/core/src/amazonq/webview/ui/connector.ts @@ -78,7 +78,7 @@ export interface ConnectorProps { sendMessageToExtension: (message: ExtensionMessage) => void onMessageReceived?: (tabID: string, messageData: any, needToShowAPIDocsTab: boolean) => void onRunTestMessageReceived?: (tabID: string, showRunTestMessage: boolean) => void - onChatAnswerUpdated?: (tabID: string, message: ChatItem) => void + onChatAnswerUpdated?: (tabID: string, message: CWCChatItem) => void onChatAnswerReceived?: (tabID: string, message: ChatItem, messageData: any) => void onWelcomeFollowUpClicked: (tabID: string, welcomeFollowUpType: WelcomeFollowupType) => void onAsyncEventProgress: (tabID: string, inProgress: boolean, message: string | undefined) => void diff --git a/packages/core/src/amazonq/webview/ui/main.ts b/packages/core/src/amazonq/webview/ui/main.ts index 580229d3ee2..3204ebe65b7 100644 --- a/packages/core/src/amazonq/webview/ui/main.ts +++ b/packages/core/src/amazonq/webview/ui/main.ts @@ -100,6 +100,41 @@ export const createMynahUI = ( welcomeCount += 1 } + /** + * Creates a file list header from context list + * @param contextList List of file contexts + * @param rootFolderTitle Title for the root folder + * @returns Header object with file list + */ + const createFileListHeader = (contextList: any[], rootFolderTitle?: string) => { + return { + fileList: { + fileTreeTitle: '', + filePaths: contextList.map((file) => file.relativeFilePath), + rootFolderTitle: rootFolderTitle, + flatList: true, + collapsed: true, + hideFileCount: true, + details: Object.fromEntries( + contextList.map((file) => [ + file.relativeFilePath, + { + label: file.lineRanges + .map((range: { first: number; second: number }) => + range.first === -1 || range.second === -1 + ? '' + : `line ${range.first} - ${range.second}` + ) + .join(', '), + description: file.relativeFilePath, + clickable: true, + }, + ]) + ), + }, + } + } + // Adding the first tab as CWC tab tabsStorage.addTab({ id: 'tab-1', @@ -346,8 +381,11 @@ export const createMynahUI = ( sendMessageToExtension: (message) => { ideApi.postMessage(message) }, - onChatAnswerUpdated: (tabID: string, item: ChatItem) => { + onChatAnswerUpdated: (tabID: string, item: CWCChatItem) => { if (item.messageId !== undefined) { + if (item.contextList !== undefined && item.contextList.length > 0) { + item.header = createFileListHeader(item.contextList, item.rootFolderTitle) + } mynahUI.updateChatAnswerWithMessageId(tabID, item.messageId, { ...(item.body !== undefined ? { body: item.body } : {}), ...(item.buttons !== undefined ? { buttons: item.buttons } : {}), @@ -409,32 +447,7 @@ export const createMynahUI = ( } if (item.contextList !== undefined && item.contextList.length > 0) { - item.header = { - fileList: { - fileTreeTitle: '', - filePaths: item.contextList.map((file) => file.relativeFilePath), - rootFolderTitle: item.rootFolderTitle, - flatList: true, - collapsed: true, - hideFileCount: true, - details: Object.fromEntries( - item.contextList.map((file) => [ - file.relativeFilePath, - { - label: file.lineRanges - .map((range) => - range.first === -1 || range.second === -1 - ? '' - : `line ${range.first} - ${range.second}` - ) - .join(', '), - description: file.relativeFilePath, - clickable: true, - }, - ]) - ), - }, - } + item.header = createFileListHeader(item.contextList, item.rootFolderTitle) } if ( diff --git a/packages/core/src/codewhispererChat/clients/chat/v0/chat.ts b/packages/core/src/codewhispererChat/clients/chat/v0/chat.ts index 53551b254f5..10409be4ada 100644 --- a/packages/core/src/codewhispererChat/clients/chat/v0/chat.ts +++ b/packages/core/src/codewhispererChat/clients/chat/v0/chat.ts @@ -14,7 +14,7 @@ import { ToolkitError } from '../../../../shared/errors' import { createCodeWhispererChatStreamingClient } from '../../../../shared/clients/codewhispererChatClient' import { createQDeveloperStreamingClient } from '../../../../shared/clients/qDeveloperChatClient' import { UserWrittenCodeTracker } from '../../../../codewhisperer/tracker/userWrittenCodeTracker' -import { PromptMessage } from '../../../controllers/chat/model' +import { DocumentReference, PromptMessage } from '../../../controllers/chat/model' import { FsWriteBackup } from '../../../../codewhispererChat/tools/fsWrite' export type ToolUseWithError = { @@ -30,8 +30,10 @@ export class ChatSession { * _readFiles = list of files read from the project to gather context before generating response. * _showDiffOnFileWrite = Controls whether to show diff view (true) or file context view (false) to the user * _context = Additional context to be passed to the LLM for generating the response + * _messageIdToUpdate = messageId of a chat message to be updated, used for reducing consecutive tool messages */ - private _readFiles: string[] = [] + private _readFiles: DocumentReference[] = [] + private _readFolders: DocumentReference[] = [] private _toolUseWithError: ToolUseWithError | undefined private _showDiffOnFileWrite: boolean = false private _context: PromptMessage['context'] @@ -41,6 +43,8 @@ export class ChatSession { * True if messages from local history have been sent to session. */ localHistoryHydrated: boolean = false + private _messageIdToUpdate: string | undefined + private _messageIdToUpdateListDirectory: string | undefined contexts: Map = new Map() // TODO: doesn't handle the edge case when two files share the same relativePath string but from different root @@ -49,6 +53,21 @@ export class ChatSession { public get sessionIdentifier(): string | undefined { return this.sessionId } + public get messageIdToUpdate(): string | undefined { + return this._messageIdToUpdate + } + + public setMessageIdToUpdate(messageId: string | undefined) { + this._messageIdToUpdate = messageId + } + + public get messageIdToUpdateListDirectory(): string | undefined { + return this._messageIdToUpdateListDirectory + } + + public setMessageIdToUpdateListDirectory(messageId: string | undefined) { + this._messageIdToUpdateListDirectory = messageId + } public get pairProgrammingModeOn(): boolean { return this._pairProgrammingModeOn @@ -95,21 +114,30 @@ export class ChatSession { public setSessionID(id?: string) { this.sessionId = id } - public get readFiles(): string[] { + public get readFiles(): DocumentReference[] { return this._readFiles } + public get readFolders(): DocumentReference[] { + return this._readFolders + } public get showDiffOnFileWrite(): boolean { return this._showDiffOnFileWrite } public setShowDiffOnFileWrite(value: boolean) { this._showDiffOnFileWrite = value } - public addToReadFiles(filePath: string) { + public addToReadFiles(filePath: DocumentReference) { this._readFiles.push(filePath) } public clearListOfReadFiles() { this._readFiles = [] } + public setReadFolders(folder: DocumentReference) { + this._readFolders.push(folder) + } + public clearListOfReadFolders() { + this._readFolders = [] + } async chatIam(chatRequest: SendMessageRequest): Promise { const client = await createQDeveloperStreamingClient() diff --git a/packages/core/src/codewhispererChat/controllers/chat/controller.ts b/packages/core/src/codewhispererChat/controllers/chat/controller.ts index 8b1f733d0e2..e347351100b 100644 --- a/packages/core/src/codewhispererChat/controllers/chat/controller.ts +++ b/packages/core/src/codewhispererChat/controllers/chat/controller.ts @@ -728,9 +728,19 @@ export class ChatController { try { await ToolUtils.validate(tool) - const chatStream = new ChatStream(this.messenger, tabID, triggerID, toolUse, { - requiresAcceptance: false, - }) + const chatStream = new ChatStream( + this.messenger, + tabID, + triggerID, + toolUse, + session, + undefined, + false, + { + requiresAcceptance: false, + }, + false + ) if (tool.type === ToolType.FsWrite && toolUse.toolUseId) { const backup = await tool.tool.getBackup() session.setFsWriteBackup(toolUse.toolUseId, backup) @@ -1221,6 +1231,7 @@ export class ChatController { private async processPromptMessageAsNewThread(message: PromptMessage) { const session = this.sessionStorage.getSession(message.tabID) session.clearListOfReadFiles() + session.clearListOfReadFolders() session.setShowDiffOnFileWrite(false) this.editorContextExtractor .extractContextForTrigger('ChatMessage') diff --git a/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts b/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts index f8306b00874..839f8ea087a 100644 --- a/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts +++ b/packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts @@ -22,6 +22,7 @@ import { CloseDetailedListMessage, SelectTabMessage, ChatItemHeader, + ToolMessage, } from '../../../view/connector/connector' import { EditorContextCommandType } from '../../../commands/registerCommands' import { ChatResponseStream as qdevChatResponseStream } from '@amzn/amazon-q-developer-streaming-client' @@ -54,6 +55,8 @@ import { MynahIconsType, DetailedList, MynahUIDataModel, + MynahIcons, + Status, } from '@aws/mynah-ui' import { Database } from '../../../../shared/db/chatDb/chatDb' import { TabType } from '../../../../amazonq/webview/ui/storages/tabsStorage' @@ -69,6 +72,8 @@ import { FsWriteParams } from '../../../tools/fsWrite' import { AsyncEventProgressMessage } from '../../../../amazonq/commons/connector/connectorMessages' import { localize } from '../../../../shared/utilities/vsCodeUtils' import { getDiffLinesFromChanges } from '../../../../shared/utilities/diffUtils' +import { FsReadParams } from '../../../tools/fsRead' +import { ListDirectoryParams } from '../../../tools/listDirectory' export type StaticTextResponseType = 'quick-action-help' | 'onboarding-help' | 'transform' | 'help' @@ -284,20 +289,65 @@ export class Messenger { const tool = ToolUtils.tryFromToolUse(toolUse) if ('type' in tool) { let changeList: Change[] | undefined = undefined + let messageIdToUpdate: string | undefined = undefined + const isReadOrList: boolean = [ToolType.FsRead, ToolType.ListDirectory].includes( + tool.type + ) if (tool.type === ToolType.FsWrite) { session.setShowDiffOnFileWrite(true) changeList = await tool.tool.getDiffChanges() } + if (tool.type === ToolType.FsRead) { + messageIdToUpdate = session.messageIdToUpdate + const input = toolUse.input as unknown as FsReadParams + // Check if this file path is already in the readFiles list + const isFileAlreadyRead = session.readFiles.some( + (file) => file.relativeFilePath === input.path + ) + if (!isFileAlreadyRead) { + session.addToReadFiles({ + relativeFilePath: input?.path, + lineRanges: [{ first: -1, second: -1 }], + }) + } + } else if (tool.type === ToolType.ListDirectory) { + messageIdToUpdate = session.messageIdToUpdateListDirectory + const input = toolUse.input as unknown as ListDirectoryParams + // Check if this folder is already in the readFolders list + const isFolderAlreadyRead = session.readFolders.some( + (folder) => folder.relativeFilePath === input.path + ) + if (!isFolderAlreadyRead) { + session.setReadFolders({ + relativeFilePath: input?.path, + lineRanges: [{ first: -1, second: -1 }], + }) + } + } const validation = ToolUtils.requiresAcceptance(tool) const chatStream = new ChatStream( this, tabID, triggerID, toolUse, + session, + messageIdToUpdate, + true, validation, + isReadOrList, changeList ) await ToolUtils.queueDescription(tool, chatStream) + if (session.messageIdToUpdate === undefined && tool.type === ToolType.FsRead) { + // Store the first messageId in a chain of tool uses + session.setMessageIdToUpdate(toolUse.toolUseId) + } + if ( + session.messageIdToUpdateListDirectory === undefined && + tool.type === ToolType.ListDirectory + ) { + session.setMessageIdToUpdateListDirectory(toolUse.toolUseId) + } getLogger().debug( `SetToolUseWithError: ${toolUse.name}:${toolUse.toolUseId} with no error` ) @@ -541,6 +591,26 @@ export class Messenger { }) } + public sendInitialToolMessage(tabID: string, triggerID: string, toolUseId: string | undefined) { + this.dispatcher.sendChatMessage( + new ChatMessage( + { + message: '', + messageType: 'answer', + followUps: undefined, + followUpsHeader: undefined, + relatedSuggestions: undefined, + triggerID, + messageID: toolUseId ?? 'toolUse', + userIntent: undefined, + codeBlockLanguage: undefined, + contextList: undefined, + }, + tabID + ) + ) + } + public sendErrorMessage( errorMessage: string | undefined, tabID: string, @@ -558,14 +628,66 @@ export class Messenger { ) } + private sendReadAndListDirToolMessage( + toolUse: ToolUse, + session: ChatSession, + tabID: string, + triggerID: string, + messageIdToUpdate?: string + ) { + const contextList = toolUse.name === ToolType.ListDirectory ? session.readFolders : session.readFiles + const isFileRead = toolUse.name === ToolType.FsRead + const items = isFileRead ? session.readFiles : session.readFolders + const itemCount = items.length + + const title = + itemCount < 1 + ? 'Gathering context' + : isFileRead + ? `${itemCount} file${itemCount > 1 ? 's' : ''} read` + : `${itemCount} ${itemCount === 1 ? 'directory' : 'directories'} listed` + + this.dispatcher.sendToolMessage( + new ToolMessage( + { + message: '', + messageType: 'answer-part', + followUps: undefined, + followUpsHeader: undefined, + relatedSuggestions: undefined, + triggerID, + messageID: messageIdToUpdate ?? toolUse?.toolUseId ?? '', + userIntent: undefined, + codeBlockLanguage: undefined, + contextList, + canBeVoted: false, + buttons: undefined, + fullWidth: false, + padding: false, + codeBlockActions: undefined, + rootFolderTitle: title, + }, + tabID + ) + ) + } + public sendPartialToolLog( message: string, tabID: string, triggerID: string, toolUse: ToolUse | undefined, + session: ChatSession, + messageIdToUpdate: string | undefined, validation: CommandValidation, changeList?: Change[] ) { + // Handle read tool and list directory messages + if (toolUse?.name === ToolType.FsRead || toolUse?.name === ToolType.ListDirectory) { + return this.sendReadAndListDirToolMessage(toolUse, session, tabID, triggerID, messageIdToUpdate) + } + + // Handle file write tool, execute bash tool and bash command output log messages const buttons: ChatItemButton[] = [] let header: ChatItemHeader | undefined = undefined if (toolUse?.name === ToolType.ExecuteBash && message.startsWith('```shell')) { @@ -614,14 +736,23 @@ export class Messenger { status: 'clear', icon: 'cancel' as MynahIconsType, }, - { - id: 'accept-code-diff', - status: 'clear', - icon: 'ok' as MynahIconsType, - }, ] + const status: { + icon?: MynahIcons | MynahIconsType + status?: { + status?: Status + icon?: MynahIcons | MynahIconsType + text?: string + } + } = { + status: { + text: 'Accepted', + status: 'success', + }, + } header = { buttons, + ...status, fileList, } } else if (toolUse?.name === ToolType.ListDirectory || toolUse?.name === ToolType.FsRead) { @@ -630,7 +761,7 @@ export class Messenger { { id: 'confirm-tool-use', text: localize('AWS.generic.run', 'Run'), - status: 'main', + status: 'clear', icon: 'play' as MynahIconsType, }, { @@ -641,7 +772,6 @@ export class Messenger { }, ] header = { - icon: 'shell' as MynahIconsType, body: 'shell', buttons, } diff --git a/packages/core/src/codewhispererChat/tools/chatStream.ts b/packages/core/src/codewhispererChat/tools/chatStream.ts index 83a7935bf05..a51aa97aa00 100644 --- a/packages/core/src/codewhispererChat/tools/chatStream.ts +++ b/packages/core/src/codewhispererChat/tools/chatStream.ts @@ -9,6 +9,7 @@ import { Messenger } from '../controllers/chat/messenger/messenger' import { ToolUse } from '@amzn/codewhisperer-streaming' import { CommandValidation } from './executeBash' import { Change } from 'diff' +import { ChatSession } from '../clients/chat/v0/chat' import { i18n } from '../../shared/i18n-helper' /** @@ -23,12 +24,22 @@ export class ChatStream extends Writable { private readonly tabID: string, private readonly triggerID: string, private readonly toolUse: ToolUse | undefined, + private readonly session: ChatSession, + private readonly messageIdToUpdate: string | undefined, + // emitEvent decides to show the streaming message or read/list directory tool message to the user. + private readonly emitEvent: boolean, private readonly validation: CommandValidation, + private readonly isReadorList: boolean, private readonly changeList?: Change[], private readonly logger = getLogger('chatStream') ) { super() - this.logger.debug(`ChatStream created for tabID: ${tabID}, triggerID: ${triggerID}`) + this.logger.debug( + `ChatStream created for tabID: ${tabID}, triggerID: ${triggerID}, readFiles: ${session.readFiles}, emitEvent to mynahUI: ${emitEvent}` + ) + if (!emitEvent) { + return + } if (validation.requiresAcceptance) { this.messenger.sendDirectiveMessage( tabID, @@ -36,18 +47,27 @@ export class ChatStream extends Writable { i18n('AWS.amazonq.chat.directive.runCommandToProceed') ) } - this.messenger.sendInitalStream(tabID, triggerID) + // For FsRead and ListDirectory tools If messageIdToUpdate is undefined, we need to first create an empty message with messageId so it can be updated later + if (isReadorList && !messageIdToUpdate) { + this.messenger.sendInitialToolMessage(tabID, triggerID, toolUse?.toolUseId) + } else { + this.messenger.sendInitalStream(tabID, triggerID) + } } override _write(chunk: Buffer, encoding: BufferEncoding, callback: (error?: Error | null) => void): void { const text = chunk.toString() this.accumulatedLogs += text - this.logger.debug(`ChatStream received chunk: ${text}`) + this.logger.debug( + `ChatStream received chunk: ${text}, emitEvent to mynahUI: ${this.emitEvent}, isReadorList tool: ${this.isReadorList}` + ) this.messenger.sendPartialToolLog( this.accumulatedLogs, this.tabID, this.triggerID, this.toolUse, + this.session, + this.messageIdToUpdate, this.validation, this.changeList ) diff --git a/packages/core/src/codewhispererChat/tools/fsRead.ts b/packages/core/src/codewhispererChat/tools/fsRead.ts index 5dfc30d26f8..5b0011a2167 100644 --- a/packages/core/src/codewhispererChat/tools/fsRead.ts +++ b/packages/core/src/codewhispererChat/tools/fsRead.ts @@ -6,7 +6,6 @@ import * as vscode from 'vscode' import { getLogger } from '../../shared/logger/logger' import fs from '../../shared/fs/fs' import { Writable } from 'stream' -import path from 'path' import { InvokeOutput, OutputKind, sanitizePath, CommandValidation } from './toolShared' import { isInDirectory } from '../../shared/filesystemUtilities' @@ -49,23 +48,7 @@ export class FsRead { } public queueDescription(updates: Writable): void { - const fileName = path.basename(this.fsPath) - const fileUri = vscode.Uri.file(this.fsPath) - updates.write(`Reading file: [${fileName}](${fileUri}), `) - - const [start, end] = this.readRange ?? [] - - if (start && end) { - updates.write(`from line ${start} to ${end}`) - } else if (start) { - if (start > 0) { - updates.write(`from line ${start} to end of file`) - } else { - updates.write(`${start} line from the end of file to end of file`) - } - } else { - updates.write('all lines') - } + updates.write('') updates.end() } diff --git a/packages/core/src/codewhispererChat/tools/listDirectory.ts b/packages/core/src/codewhispererChat/tools/listDirectory.ts index 7a15bd4be8f..a1eb6a37858 100644 --- a/packages/core/src/codewhispererChat/tools/listDirectory.ts +++ b/packages/core/src/codewhispererChat/tools/listDirectory.ts @@ -52,12 +52,12 @@ export class ListDirectory { public queueDescription(updates: Writable): void { const fileName = path.basename(this.fsPath) if (this.maxDepth === undefined) { - updates.write(`Listing directory recursively: ${fileName}`) + updates.write(`Analyzing directories recursively: ${fileName}`) } else if (this.maxDepth === 0) { - updates.write(`Listing directory: ${fileName}`) + updates.write(`Analyzing directory: ${fileName}`) } else { const level = this.maxDepth > 1 ? 'levels' : 'level' - updates.write(`Listing directory: ${fileName} limited to ${this.maxDepth} subfolder ${level}`) + updates.write(`Analyzing directory: ${fileName} limited to ${this.maxDepth} subfolder ${level}`) } updates.end() } diff --git a/packages/core/src/codewhispererChat/tools/tool_index.json b/packages/core/src/codewhispererChat/tools/tool_index.json index 1416036bc3a..0a4d00a2d88 100644 --- a/packages/core/src/codewhispererChat/tools/tool_index.json +++ b/packages/core/src/codewhispererChat/tools/tool_index.json @@ -10,7 +10,7 @@ "type": "string" }, "readRange": { - "description": "Optional parameter when reading files.\n * If none is given, the full file is shown. If provided, the file will be shown in the indicated line number range, e.g. [11, 12] will show lines 11 and 12. Indexing at 1 to start. Setting `[startLine, -1]` shows all lines from `startLine` to the end of the file. If the whole file is too large, try reading 4000 lines at once, for example: after reading [1, 4000], read [4000, 8000] next and repeat.", + "description": "Optional parameter when reading files.\n * If none is given, the full file is shown. If provided, the file will be shown in the indicated line number range, e.g. [11, 12] will show lines 11 and 12. Indexing at 1 to start. Setting `[startLine, -1]` shows all lines from `startLine` to the end of the file. If the whole file is too large, try reading 4000 lines at once, for example: after reading [1, 4000], read [4000, 8000] next and repeat. You should read atleast 250 lines per invocation of the tool. In some cases, if reading a range of lines results in too many invocations instead attempt to read 4000 lines.", "items": { "type": "integer" }, diff --git a/packages/core/src/codewhispererChat/view/connector/connector.ts b/packages/core/src/codewhispererChat/view/connector/connector.ts index 1f10eeb3134..a718762d8af 100644 --- a/packages/core/src/codewhispererChat/view/connector/connector.ts +++ b/packages/core/src/codewhispererChat/view/connector/connector.ts @@ -404,6 +404,10 @@ export class ChatMessage extends UiMessage { } } +export class ToolMessage extends ChatMessage { + override type = 'toolMessage' +} + export interface FollowUp { readonly type: string readonly pillText: string @@ -458,6 +462,10 @@ export class AppToWebViewMessageDispatcher { this.appsToWebViewMessagePublisher.publish(message) } + public sendToolMessage(message: ToolMessage) { + this.appsToWebViewMessagePublisher.publish(message) + } + public sendEditorContextCommandMessage(message: EditorContextCommandMessage) { this.appsToWebViewMessagePublisher.publish(message) }