export function registerChat()

in client/vscode/src/chatActivation.ts [49:381]


export function registerChat(
    languageClient: LanguageClient,
    extensionUri: Uri,
    encryptionKey?: Buffer,
    agenticMode?: boolean
) {
    const webviewInitialized: Promise<Webview> = new Promise(resolveWebview => {
        const provider = {
            resolveWebviewView(webviewView: WebviewView) {
                webviewView.webview.options = {
                    enableScripts: true,
                    localResourceRoots: [Uri.joinPath(extensionUri, 'build')],
                }

                resolveWebview(webviewView.webview)

                webviewView.webview.onDidReceiveMessage(async message => {
                    languageClient.info(`[VSCode Client]  Received ${JSON.stringify(message)} from chat`)

                    switch (message.command) {
                        case COPY_TO_CLIPBOARD:
                            languageClient.info('[VSCode Client] Copy to clipboard event received')
                            break
                        case INSERT_TO_CURSOR_POSITION: {
                            const editor = window.activeTextEditor
                            let textDocument: TextDocumentIdentifier | undefined = undefined
                            let cursorPosition: Position | undefined = undefined
                            if (editor) {
                                cursorPosition = editor.selection.active
                                textDocument = { uri: editor.document.uri.toString() }
                            }

                            languageClient.sendNotification(insertToCursorPositionNotificationType, {
                                ...message.params,
                                cursorPosition,
                                textDocument,
                            })
                            break
                        }
                        case AUTH_FOLLOW_UP_CLICKED:
                            languageClient.info('[VSCode Client] AuthFollowUp clicked')
                            break
                        case chatRequestType.method: {
                            const partialResultToken = uuidv4()
                            const chatDisposable = languageClient.onProgress(
                                chatRequestType,
                                partialResultToken,
                                partialResult =>
                                    handlePartialResult<ChatResult>(
                                        partialResult,
                                        encryptionKey,
                                        webviewView.webview,
                                        message.params.tabId
                                    )
                            )

                            const editor =
                                window.activeTextEditor ||
                                window.visibleTextEditors.find(editor => editor.document.languageId != 'Log')
                            if (editor) {
                                message.params.cursorPosition = [editor.selection.active]
                                message.params.textDocument = { uri: editor.document.uri.toString() }
                            }

                            const chatRequest = await encryptRequest<ChatParams>(message.params, encryptionKey)
                            const chatResult = await languageClient.sendRequest(chatRequestType, {
                                ...chatRequest,
                                partialResultToken,
                            })
                            handleCompleteResult<ChatResult>(
                                chatResult,
                                encryptionKey,
                                webviewView.webview,
                                message.params.tabId,
                                chatDisposable
                            )
                            break
                        }
                        case quickActionRequestType.method: {
                            const quickActionPartialResultToken = uuidv4()
                            const quickActionDisposable = languageClient.onProgress(
                                quickActionRequestType,
                                quickActionPartialResultToken,
                                partialResult =>
                                    handlePartialResult<QuickActionResult>(
                                        partialResult,
                                        encryptionKey,
                                        webviewView.webview,
                                        message.params.tabId
                                    )
                            )

                            const quickActionRequest = await encryptRequest<QuickActionParams>(
                                message.params,
                                encryptionKey
                            )
                            const quickActionResult = await languageClient.sendRequest(quickActionRequestType, {
                                ...quickActionRequest,
                                partialResultToken: quickActionPartialResultToken,
                            })
                            handleCompleteResult<ChatResult>(
                                quickActionResult,
                                encryptionKey,
                                webviewView.webview,
                                message.params.tabId,
                                quickActionDisposable
                            )
                            break
                        }
                        case listConversationsRequestType.method:
                            await handleRequest(
                                languageClient,
                                message.params,
                                webviewView,
                                listConversationsRequestType.method
                            )
                            break
                        case conversationClickRequestType.method:
                            await handleRequest(
                                languageClient,
                                message.params,
                                webviewView,
                                conversationClickRequestType.method
                            )
                            break
                        case tabBarActionRequestType.method:
                            await handleRequest(
                                languageClient,
                                message.params,
                                webviewView,
                                tabBarActionRequestType.method
                            )
                            break
                        case buttonClickRequestType.method:
                            await handleRequest(
                                languageClient,
                                message.params,
                                webviewView,
                                buttonClickRequestType.method
                            )
                            break
                        case followUpClickNotificationType.method:
                            if (!isValidAuthFollowUpType(message.params.followUp.type))
                                languageClient.sendNotification(followUpClickNotificationType, message.params)
                            break
                        default:
                            if (isServerEvent(message.command))
                                languageClient.sendNotification(message.command, message.params)
                            else languageClient.info(`[VSCode Client]  Unhandled command: ${message.command}`)
                            break
                    }
                }, undefined)

                languageClient.onNotification(chatOptionsUpdateType, params => {
                    webviewView.webview.postMessage({
                        command: chatOptionsUpdateType.method,
                        params: params,
                    })
                })

                languageClient.onNotification(contextCommandsNotificationType, params => {
                    webviewView.webview.postMessage({
                        command: contextCommandsNotificationType.method,
                        params: params,
                    })
                })

                languageClient.onNotification(chatUpdateNotificationType, params => {
                    webviewView.webview.postMessage({
                        command: chatUpdateNotificationType.method,
                        params: params,
                    })
                })

                const registerHandlerWithResponseRouter = (command: string) => {
                    const handler = async (params: any, _: any) => {
                        const mapErrorType = (type: string | undefined): number => {
                            switch (type) {
                                case 'InvalidRequest':
                                    return ErrorCodes.InvalidRequest
                                case 'InternalError':
                                    return ErrorCodes.InternalError
                                case 'UnknownError':
                                default:
                                    return ErrorCodes.UnknownErrorCode
                            }
                        }
                        const requestId = uuidv4()

                        webviewView.webview.postMessage({
                            requestId: requestId,
                            command: command,
                            params: params,
                        })
                        const responsePromise = new Promise<UiMessageResultParams | undefined>((resolve, reject) => {
                            const timeout = setTimeout(() => {
                                disposable.dispose()
                                reject(new Error('Request timed out'))
                            }, 30000)

                            const disposable = webviewView.webview.onDidReceiveMessage((message: any) => {
                                if (message.requestId === requestId) {
                                    clearTimeout(timeout)
                                    disposable.dispose()
                                    resolve(message.params)
                                }
                            })
                        })

                        const result = await responsePromise

                        if (result?.success) {
                            return result.result
                        } else {
                            return new ResponseError(
                                mapErrorType(result?.error.type),
                                result?.error.message ?? 'No response from client'
                            )
                        }
                    }

                    languageClient.onRequest(command, handler)
                }

                registerHandlerWithResponseRouter(openTabRequestType.method)
                registerHandlerWithResponseRouter(getSerializedChatRequestType.method)

                webviewView.webview.html = getWebviewContent(webviewView.webview, extensionUri, !!agenticMode)

                registerGenericCommand('aws.sample-vscode-ext-amazonq.explainCode', 'Explain', webviewView.webview)
                registerGenericCommand('aws.sample-vscode-ext-amazonq.refactorCode', 'Refactor', webviewView.webview)
                registerGenericCommand('aws.sample-vscode-ext-amazonq.fixCode', 'Fix', webviewView.webview)
                registerGenericCommand('aws.sample-vscode-ext-amazonq.optimizeCode', 'Optimize', webviewView.webview)

                commands.registerCommand('aws.sample-vscode-ext-amazonq.sendToPrompt', data => {
                    const triggerType = getCommandTriggerType(data)
                    const selection = getSelectedText()

                    webviewView.webview.postMessage({
                        command: 'sendToPrompt',
                        params: { selection: selection, triggerType },
                    })
                })

                commands.registerCommand('aws.sample-vscode-ext-amazonq.openTab', data => {
                    webviewView.webview.postMessage({
                        command: 'aws/chat/openTab',
                        params: {},
                    })
                })
            },
        }

        // Register the provider for the auxiliary bar
        window.registerWebviewViewProvider('amazonq.chat', provider, {
            webviewOptions: { retainContextWhenHidden: true },
        })
    })

    // Listen for Initialize handshake from LSP server to register quick actions dynamically
    languageClient.onDidChangeState(({ oldState, newState }) => {
        if (oldState === State.Starting && newState === State.Running) {
            languageClient.info(
                'Language client received initializeResult from server:',
                JSON.stringify(languageClient.initializeResult)
            )

            const chatOptions = languageClient.initializeResult?.awsServerCapabilities?.chatOptions

            // We can only initialize the chat once the webview is initialized
            webviewInitialized.then(webview => {
                webview.postMessage({
                    command: CHAT_OPTIONS,
                    params: chatOptions,
                })
            })
        }
    })

    languageClient.onTelemetry(e => {
        languageClient.info(`[VSCode Client] Received telemetry event from server ${JSON.stringify(e)}`)
    })

    languageClient.onRequest(ShowSaveFileDialogRequestType.method, async (params: ShowSaveFileDialogParams) => {
        // Show native Save File dialog
        const filters: Record<string, string[]> = {}
        const formatMappings = [
            { format: 'markdown', key: 'Markdown', extensions: ['md'] },
            { format: 'html', key: 'HTML', extensions: ['html'] },
        ]
        params.supportedFormats?.forEach(format => {
            const mapping = formatMappings.find(m => m.format === format)
            if (mapping) {
                filters[mapping.key] = mapping.extensions
            }
        })

        const saveAtUri = params.defaultUri ? vscode.Uri.parse(params.defaultUri) : vscode.Uri.file('export-chat.md')
        const targetUri = await vscode.window.showSaveDialog({
            filters,
            defaultUri: saveAtUri,
            title: 'Export',
        })

        // Send message to Chat Client to do Conversation Export
        if (!targetUri) {
            return new ResponseError(LSPErrorCodes.RequestFailed, 'Export failed')
        }

        return {
            targetUri: targetUri.toString(),
        }
    })

    commands.registerCommand('aws.sample-vscode-ext-amazonq.sendInlineChat', async () => {
        const params = getCurrentEditorParams()
        languageClient.info(`Logging request for inline chat ${JSON.stringify(params)}`)
        if (!params) {
            languageClient.warn(`Invalid request params for inline chat`)
            return
        }
        try {
            const inlineChatRequest = await encryptRequest<InlineChatParams>(params, encryptionKey)
            const response = await languageClient.sendRequest(inlineChatRequestType, inlineChatRequest)
            const result: InlineChatResult = response as InlineChatResult
            const decryptedMessage =
                typeof result === 'string' && encryptionKey ? await decodeRequest(result, encryptionKey) : result
            languageClient.info(`Logging response for inline chat ${JSON.stringify(decryptedMessage)}`)
        } catch (e) {
            languageClient.info(`Logging error for inline chat ${JSON.stringify(e)}`)
        }
    })
}