@@ -11,6 +11,7 @@ import {
11
11
COPY_TO_CLIPBOARD ,
12
12
AuthFollowUpType ,
13
13
DISCLAIMER_ACKNOWLEDGED ,
14
+ UiMessageResultParams ,
14
15
} from '@aws/chat-client-ui-types'
15
16
import {
16
17
ChatResult ,
@@ -21,30 +22,42 @@ import {
21
22
QuickActionResult ,
22
23
QuickActionParams ,
23
24
insertToCursorPositionNotificationType ,
25
+ ErrorCodes ,
26
+ ResponseError ,
27
+ openTabRequestType ,
28
+ getSerializedChatRequestType ,
29
+ listConversationsRequestType ,
30
+ conversationClickRequestType ,
31
+ ShowSaveFileDialogRequestType ,
32
+ ShowSaveFileDialogParams ,
33
+ LSPErrorCodes ,
34
+ tabBarActionRequestType ,
24
35
} from '@aws/language-server-runtimes/protocol'
25
36
import { v4 as uuidv4 } from 'uuid'
26
37
import * as vscode from 'vscode'
27
- import { Disposable , LanguageClient , Position , State , TextDocumentIdentifier } from 'vscode-languageclient'
38
+ import { Disposable , LanguageClient , Position , TextDocumentIdentifier } from 'vscode-languageclient'
28
39
import * as jose from 'jose'
29
40
import { AmazonQChatViewProvider } from './webviewProvider'
30
41
import { AuthUtil } from 'aws-core-vscode/codewhisperer'
31
42
import { AmazonQPromptSettings , messages } from 'aws-core-vscode/shared'
32
43
33
44
export 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
+ )
40
49
41
- const chatOptions = languageClient . initializeResult ?. awsServerCapabilities ?. chatOptions
50
+ const chatOptions = languageClient . initializeResult ?. awsServerCapabilities ?. chatOptions
42
51
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
+ } )
48
61
} )
49
62
50
63
languageClient . onTelemetry ( ( e ) => {
@@ -60,6 +73,7 @@ export function registerMessageListeners(
60
73
provider . webview ?. onDidReceiveMessage ( async ( message ) => {
61
74
languageClient . info ( `[VSCode Client] Received ${ JSON . stringify ( message ) } from chat` )
62
75
76
+ const webview = provider . webview
63
77
switch ( message . command ) {
64
78
case COPY_TO_CLIPBOARD :
65
79
languageClient . info ( '[VSCode Client] Copy to clipboard event received' )
@@ -172,6 +186,11 @@ export function registerMessageListeners(
172
186
)
173
187
break
174
188
}
189
+ case listConversationsRequestType . method :
190
+ case conversationClickRequestType . method :
191
+ case tabBarActionRequestType . method :
192
+ await resolveChatResponse ( message . command , message . params , languageClient , webview )
193
+ break
175
194
case followUpClickNotificationType . method :
176
195
if ( ! isValidAuthFollowUpType ( message . params . followUp . type ) ) {
177
196
languageClient . sendNotification ( followUpClickNotificationType . method , message . params )
@@ -184,6 +203,89 @@ export function registerMessageListeners(
184
203
break
185
204
}
186
205
} , 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
+ } )
187
289
}
188
290
189
291
function isServerEvent ( command : string ) {
@@ -258,3 +360,16 @@ async function handleCompleteResult<T>(
258
360
} )
259
361
disposable . dispose ( )
260
362
}
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