@@ -11,6 +11,7 @@ import {
1111 COPY_TO_CLIPBOARD ,
1212 AuthFollowUpType ,
1313 DISCLAIMER_ACKNOWLEDGED ,
14+ UiMessageResultParams ,
1415} from '@aws/chat-client-ui-types'
1516import {
1617 ChatResult ,
@@ -21,30 +22,42 @@ import {
2122 QuickActionResult ,
2223 QuickActionParams ,
2324 insertToCursorPositionNotificationType ,
25+ ErrorCodes ,
26+ ResponseError ,
27+ openTabRequestType ,
28+ getSerializedChatRequestType ,
29+ listConversationsRequestType ,
30+ conversationClickRequestType ,
31+ ShowSaveFileDialogRequestType ,
32+ ShowSaveFileDialogParams ,
33+ LSPErrorCodes ,
34+ tabBarActionRequestType ,
2435} from '@aws/language-server-runtimes/protocol'
2536import { v4 as uuidv4 } from 'uuid'
2637import * as vscode from 'vscode'
27- import { Disposable , LanguageClient , Position , State , TextDocumentIdentifier } from 'vscode-languageclient'
38+ import { Disposable , LanguageClient , Position , TextDocumentIdentifier } from 'vscode-languageclient'
2839import * as jose from 'jose'
2940import { AmazonQChatViewProvider } from './webviewProvider'
3041import { AuthUtil } from 'aws-core-vscode/codewhisperer'
3142import { AmazonQPromptSettings , messages } from 'aws-core-vscode/shared'
3243
3344export function registerLanguageServerEventListener ( languageClient : LanguageClient , provider : AmazonQChatViewProvider ) {
34- languageClient . onDidChangeState ( ( { oldState, newState } ) => {
35- if ( oldState === State . Starting && newState === State . Running ) {
36- languageClient . info (
37- 'Language client received initializeResult from server:' ,
38- JSON . stringify ( languageClient . initializeResult )
39- )
45+ languageClient . info (
46+ 'Language client received initializeResult from server:' ,
47+ JSON . stringify ( languageClient . initializeResult )
48+ )
4049
41- const chatOptions = languageClient . initializeResult ?. awsServerCapabilities ?. chatOptions
50+ const chatOptions = languageClient . initializeResult ?. awsServerCapabilities ?. chatOptions
4251
43- void provider . webview ?. postMessage ( {
44- command : CHAT_OPTIONS ,
45- params : chatOptions ,
46- } )
47- }
52+ // Enable the history/export feature flags
53+ chatOptions . history = true
54+ chatOptions . export = true
55+
56+ provider . onDidResolveWebview ( ( ) => {
57+ void provider . webview ?. postMessage ( {
58+ command : CHAT_OPTIONS ,
59+ params : chatOptions ,
60+ } )
4861 } )
4962
5063 languageClient . onTelemetry ( ( e ) => {
@@ -60,6 +73,7 @@ export function registerMessageListeners(
6073 provider . webview ?. onDidReceiveMessage ( async ( message ) => {
6174 languageClient . info ( `[VSCode Client] Received ${ JSON . stringify ( message ) } from chat` )
6275
76+ const webview = provider . webview
6377 switch ( message . command ) {
6478 case COPY_TO_CLIPBOARD :
6579 languageClient . info ( '[VSCode Client] Copy to clipboard event received' )
@@ -172,6 +186,11 @@ export function registerMessageListeners(
172186 )
173187 break
174188 }
189+ case listConversationsRequestType . method :
190+ case conversationClickRequestType . method :
191+ case tabBarActionRequestType . method :
192+ await resolveChatResponse ( message . command , message . params , languageClient , webview )
193+ break
175194 case followUpClickNotificationType . method :
176195 if ( ! isValidAuthFollowUpType ( message . params . followUp . type ) ) {
177196 languageClient . sendNotification ( followUpClickNotificationType . method , message . params )
@@ -184,6 +203,89 @@ export function registerMessageListeners(
184203 break
185204 }
186205 } , undefined )
206+
207+ const registerHandlerWithResponseRouter = ( command : string ) => {
208+ const handler = async ( params : any , _ : any ) => {
209+ const mapErrorType = ( type : string | undefined ) : number => {
210+ switch ( type ) {
211+ case 'InvalidRequest' :
212+ return ErrorCodes . InvalidRequest
213+ case 'InternalError' :
214+ return ErrorCodes . InternalError
215+ case 'UnknownError' :
216+ default :
217+ return ErrorCodes . UnknownErrorCode
218+ }
219+ }
220+ const requestId = uuidv4 ( )
221+
222+ void provider . webview ?. postMessage ( {
223+ requestId : requestId ,
224+ command : command ,
225+ params : params ,
226+ } )
227+ const responsePromise = new Promise < UiMessageResultParams | undefined > ( ( resolve , reject ) => {
228+ const timeout = setTimeout ( ( ) => {
229+ disposable ?. dispose ( )
230+ reject ( new Error ( 'Request timed out' ) )
231+ } , 30000 )
232+
233+ const disposable = provider . webview ?. onDidReceiveMessage ( ( message : any ) => {
234+ if ( message . requestId === requestId ) {
235+ clearTimeout ( timeout )
236+ disposable ?. dispose ( )
237+ resolve ( message . params )
238+ }
239+ } )
240+ } )
241+
242+ const result = await responsePromise
243+
244+ if ( result ?. success ) {
245+ return result . result
246+ } else {
247+ return new ResponseError (
248+ mapErrorType ( result ?. error . type ) ,
249+ result ?. error . message ?? 'No response from client'
250+ )
251+ }
252+ }
253+
254+ languageClient . onRequest ( command , handler )
255+ }
256+
257+ registerHandlerWithResponseRouter ( openTabRequestType . method )
258+ registerHandlerWithResponseRouter ( getSerializedChatRequestType . method )
259+
260+ languageClient . onRequest ( ShowSaveFileDialogRequestType . method , async ( params : ShowSaveFileDialogParams ) => {
261+ const filters : Record < string , string [ ] > = { }
262+ const formatMappings = [
263+ { format : 'markdown' , key : 'Markdown' , extensions : [ 'md' ] } ,
264+ { format : 'html' , key : 'HTML' , extensions : [ 'html' ] } ,
265+ ]
266+
267+ for ( const format of params . supportedFormats ?? [ ] ) {
268+ const mapping = formatMappings . find ( ( m ) => m . format === format )
269+ if ( mapping ) {
270+ filters [ mapping . key ] = mapping . extensions
271+ }
272+ }
273+
274+ const saveAtUri = params . defaultUri ? vscode . Uri . parse ( params . defaultUri ) : vscode . Uri . file ( 'export-chat.md' )
275+ const targetUri = await vscode . window . showSaveDialog ( {
276+ filters,
277+ defaultUri : saveAtUri ,
278+ title : 'Export' ,
279+ } )
280+
281+ if ( ! targetUri ) {
282+ return new ResponseError ( LSPErrorCodes . RequestFailed , 'Export failed' )
283+ }
284+
285+ return {
286+ targetUri : targetUri . toString ( ) ,
287+ }
288+ } )
187289}
188290
189291function isServerEvent ( command : string ) {
@@ -258,3 +360,16 @@ async function handleCompleteResult<T>(
258360 } )
259361 disposable . dispose ( )
260362}
363+
364+ async function resolveChatResponse (
365+ requestMethod : string ,
366+ params : any ,
367+ languageClient : LanguageClient ,
368+ webview : vscode . Webview | undefined
369+ ) {
370+ const result = await languageClient . sendRequest ( requestMethod , params )
371+ void webview ?. postMessage ( {
372+ command : requestMethod ,
373+ params : result ,
374+ } )
375+ }
0 commit comments