@@ -284,116 +284,126 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
284284 ) ;
285285 }
286286
287- if ( agentEnabled ) {
288- const chatHandler : vscode . ChatRequestHandler = async ( request , context , response , token ) => {
289- let threadId : string ;
290- if ( context . history . length === 0 ) {
291- threadId = crypto . randomUUID ( ) ;
292- } else {
293- // check if the last message has a threadId in its metadata, if so use that, otherwise generate a new one
294- let lastResponseTurn : vscode . ChatResponseTurn | undefined ;
295- for ( let i = context . history . length - 1 ; i >= 0 ; i -- ) {
296- const turn = context . history [ i ] ;
297- if ( turn instanceof vscode . ChatResponseTurn && turn . participant === PARTICIPANT_ID ) {
298- lastResponseTurn = turn ;
299- break ;
300- }
287+ const chatHandler : vscode . ChatRequestHandler = async ( request , context , response , token ) => {
288+ if ( ! agentEnabled || agentProvider == null ) {
289+ response . markdown (
290+ 'The Lynx agent is not enabled. To use `@lynx`, update your settings:\n\n' +
291+ '- Set `lynx.agent.enabled` to `true`\n' +
292+ '- Set `lynx.agent.provider` to `anthropic`, `openai`, or `google-genai`\n' +
293+ "- Provide an API key via `lynx.agent.apiKey` (or the provider's environment variable: " +
294+ '`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or `GOOGLE_API_KEY`)\n\n' +
295+ 'Then reload the window for the changes to take effect.' ,
296+ ) ;
297+ return { } ;
298+ }
299+
300+ let threadId : string ;
301+ if ( context . history . length === 0 ) {
302+ threadId = crypto . randomUUID ( ) ;
303+ } else {
304+ // check if the last message has a threadId in its metadata, if so use that, otherwise generate a new one
305+ let lastResponseTurn : vscode . ChatResponseTurn | undefined ;
306+ for ( let i = context . history . length - 1 ; i >= 0 ; i -- ) {
307+ const turn = context . history [ i ] ;
308+ if ( turn instanceof vscode . ChatResponseTurn && turn . participant === PARTICIPANT_ID ) {
309+ lastResponseTurn = turn ;
310+ break ;
301311 }
302- threadId = ( lastResponseTurn ?. result . metadata ?. threadId as string | undefined ) ?? crypto . randomUUID ( ) ;
303312 }
304- const events$ = workspace . streamAgent ( request . prompt , threadId ) ;
305-
306- return new Promise < vscode . ChatResult > ( ( resolve ) => {
307- let todoCounter = 0 ;
308- let currentTodoCallId : string | undefined ;
309- let lastTodoData : vscode . ChatTodoToolInvocationData | undefined ;
310-
311- const hidePreviousTodos = ( ) => {
312- if ( currentTodoCallId ) {
313- const hide = new vscode . ChatToolInvocationPart ( 'todos' , currentTodoCallId ) ;
314- hide . enablePartialUpdate = true ;
315- hide . isComplete = true ;
316- hide . presentation = 'hidden' ;
317- response . push ( hide ) ;
318- }
319- } ;
320-
321- const completeTodos = ( ) => {
322- if ( currentTodoCallId ) {
323- const part = new vscode . ChatToolInvocationPart ( 'todos' , currentTodoCallId ) ;
324- part . enablePartialUpdate = true ;
325- part . isComplete = true ;
326- part . toolSpecificData = lastTodoData ;
327- response . push ( part ) ;
328- }
329- } ;
330-
331- const subscription = events$ . subscribe ( {
332- next ( event ) {
333- switch ( event . type ) {
334- case AgentEventType . Started :
335- response . progress ( 'Thinking' ) ;
336- break ;
337- case AgentEventType . Message :
338- response . markdown ( event . content ) ;
339- break ;
340- case AgentEventType . ToolCall :
341- response . progress ( `Running ${ event . toolName } ` ) ;
342- break ;
343- case AgentEventType . Todos : {
344- // Workaround for VS Code bug: toolSpecificData updates don't trigger
345- // UI re-renders, so we hide the old part and create a new one.
346- hidePreviousTodos ( ) ;
347- todoCounter ++ ;
348- const callId = `lynx-todos-${ todoCounter . toString ( ) } ` ;
349- currentTodoCallId = callId ;
350- lastTodoData = {
351- todoList : event . todos . map ( ( t , i ) => ( {
352- id : i ,
353- title : t . content ,
354- status : toVscodeTodoStatus ( t . status ) ,
355- } ) ) ,
356- } ;
357- const part = new vscode . ChatToolInvocationPart ( 'todos' , callId ) ;
358- part . enablePartialUpdate = true ;
359- part . isComplete = false ;
360- part . toolSpecificData = lastTodoData ;
361- response . push ( part ) ;
362- break ;
363- }
364- case AgentEventType . Error :
365- completeTodos ( ) ;
366- resolve ( { metadata : { threadId } , errorDetails : { message : event . error } } ) ;
367- break ;
368- case AgentEventType . Completed :
369- completeTodos ( ) ;
370- resolve ( { metadata : { threadId } } ) ;
371- break ;
313+ threadId = ( lastResponseTurn ?. result . metadata ?. threadId as string | undefined ) ?? crypto . randomUUID ( ) ;
314+ }
315+ const events$ = workspace . streamAgent ( request . prompt , threadId ) ;
316+
317+ return new Promise < vscode . ChatResult > ( ( resolve ) => {
318+ let todoCounter = 0 ;
319+ let currentTodoCallId : string | undefined ;
320+ let lastTodoData : vscode . ChatTodoToolInvocationData | undefined ;
321+
322+ const hidePreviousTodos = ( ) => {
323+ if ( currentTodoCallId ) {
324+ const hide = new vscode . ChatToolInvocationPart ( 'todos' , currentTodoCallId ) ;
325+ hide . enablePartialUpdate = true ;
326+ hide . isComplete = true ;
327+ hide . presentation = 'hidden' ;
328+ response . push ( hide ) ;
329+ }
330+ } ;
331+
332+ const completeTodos = ( ) => {
333+ if ( currentTodoCallId ) {
334+ const part = new vscode . ChatToolInvocationPart ( 'todos' , currentTodoCallId ) ;
335+ part . enablePartialUpdate = true ;
336+ part . isComplete = true ;
337+ part . toolSpecificData = lastTodoData ;
338+ response . push ( part ) ;
339+ }
340+ } ;
341+
342+ const subscription = events$ . subscribe ( {
343+ next ( event ) {
344+ switch ( event . type ) {
345+ case AgentEventType . Started :
346+ response . progress ( 'Thinking' ) ;
347+ break ;
348+ case AgentEventType . Message :
349+ response . markdown ( event . content ) ;
350+ break ;
351+ case AgentEventType . ToolCall :
352+ response . progress ( `Running ${ event . toolName } ` ) ;
353+ break ;
354+ case AgentEventType . Todos : {
355+ // Workaround for VS Code bug: toolSpecificData updates don't trigger
356+ // UI re-renders, so we hide the old part and create a new one.
357+ hidePreviousTodos ( ) ;
358+ todoCounter ++ ;
359+ const callId = `lynx-todos-${ todoCounter . toString ( ) } ` ;
360+ currentTodoCallId = callId ;
361+ lastTodoData = {
362+ todoList : event . todos . map ( ( t , i ) => ( {
363+ id : i ,
364+ title : t . content ,
365+ status : toVscodeTodoStatus ( t . status ) ,
366+ } ) ) ,
367+ } ;
368+ const part = new vscode . ChatToolInvocationPart ( 'todos' , callId ) ;
369+ part . enablePartialUpdate = true ;
370+ part . isComplete = false ;
371+ part . toolSpecificData = lastTodoData ;
372+ response . push ( part ) ;
373+ break ;
372374 }
373- } ,
374- error ( err ) {
375- completeTodos ( ) ;
376- resolve ( {
377- metadata : { threadId } ,
378- errorDetails : { message : err instanceof Error ? err . message : String ( err ) } ,
379- } ) ;
380- } ,
381- } ) ;
382-
383- const cancellationListener = token . onCancellationRequested ( ( ) => {
375+ case AgentEventType . Error :
376+ completeTodos ( ) ;
377+ resolve ( { metadata : { threadId } , errorDetails : { message : event . error } } ) ;
378+ break ;
379+ case AgentEventType . Completed :
380+ completeTodos ( ) ;
381+ resolve ( { metadata : { threadId } } ) ;
382+ break ;
383+ }
384+ } ,
385+ error ( err ) {
384386 completeTodos ( ) ;
385- subscription . unsubscribe ( ) ;
386- resolve ( { metadata : { threadId } } ) ;
387- } ) ;
388- subscription . add ( ( ) => {
389- cancellationListener . dispose ( ) ;
390- } ) ;
387+ resolve ( {
388+ metadata : { threadId } ,
389+ errorDetails : { message : err instanceof Error ? err . message : String ( err ) } ,
390+ } ) ;
391+ } ,
391392 } ) ;
392- } ;
393393
394- const participant = vscode . chat . createChatParticipant ( PARTICIPANT_ID , chatHandler ) ;
395- context . subscriptions . push ( participant ) ;
396- }
394+ const cancellationListener = token . onCancellationRequested ( ( ) => {
395+ completeTodos ( ) ;
396+ subscription . unsubscribe ( ) ;
397+ resolve ( { metadata : { threadId } } ) ;
398+ } ) ;
399+ subscription . add ( ( ) => {
400+ cancellationListener . dispose ( ) ;
401+ } ) ;
402+ } ) ;
403+ } ;
404+
405+ const participant = vscode . chat . createChatParticipant ( PARTICIPANT_ID , chatHandler ) ;
406+ context . subscriptions . push ( participant ) ;
397407}
398408
399409export function deactivate ( ) : void {
0 commit comments