Skip to content

Commit 022629d

Browse files
committed
Gracefully handle a lynx agent not being enabled
1 parent 65ff309 commit 022629d

1 file changed

Lines changed: 114 additions & 104 deletions

File tree

packages/vscode/src/extension.ts

Lines changed: 114 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -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

399409
export function deactivate(): void {

0 commit comments

Comments
 (0)