@lvce-editor/chat-view 6.6.0 → 6.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1492,6 +1492,15 @@ const backToChats = () => {
1492
1492
  const settings = () => {
1493
1493
  return i18nString('Settings');
1494
1494
  };
1495
+ const loginToBackend = () => {
1496
+ return i18nString('Login to backend');
1497
+ };
1498
+ const logoutFromBackend = () => {
1499
+ return i18nString('Logout from backend');
1500
+ };
1501
+ const loggingInToBackend = () => {
1502
+ return i18nString('Logging in to backend');
1503
+ };
1495
1504
  const chatFocusMode = () => {
1496
1505
  return i18nString('Switch to chat focus mode');
1497
1506
  };
@@ -1651,6 +1660,12 @@ const createDefaultState = () => {
1651
1660
  return {
1652
1661
  aiSessionTitleGenerationEnabled: false,
1653
1662
  assetDir: '',
1663
+ authAccessToken: '',
1664
+ authEnabled: false,
1665
+ authErrorMessage: '',
1666
+ authRefreshToken: '',
1667
+ authStatus: 'signed-out',
1668
+ backendUrl: '',
1654
1669
  chatListScrollTop: 0,
1655
1670
  chatMessageFontFamily: 'system-ui',
1656
1671
  chatMessageFontSize,
@@ -1699,6 +1714,7 @@ const createDefaultState = () => {
1699
1714
  name: '_blank',
1700
1715
  uri: ''
1701
1716
  }],
1717
+ questionToolEnabled: false,
1702
1718
  renamingSessionId: '',
1703
1719
  selectedModelId: defaultModelId,
1704
1720
  selectedProjectId: defaultProjectId,
@@ -1714,11 +1730,14 @@ const createDefaultState = () => {
1714
1730
  tokensUsed: 0,
1715
1731
  uid: 0,
1716
1732
  usageOverviewEnabled: false,
1717
- useChatCoordinatorWorker: true,
1733
+ useChatCoordinatorWorker: false,
1718
1734
  useChatMathWorker: true,
1719
1735
  useChatNetworkWorkerForRequests: false,
1720
- useChatToolWorker: false,
1736
+ useChatToolWorker: true,
1721
1737
  useMockApi: false,
1738
+ userName: '',
1739
+ userSubscriptionPlan: '',
1740
+ userUsedTokens: 0,
1722
1741
  viewMode: 'list',
1723
1742
  voiceDictationEnabled: false,
1724
1743
  warningCount: 0,
@@ -2522,7 +2541,7 @@ const isEqualProjectExpandedIds = (a, b) => {
2522
2541
  return true;
2523
2542
  };
2524
2543
  const isEqual = (oldState, newState) => {
2525
- return oldState.composerDropActive === newState.composerDropActive && oldState.composerDropEnabled === newState.composerDropEnabled && oldState.composerValue === newState.composerValue && oldState.initial === newState.initial && isEqualProjectExpandedIds(oldState.projectExpandedIds, newState.projectExpandedIds) && oldState.projectListScrollTop === newState.projectListScrollTop && oldState.renamingSessionId === newState.renamingSessionId && oldState.selectedModelId === newState.selectedModelId && oldState.selectedProjectId === newState.selectedProjectId && oldState.selectedSessionId === newState.selectedSessionId && oldState.sessions === newState.sessions && oldState.tokensMax === newState.tokensMax && oldState.tokensUsed === newState.tokensUsed && oldState.usageOverviewEnabled === newState.usageOverviewEnabled && oldState.useChatMathWorker === newState.useChatMathWorker && oldState.viewMode === newState.viewMode && oldState.voiceDictationEnabled === newState.voiceDictationEnabled;
2544
+ return oldState.authEnabled === newState.authEnabled && oldState.authErrorMessage === newState.authErrorMessage && oldState.authStatus === newState.authStatus && oldState.composerDropActive === newState.composerDropActive && oldState.composerDropEnabled === newState.composerDropEnabled && oldState.composerValue === newState.composerValue && oldState.initial === newState.initial && isEqualProjectExpandedIds(oldState.projectExpandedIds, newState.projectExpandedIds) && oldState.projectListScrollTop === newState.projectListScrollTop && oldState.renamingSessionId === newState.renamingSessionId && oldState.selectedModelId === newState.selectedModelId && oldState.selectedProjectId === newState.selectedProjectId && oldState.selectedSessionId === newState.selectedSessionId && oldState.sessions === newState.sessions && oldState.tokensMax === newState.tokensMax && oldState.tokensUsed === newState.tokensUsed && oldState.usageOverviewEnabled === newState.usageOverviewEnabled && oldState.useChatMathWorker === newState.useChatMathWorker && oldState.viewMode === newState.viewMode && oldState.voiceDictationEnabled === newState.voiceDictationEnabled;
2526
2545
  };
2527
2546
 
2528
2547
  const diffScrollTop = (oldState, newState) => {
@@ -2567,6 +2586,20 @@ const diff2 = uid => {
2567
2586
  return result;
2568
2587
  };
2569
2588
 
2589
+ const getAuthState = state => {
2590
+ return {
2591
+ authAccessToken: state.authAccessToken,
2592
+ authEnabled: state.authEnabled,
2593
+ authErrorMessage: state.authErrorMessage,
2594
+ authRefreshToken: state.authRefreshToken,
2595
+ authStatus: state.authStatus,
2596
+ backendUrl: state.backendUrl,
2597
+ userName: state.userName,
2598
+ userSubscriptionPlan: state.userSubscriptionPlan,
2599
+ userUsedTokens: state.userUsedTokens
2600
+ };
2601
+ };
2602
+
2570
2603
  const mergeClassNames = (...classNames) => {
2571
2604
  return classNames.filter(Boolean).join(' ');
2572
2605
  };
@@ -3138,6 +3171,130 @@ const handleClickCreateProject = async state => {
3138
3171
  };
3139
3172
  };
3140
3173
 
3174
+ let nextLoginResponse;
3175
+ const setNextLoginResponse = response => {
3176
+ nextLoginResponse = response;
3177
+ };
3178
+ const hasPendingMockLoginResponse = () => {
3179
+ return !!nextLoginResponse;
3180
+ };
3181
+ const consumeNextLoginResponse = async () => {
3182
+ if (!nextLoginResponse) {
3183
+ return undefined;
3184
+ }
3185
+ const response = nextLoginResponse;
3186
+ nextLoginResponse = undefined;
3187
+ if (response.delay > 0) {
3188
+ await new Promise(resolve => setTimeout(resolve, response.delay));
3189
+ }
3190
+ if (response.type === 'error') {
3191
+ throw new Error(response.message);
3192
+ }
3193
+ return response.response;
3194
+ };
3195
+
3196
+ const get = async key => {
3197
+ return getPreference(key);
3198
+ };
3199
+ const update = async settings => {
3200
+ await invoke$1('Preferences.update', settings);
3201
+ };
3202
+
3203
+ const isLoginResponse = value => {
3204
+ if (!value || typeof value !== 'object') {
3205
+ return false;
3206
+ }
3207
+ return true;
3208
+ };
3209
+ const trimTrailingSlashes = value => {
3210
+ return value.replace(/\/+$/, '');
3211
+ };
3212
+ const handleClickLogin = async state => {
3213
+ if (!state.backendUrl) {
3214
+ return {
3215
+ ...state,
3216
+ authErrorMessage: 'Backend URL is missing.',
3217
+ authStatus: 'signed-out'
3218
+ };
3219
+ }
3220
+ const signingInState = {
3221
+ ...state,
3222
+ authErrorMessage: '',
3223
+ authStatus: 'signing-in'
3224
+ };
3225
+ if (state.uid) {
3226
+ set$1(state.uid, state, signingInState);
3227
+ await invoke$1('Chat.rerender');
3228
+ }
3229
+ let usedMockResponse = false;
3230
+ try {
3231
+ usedMockResponse = hasPendingMockLoginResponse();
3232
+ const response = usedMockResponse ? await consumeNextLoginResponse() : await invoke$1('Auth.login', state.backendUrl);
3233
+ if (!isLoginResponse(response)) {
3234
+ return {
3235
+ ...signingInState,
3236
+ authErrorMessage: 'Backend returned an invalid login response.',
3237
+ authStatus: 'signed-out'
3238
+ };
3239
+ }
3240
+ if (typeof response.error === 'string' && response.error) {
3241
+ return {
3242
+ ...signingInState,
3243
+ authErrorMessage: response.error,
3244
+ authStatus: 'signed-out'
3245
+ };
3246
+ }
3247
+ const accessToken = typeof response.accessToken === 'string' ? response.accessToken : '';
3248
+ const refreshToken = typeof response.refreshToken === 'string' ? response.refreshToken : '';
3249
+ await update({
3250
+ 'secrets.chatBackendAccessToken': accessToken,
3251
+ 'secrets.chatBackendRefreshToken': refreshToken
3252
+ });
3253
+ return {
3254
+ ...signingInState,
3255
+ authAccessToken: accessToken,
3256
+ authErrorMessage: '',
3257
+ authRefreshToken: refreshToken,
3258
+ authStatus: accessToken ? 'signed-in' : 'signed-out',
3259
+ userName: typeof response.userName === 'string' ? response.userName : state.userName,
3260
+ userSubscriptionPlan: typeof response.subscriptionPlan === 'string' ? response.subscriptionPlan : state.userSubscriptionPlan,
3261
+ userUsedTokens: typeof response.usedTokens === 'number' ? response.usedTokens : state.userUsedTokens
3262
+ };
3263
+ } catch (error) {
3264
+ const errorMessage = error instanceof Error && error.message ? error.message : 'Backend authentication failed.';
3265
+ if (!usedMockResponse) {
3266
+ await invoke$1('Main.openUri', `${trimTrailingSlashes(state.backendUrl)}/auth/login`);
3267
+ }
3268
+ return {
3269
+ ...signingInState,
3270
+ authErrorMessage: errorMessage,
3271
+ authStatus: 'signed-out'
3272
+ };
3273
+ }
3274
+ };
3275
+
3276
+ const handleClickLogout = async state => {
3277
+ try {
3278
+ await invoke$1('Auth.logout', state.backendUrl);
3279
+ } catch {
3280
+ // Ignore logout bridge errors and still clear local auth state.
3281
+ }
3282
+ await update({
3283
+ 'secrets.chatBackendAccessToken': '',
3284
+ 'secrets.chatBackendRefreshToken': ''
3285
+ });
3286
+ return {
3287
+ ...state,
3288
+ authAccessToken: '',
3289
+ authErrorMessage: '',
3290
+ authRefreshToken: '',
3291
+ authStatus: 'signed-out',
3292
+ userName: '',
3293
+ userSubscriptionPlan: '',
3294
+ userUsedTokens: 0
3295
+ };
3296
+ };
3297
+
3141
3298
  const handleClickOpenApiApiKeySettings = async state => {
3142
3299
  await invoke$1('Main.openUri', 'app://settings.json');
3143
3300
  return state;
@@ -3166,6 +3323,9 @@ const openRouterRequestFailedMessage = 'OpenRouter request failed. Possible reas
3166
3323
  const openRouterTooManyRequestsMessage = 'OpenRouter rate limit reached (429). Please try again soon. Helpful tips:';
3167
3324
  const openRouterRequestFailureReasons = ['ContentSecurityPolicyViolation: Check DevTools for details.', 'OpenRouter server offline: Check DevTools for details.', 'Check your internet connection.'];
3168
3325
  const openRouterTooManyRequestsReasons = ['Wait a short time and retry your request.', 'Reduce request frequency to avoid rate limits.', 'Use a different model if this one is saturated.'];
3326
+ const backendUrlRequiredMessage = 'Backend URL is not configured. Configure your backend URL and try again.';
3327
+ const backendAccessTokenRequiredMessage = 'You are not logged in. Click Login to continue.';
3328
+ const backendCompletionFailedMessage = 'Backend completion request failed.';
3169
3329
 
3170
3330
  let rpc;
3171
3331
  const set = value => {
@@ -3185,6 +3345,20 @@ const getAiResponse$1 = async options => {
3185
3345
  const execute = async (name, rawArguments, options) => {
3186
3346
  return invoke$3('ChatTool.execute', name, rawArguments, options);
3187
3347
  };
3348
+ const getTools = async () => {
3349
+ return invoke$3('ChatTool.getTools');
3350
+ };
3351
+
3352
+ const executeAskQuestionTool = args => {
3353
+ const normalized = args && typeof args === 'object' ? args : {};
3354
+ const question = Reflect.get(normalized, 'question');
3355
+ const answers = Reflect.get(normalized, 'answers');
3356
+ return JSON.stringify({
3357
+ answers: Array.isArray(answers) ? answers.filter(answer => typeof answer === 'string') : [],
3358
+ ok: true,
3359
+ question: typeof question === 'string' ? question : ''
3360
+ });
3361
+ };
3188
3362
 
3189
3363
  const getToolErrorPayload = error => {
3190
3364
  const rawStack = error && typeof error === 'object' ? Reflect.get(error, 'stack') : undefined;
@@ -3363,12 +3537,19 @@ const parseToolArguments = rawArguments => {
3363
3537
  }
3364
3538
  };
3365
3539
 
3540
+ const stringifyToolOutput = output => {
3541
+ if (typeof output === 'string') {
3542
+ return output;
3543
+ }
3544
+ return JSON.stringify(output) ?? 'null';
3545
+ };
3366
3546
  const executeChatTool = async (name, rawArguments, options) => {
3367
3547
  if (options.useChatToolWorker) {
3368
- return execute(name, rawArguments, {
3548
+ const workerOutput = await execute(name, rawArguments, {
3369
3549
  assetDir: options.assetDir,
3370
3550
  platform: options.platform
3371
3551
  });
3552
+ return stringifyToolOutput(workerOutput);
3372
3553
  }
3373
3554
  const args = parseToolArguments(rawArguments);
3374
3555
  if (name === 'read_file') {
@@ -3386,6 +3567,9 @@ const executeChatTool = async (name, rawArguments, options) => {
3386
3567
  if (name === 'render_html') {
3387
3568
  return executeRenderHtmlTool(args);
3388
3569
  }
3570
+ if (name === 'ask_question') {
3571
+ return executeAskQuestionTool(args);
3572
+ }
3389
3573
  return JSON.stringify({
3390
3574
  error: `Unknown tool: ${name}`
3391
3575
  });
@@ -3394,7 +3578,7 @@ const executeChatTool = async (name, rawArguments, options) => {
3394
3578
  const getReadFileTool = () => {
3395
3579
  return {
3396
3580
  function: {
3397
- description: 'Read UTF-8 text content from a file inside the currently open workspace folder. Only pass an absolute URI. When you reference files in your response, use markdown links like [index.ts](file:///workspace/src/index.ts).',
3581
+ description: 'Read UTF-8 text content from a file inside the currently open workspace folder. Only pass an absolute URI. When you reference files in your response, use markdown links like [index.ts](vscode-references:///workspace/src/index.ts).',
3398
3582
  name: 'read_file',
3399
3583
  parameters: {
3400
3584
  additionalProperties: false,
@@ -3497,8 +3681,53 @@ const getRenderHtmlTool = () => {
3497
3681
  type: 'function'
3498
3682
  };
3499
3683
  };
3500
- const getBasicChatTools = () => {
3501
- return [getReadFileTool(), getWriteFileTool(), getListFilesTool(), getGetWorkspaceUriTool(), getRenderHtmlTool()];
3684
+ const getAskQuestionTool = () => {
3685
+ return {
3686
+ function: {
3687
+ description: 'Ask the user a multiple-choice question in the chat UI. Use this when you need a user decision before continuing. Provide short answer options.',
3688
+ name: 'ask_question',
3689
+ parameters: {
3690
+ additionalProperties: false,
3691
+ properties: {
3692
+ answers: {
3693
+ description: 'List of answer options shown to the user.',
3694
+ items: {
3695
+ type: 'string'
3696
+ },
3697
+ type: 'array'
3698
+ },
3699
+ question: {
3700
+ description: 'The question text shown to the user.',
3701
+ type: 'string'
3702
+ }
3703
+ },
3704
+ required: ['question', 'answers'],
3705
+ type: 'object'
3706
+ }
3707
+ },
3708
+ type: 'function'
3709
+ };
3710
+ };
3711
+
3712
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
3713
+ const withQuestionTool = (tools, questionToolEnabled) => {
3714
+ if (!questionToolEnabled) {
3715
+ return tools;
3716
+ }
3717
+ for (const tool of tools) {
3718
+ if (tool.function.name === 'ask_question') {
3719
+ return tools;
3720
+ }
3721
+ }
3722
+ return [...tools, getAskQuestionTool()];
3723
+ };
3724
+ const getBasicChatTools = async (questionToolEnabled = false) => {
3725
+ const fallbackTools = [getReadFileTool(), getWriteFileTool(), getListFilesTool(), getGetWorkspaceUriTool(), getRenderHtmlTool()];
3726
+ try {
3727
+ return withQuestionTool(await getTools(), questionToolEnabled);
3728
+ } catch {
3729
+ return withQuestionTool(fallbackTools, questionToolEnabled);
3730
+ }
3502
3731
  };
3503
3732
 
3504
3733
  const getClientRequestIdHeader = () => {
@@ -4608,9 +4837,10 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
4608
4837
  onEventStreamFinished,
4609
4838
  onTextChunk,
4610
4839
  onToolCallsChunk,
4840
+ questionToolEnabled = false,
4611
4841
  stream,
4612
4842
  useChatNetworkWorkerForRequests = false,
4613
- useChatToolWorker = false,
4843
+ useChatToolWorker = true,
4614
4844
  webSearchEnabled = false
4615
4845
  } = options ?? {
4616
4846
  stream: false
@@ -4619,7 +4849,7 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
4619
4849
  content: message.text,
4620
4850
  role: message.role
4621
4851
  }));
4622
- const tools = getBasicChatTools();
4852
+ const tools = await getBasicChatTools(questionToolEnabled);
4623
4853
  const maxToolIterations = 4;
4624
4854
  let previousResponseId;
4625
4855
  for (let i = 0; i <= maxToolIterations; i++) {
@@ -5197,12 +5427,12 @@ const getOpenRouterLimitInfo = async (openRouterApiKey, openRouterApiBaseUrl, us
5197
5427
  }
5198
5428
  return normalizedLimitInfo;
5199
5429
  };
5200
- const getOpenRouterAssistantText = async (messages, modelId, openRouterApiKey, openRouterApiBaseUrl, assetDir, platform, useChatNetworkWorkerForRequests = false, useChatToolWorker = false) => {
5430
+ const getOpenRouterAssistantText = async (messages, modelId, openRouterApiKey, openRouterApiBaseUrl, assetDir, platform, useChatNetworkWorkerForRequests = false, useChatToolWorker = true, questionToolEnabled = false) => {
5201
5431
  const completionMessages = messages.map(message => ({
5202
5432
  content: message.text,
5203
5433
  role: message.role
5204
5434
  }));
5205
- const tools = getBasicChatTools();
5435
+ const tools = await getBasicChatTools(questionToolEnabled);
5206
5436
  const maxToolIterations = 4;
5207
5437
  for (let i = 0; i <= maxToolIterations; i++) {
5208
5438
  let parsed;
@@ -5463,8 +5693,51 @@ const getAll = () => {
5463
5693
 
5464
5694
  /* eslint-disable prefer-destructuring */
5465
5695
 
5696
+ const getBackendCompletionsEndpoint = backendUrl => {
5697
+ const trimmedBackendUrl = backendUrl.replace(/\/+$/, '');
5698
+ return `${trimmedBackendUrl}/v1/chat/completions`;
5699
+ };
5700
+ const getEffectiveBackendModelId = selectedModelId => {
5701
+ const separatorIndex = selectedModelId.indexOf('/');
5702
+ if (separatorIndex === -1) {
5703
+ return selectedModelId;
5704
+ }
5705
+ return selectedModelId.slice(separatorIndex + 1);
5706
+ };
5707
+ const getBackendAssistantText = async (messages, selectedModelId, backendUrl, authAccessToken) => {
5708
+ let response;
5709
+ try {
5710
+ response = await fetch(getBackendCompletionsEndpoint(backendUrl), {
5711
+ body: JSON.stringify({
5712
+ messages: messages.map(message => ({
5713
+ content: message.text,
5714
+ role: message.role
5715
+ })),
5716
+ model: getEffectiveBackendModelId(selectedModelId),
5717
+ stream: false
5718
+ }),
5719
+ headers: {
5720
+ Authorization: `Bearer ${authAccessToken}`,
5721
+ 'Content-Type': 'application/json',
5722
+ ...getClientRequestIdHeader()
5723
+ },
5724
+ method: 'POST'
5725
+ });
5726
+ } catch {
5727
+ return backendCompletionFailedMessage;
5728
+ }
5729
+ if (!response.ok) {
5730
+ return backendCompletionFailedMessage;
5731
+ }
5732
+ const json = await response.json();
5733
+ const content = json.choices?.[0]?.message?.content;
5734
+ return typeof content === 'string' && content ? content : backendCompletionFailedMessage;
5735
+ };
5466
5736
  const getAiResponse = async ({
5467
5737
  assetDir,
5738
+ authAccessToken,
5739
+ authEnabled = false,
5740
+ backendUrl = '',
5468
5741
  messageId,
5469
5742
  messages,
5470
5743
  mockAiResponseDelay = 800,
@@ -5481,16 +5754,17 @@ const getAiResponse = async ({
5481
5754
  openRouterApiKey,
5482
5755
  passIncludeObfuscation = false,
5483
5756
  platform,
5757
+ questionToolEnabled = false,
5484
5758
  selectedModelId,
5485
5759
  streamingEnabled = true,
5486
5760
  useChatCoordinatorWorker = false,
5487
5761
  useChatNetworkWorkerForRequests = false,
5488
- useChatToolWorker = false,
5762
+ useChatToolWorker = true,
5489
5763
  useMockApi,
5490
5764
  userText,
5491
5765
  webSearchEnabled = false
5492
5766
  }) => {
5493
- if (useChatCoordinatorWorker) {
5767
+ if (useChatCoordinatorWorker && !authEnabled) {
5494
5768
  try {
5495
5769
  const result = await getAiResponse$1({
5496
5770
  assetDir,
@@ -5508,6 +5782,7 @@ const getAiResponse = async ({
5508
5782
  openRouterApiKey,
5509
5783
  passIncludeObfuscation,
5510
5784
  platform,
5785
+ questionToolEnabled,
5511
5786
  selectedModelId,
5512
5787
  streamingEnabled,
5513
5788
  useChatNetworkWorkerForRequests,
@@ -5530,9 +5805,18 @@ const getAiResponse = async ({
5530
5805
  }
5531
5806
  }
5532
5807
  let text = '';
5808
+ if (authEnabled) {
5809
+ if (!backendUrl) {
5810
+ text = backendUrlRequiredMessage;
5811
+ } else if (authAccessToken) {
5812
+ text = await getBackendAssistantText(messages, selectedModelId, backendUrl, authAccessToken);
5813
+ } else {
5814
+ text = backendAccessTokenRequiredMessage;
5815
+ }
5816
+ }
5533
5817
  const usesOpenApiModel = isOpenApiModel(selectedModelId, models);
5534
5818
  const usesOpenRouterModel = isOpenRouterModel(selectedModelId, models);
5535
- if (usesOpenApiModel) {
5819
+ if (!text && usesOpenApiModel) {
5536
5820
  if (useMockApi) {
5537
5821
  const openAiInput = messages.map(message => ({
5538
5822
  content: message.text,
@@ -5550,7 +5834,7 @@ const getAiResponse = async ({
5550
5834
  capture({
5551
5835
  headers,
5552
5836
  method: 'POST',
5553
- payload: getOpenAiParams(openAiInput, modelId, streamingEnabled, passIncludeObfuscation, getBasicChatTools(), webSearchEnabled, previousResponseId),
5837
+ payload: getOpenAiParams(openAiInput, modelId, streamingEnabled, passIncludeObfuscation, await getBasicChatTools(questionToolEnabled), webSearchEnabled, previousResponseId),
5554
5838
  url: getOpenApiApiEndpoint(openApiApiBaseUrl)
5555
5839
  });
5556
5840
  const result = await getMockOpenApiAssistantText(streamingEnabled, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished);
@@ -5594,6 +5878,7 @@ const getAiResponse = async ({
5594
5878
  ...(onToolCallsChunk ? {
5595
5879
  onToolCallsChunk
5596
5880
  } : {}),
5881
+ questionToolEnabled,
5597
5882
  stream: streamingEnabled,
5598
5883
  useChatNetworkWorkerForRequests,
5599
5884
  useChatToolWorker,
@@ -5610,7 +5895,7 @@ const getAiResponse = async ({
5610
5895
  } else {
5611
5896
  text = openApiApiKeyRequiredMessage;
5612
5897
  }
5613
- } else if (usesOpenRouterModel) {
5898
+ } else if (!text && usesOpenRouterModel) {
5614
5899
  const modelId = getOpenRouterModelId(selectedModelId);
5615
5900
  if (useMockApi) {
5616
5901
  const result = await getMockOpenRouterAssistantText(messages, modelId, openRouterApiBaseUrl, openRouterApiKey, mockApiCommandId, assetDir, platform);
@@ -5623,7 +5908,7 @@ const getAiResponse = async ({
5623
5908
  text = getOpenRouterErrorMessage(result);
5624
5909
  }
5625
5910
  } else if (openRouterApiKey) {
5626
- const result = await getOpenRouterAssistantText(messages, modelId, openRouterApiKey, openRouterApiBaseUrl, assetDir, platform, useChatNetworkWorkerForRequests, useChatToolWorker);
5911
+ const result = await getOpenRouterAssistantText(messages, modelId, openRouterApiKey, openRouterApiBaseUrl, assetDir, platform, useChatNetworkWorkerForRequests, useChatToolWorker, questionToolEnabled);
5627
5912
  if (result.type === 'success') {
5628
5913
  const {
5629
5914
  text: assistantText
@@ -5652,13 +5937,6 @@ const getAiResponse = async ({
5652
5937
  return message;
5653
5938
  };
5654
5939
 
5655
- const get = async key => {
5656
- return getPreference(key);
5657
- };
5658
- const update = async settings => {
5659
- await invoke$1('Preferences.update', settings);
5660
- };
5661
-
5662
5940
  const setOpenApiApiKey = async (state, openApiApiKey, persist = true) => {
5663
5941
  if (persist) {
5664
5942
  await update({
@@ -5847,7 +6125,7 @@ const isAlphaNumeric = value => {
5847
6125
  };
5848
6126
  const sanitizeLinkUrl = url => {
5849
6127
  const normalized = url.trim().toLowerCase();
5850
- if (normalized.startsWith('http://') || normalized.startsWith('https://') || normalized.startsWith('file://')) {
6128
+ if (normalized.startsWith('http://') || normalized.startsWith('https://') || normalized.startsWith('file://') || normalized.startsWith('vscode-references://')) {
5851
6129
  return url;
5852
6130
  }
5853
6131
  return '#';
@@ -5859,6 +6137,73 @@ const sanitizeImageUrl = url => {
5859
6137
  }
5860
6138
  return '#';
5861
6139
  };
6140
+ const isOpenBracket = value => {
6141
+ return value === '(' || value === '[' || value === '{';
6142
+ };
6143
+ const isCloseBracket = value => {
6144
+ return value === ')' || value === ']' || value === '}';
6145
+ };
6146
+ const findRawUrlEnd = (value, start) => {
6147
+ let index = start;
6148
+ while (index < value.length) {
6149
+ const current = value[index];
6150
+ if (current === ' ' || current === '\n' || current === '\r' || current === '\t' || current === '"' || current === "'" || current === '`' || current === '<' || current === '>') {
6151
+ break;
6152
+ }
6153
+ index++;
6154
+ }
6155
+ return index;
6156
+ };
6157
+ const trimRawUrlEnd = url => {
6158
+ let end = url.length;
6159
+ while (end > 0) {
6160
+ const current = url[end - 1];
6161
+ if (current === '.' || current === ',' || current === ':' || current === ';' || current === '!' || current === '?') {
6162
+ end--;
6163
+ continue;
6164
+ }
6165
+ if (isCloseBracket(current)) {
6166
+ const inner = url.slice(0, end - 1);
6167
+ let openCount = 0;
6168
+ let closeCount = 0;
6169
+ for (let i = 0; i < inner.length; i++) {
6170
+ if (isOpenBracket(inner[i])) {
6171
+ openCount++;
6172
+ } else if (isCloseBracket(inner[i])) {
6173
+ closeCount++;
6174
+ }
6175
+ }
6176
+ if (closeCount >= openCount) {
6177
+ end--;
6178
+ continue;
6179
+ }
6180
+ }
6181
+ break;
6182
+ }
6183
+ return url.slice(0, end);
6184
+ };
6185
+ const parseRawLinkToken = (value, start) => {
6186
+ if (!value.startsWith('https://', start) && !value.startsWith('http://', start)) {
6187
+ return undefined;
6188
+ }
6189
+ if (start >= 2 && value[start - 1] === '(' && value[start - 2] === ']') {
6190
+ return undefined;
6191
+ }
6192
+ const end = findRawUrlEnd(value, start);
6193
+ const rawUrl = value.slice(start, end);
6194
+ const href = trimRawUrlEnd(rawUrl);
6195
+ if (!href) {
6196
+ return undefined;
6197
+ }
6198
+ return {
6199
+ length: href.length,
6200
+ node: {
6201
+ href: sanitizeLinkUrl(href),
6202
+ text: href,
6203
+ type: 'link'
6204
+ }
6205
+ };
6206
+ };
5862
6207
  const parseLinkToken = (value, start) => {
5863
6208
  if (value[start] !== '[') {
5864
6209
  return undefined;
@@ -6087,7 +6432,7 @@ const parseMathToken = (value, start) => {
6087
6432
  return undefined;
6088
6433
  };
6089
6434
  const parseInlineToken = (value, start) => {
6090
- return parseImageToken(value, start) || parseLinkToken(value, start) || parseBoldToken(value, start) || parseItalicToken(value, start) || parseLinkToken(value, start) || parseBoldToken(value, start) || parseItalicToken(value, start) || parseStrikethroughToken(value, start) || parseInlineCodeToken(value, start) || parseMathToken(value, start);
6435
+ return parseImageToken(value, start) || parseLinkToken(value, start) || parseRawLinkToken(value, start) || parseBoldToken(value, start) || parseItalicToken(value, start) || parseLinkToken(value, start) || parseBoldToken(value, start) || parseItalicToken(value, start) || parseStrikethroughToken(value, start) || parseInlineCodeToken(value, start) || parseMathToken(value, start);
6091
6436
  };
6092
6437
  const parseInlineNodes = value => {
6093
6438
  const nodes = [];
@@ -6824,6 +7169,9 @@ const sanitizeGeneratedTitle = value => {
6824
7169
 
6825
7170
  const getAiSessionTitle = async (state, userText, assistantText) => {
6826
7171
  const {
7172
+ authAccessToken,
7173
+ authEnabled,
7174
+ backendUrl,
6827
7175
  models,
6828
7176
  openApiApiBaseUrl,
6829
7177
  openApiApiKey,
@@ -6837,13 +7185,13 @@ const getAiSessionTitle = async (state, userText, assistantText) => {
6837
7185
  }
6838
7186
  const usesOpenApiModel = isOpenApiModel(selectedModelId, models);
6839
7187
  const usesOpenRouterModel = isOpenRouterModel(selectedModelId, models);
6840
- if (usesOpenApiModel && !openApiApiKey) {
7188
+ if (!authEnabled && usesOpenApiModel && !openApiApiKey) {
6841
7189
  return '';
6842
7190
  }
6843
- if (usesOpenRouterModel && !openRouterApiKey) {
7191
+ if (!authEnabled && usesOpenRouterModel && !openRouterApiKey) {
6844
7192
  return '';
6845
7193
  }
6846
- if (!usesOpenApiModel && !usesOpenRouterModel) {
7194
+ if (!authEnabled && !usesOpenApiModel && !usesOpenRouterModel) {
6847
7195
  return '';
6848
7196
  }
6849
7197
  const titlePrompt = `Create a concise title (max 6 words) for this conversation. Respond only with the title, no punctuation at the end.
@@ -6860,6 +7208,9 @@ Assistant: ${assistantText}`;
6860
7208
  };
6861
7209
  const titleResponse = await getAiResponse({
6862
7210
  assetDir: state.assetDir,
7211
+ authAccessToken,
7212
+ authEnabled,
7213
+ backendUrl,
6863
7214
  messages: [promptMessage],
6864
7215
  mockAiResponseDelay: state.mockAiResponseDelay,
6865
7216
  mockApiCommandId: state.mockApiCommandId,
@@ -7169,6 +7520,9 @@ const handleSubmit = async state => {
7169
7520
  const {
7170
7521
  aiSessionTitleGenerationEnabled,
7171
7522
  assetDir,
7523
+ authAccessToken,
7524
+ authEnabled,
7525
+ backendUrl,
7172
7526
  composerValue,
7173
7527
  emitStreamingFunctionCallEvents,
7174
7528
  mockAiResponseDelay,
@@ -7181,6 +7535,7 @@ const handleSubmit = async state => {
7181
7535
  openRouterApiKey,
7182
7536
  passIncludeObfuscation,
7183
7537
  platform,
7538
+ questionToolEnabled,
7184
7539
  selectedModelId,
7185
7540
  selectedSessionId,
7186
7541
  sessions,
@@ -7308,6 +7663,9 @@ const handleSubmit = async state => {
7308
7663
  } : undefined;
7309
7664
  const assistantMessage = await getAiResponse({
7310
7665
  assetDir,
7666
+ authAccessToken,
7667
+ authEnabled,
7668
+ backendUrl,
7311
7669
  messageId: assistantMessageId,
7312
7670
  messages: messagesWithMentionContext,
7313
7671
  mockAiResponseDelay,
@@ -7346,6 +7704,9 @@ const handleSubmit = async state => {
7346
7704
  openRouterApiKey,
7347
7705
  passIncludeObfuscation,
7348
7706
  platform,
7707
+ ...(typeof questionToolEnabled === 'boolean' ? {
7708
+ questionToolEnabled
7709
+ } : {}),
7349
7710
  selectedModelId,
7350
7711
  streamingEnabled,
7351
7712
  useChatCoordinatorWorker,
@@ -7415,6 +7776,8 @@ const CreateSession = 'create-session';
7415
7776
  const CreateSessionInProjectPrefix = 'create-session-in-project:';
7416
7777
  const SessionDebug = 'session-debug';
7417
7778
  const Settings = 'settings';
7779
+ const Login = 'login';
7780
+ const Logout = 'logout';
7418
7781
  const CloseChat = 'close-chat';
7419
7782
  const SessionDelete = 'SessionDelete';
7420
7783
  const ProjectPrefix = 'project:';
@@ -7620,6 +7983,10 @@ const handleClick = async (state, name, id = '') => {
7620
7983
  return handleClickOpenApiApiKeySettings(state);
7621
7984
  case name === OpenOpenApiApiKeyWebsite:
7622
7985
  return handleClickOpenApiApiKeyWebsite(state);
7986
+ case name === Login:
7987
+ return handleClickLogin(state);
7988
+ case name === Logout:
7989
+ return handleClickLogout(state);
7623
7990
  default:
7624
7991
  return state;
7625
7992
  }
@@ -7653,11 +8020,18 @@ const handleClickNew = async state => {
7653
8020
  return focusInput(clearedState);
7654
8021
  };
7655
8022
 
8023
+ const normalizeFileReferenceUri = uri => {
8024
+ if (uri.startsWith('vscode-references://')) {
8025
+ return `file://${uri.slice('vscode-references://'.length)}`;
8026
+ }
8027
+ return uri;
8028
+ };
7656
8029
  const handleClickReadFile = async uri => {
7657
8030
  if (!uri) {
7658
8031
  return;
7659
8032
  }
7660
- await invoke$1('Main.openUri', uri);
8033
+ const normalizedUri = normalizeFileReferenceUri(uri);
8034
+ await invoke$1('Main.openUri', normalizedUri);
7661
8035
  };
7662
8036
 
7663
8037
  const handleClickSessionDebug = async state => {
@@ -7965,12 +8339,12 @@ const initialize = async () => {
7965
8339
  set$4(rpc);
7966
8340
  };
7967
8341
 
7968
- const isObject = value => {
8342
+ const isObject$1 = value => {
7969
8343
  return typeof value === 'object' && value !== null;
7970
8344
  };
7971
8345
 
7972
8346
  const getSavedChatListScrollTop = savedState => {
7973
- if (!isObject(savedState)) {
8347
+ if (!isObject$1(savedState)) {
7974
8348
  return undefined;
7975
8349
  }
7976
8350
  const {
@@ -7983,7 +8357,7 @@ const getSavedChatListScrollTop = savedState => {
7983
8357
  };
7984
8358
 
7985
8359
  const getSavedMessagesScrollTop = savedState => {
7986
- if (!isObject(savedState)) {
8360
+ if (!isObject$1(savedState)) {
7987
8361
  return undefined;
7988
8362
  }
7989
8363
  const {
@@ -7996,7 +8370,7 @@ const getSavedMessagesScrollTop = savedState => {
7996
8370
  };
7997
8371
 
7998
8372
  const getSavedSelectedModelId = savedState => {
7999
- if (!isObject(savedState)) {
8373
+ if (!isObject$1(savedState)) {
8000
8374
  return undefined;
8001
8375
  }
8002
8376
  const {
@@ -8009,7 +8383,7 @@ const getSavedSelectedModelId = savedState => {
8009
8383
  };
8010
8384
 
8011
8385
  const getSavedSelectedSessionId = savedState => {
8012
- if (!isObject(savedState)) {
8386
+ if (!isObject$1(savedState)) {
8013
8387
  return undefined;
8014
8388
  }
8015
8389
  const {
@@ -8022,7 +8396,7 @@ const getSavedSelectedSessionId = savedState => {
8022
8396
  };
8023
8397
 
8024
8398
  const getSavedSessions = savedState => {
8025
- if (!isObject(savedState)) {
8399
+ if (!isObject$1(savedState)) {
8026
8400
  return undefined;
8027
8401
  }
8028
8402
  const {
@@ -8035,7 +8409,7 @@ const getSavedSessions = savedState => {
8035
8409
  };
8036
8410
 
8037
8411
  const getSavedViewMode = savedState => {
8038
- if (!isObject(savedState)) {
8412
+ if (!isObject$1(savedState)) {
8039
8413
  return undefined;
8040
8414
  }
8041
8415
  const {
@@ -8056,6 +8430,42 @@ const loadAiSessionTitleGenerationEnabled = async () => {
8056
8430
  }
8057
8431
  };
8058
8432
 
8433
+ const loadAuthEnabled = async () => {
8434
+ try {
8435
+ const savedAuthEnabled = await get('chat.authEnabled');
8436
+ return typeof savedAuthEnabled === 'boolean' ? savedAuthEnabled : false;
8437
+ } catch {
8438
+ return false;
8439
+ }
8440
+ };
8441
+
8442
+ const loadBackendAccessToken = async () => {
8443
+ try {
8444
+ const savedAccessToken = await get('secrets.chatBackendAccessToken');
8445
+ return typeof savedAccessToken === 'string' ? savedAccessToken : '';
8446
+ } catch {
8447
+ return '';
8448
+ }
8449
+ };
8450
+
8451
+ const loadBackendRefreshToken = async () => {
8452
+ try {
8453
+ const savedRefreshToken = await get('secrets.chatBackendRefreshToken');
8454
+ return typeof savedRefreshToken === 'string' ? savedRefreshToken : '';
8455
+ } catch {
8456
+ return '';
8457
+ }
8458
+ };
8459
+
8460
+ const loadBackendUrl = async () => {
8461
+ try {
8462
+ const savedBackendUrl = await get('chat.backendUrl');
8463
+ return typeof savedBackendUrl === 'string' ? savedBackendUrl : '';
8464
+ } catch {
8465
+ return '';
8466
+ }
8467
+ };
8468
+
8059
8469
  const loadComposerDropEnabled = async () => {
8060
8470
  try {
8061
8471
  const savedComposerDropEnabled = await get('chatView.composerDropEnabled');
@@ -8148,9 +8558,9 @@ const loadUseChatNetworkWorkerForRequests = async () => {
8148
8558
  const loadUseChatToolWorker = async () => {
8149
8559
  try {
8150
8560
  const savedUseChatToolWorker = await get('chatView.useChatToolWorker');
8151
- return typeof savedUseChatToolWorker === 'boolean' ? savedUseChatToolWorker : false;
8561
+ return typeof savedUseChatToolWorker === 'boolean' ? savedUseChatToolWorker : true;
8152
8562
  } catch {
8153
- return false;
8563
+ return true;
8154
8564
  }
8155
8565
  };
8156
8566
 
@@ -8164,9 +8574,13 @@ const loadVoiceDictationEnabled = async () => {
8164
8574
  };
8165
8575
 
8166
8576
  const loadPreferences = async () => {
8167
- const [aiSessionTitleGenerationEnabled, composerDropEnabled, openApiApiKey, openRouterApiKey, emitStreamingFunctionCallEvents, streamingEnabled, passIncludeObfuscation, useChatCoordinatorWorker, useChatMathWorker, useChatNetworkWorkerForRequests, useChatToolWorker, voiceDictationEnabled] = await Promise.all([loadAiSessionTitleGenerationEnabled(), loadComposerDropEnabled(), loadOpenApiApiKey(), loadOpenRouterApiKey(), loadEmitStreamingFunctionCallEvents(), loadStreamingEnabled(), loadPassIncludeObfuscation(), loadUseChatCoordinatorWorker(), loadUseChatMathWorker(), loadUseChatNetworkWorkerForRequests(), loadUseChatToolWorker(), loadVoiceDictationEnabled()]);
8577
+ const [aiSessionTitleGenerationEnabled, authAccessToken, authEnabled, authRefreshToken, backendUrl, composerDropEnabled, openApiApiKey, openRouterApiKey, emitStreamingFunctionCallEvents, streamingEnabled, passIncludeObfuscation, useChatCoordinatorWorker, useChatMathWorker, useChatNetworkWorkerForRequests, useChatToolWorker, voiceDictationEnabled] = await Promise.all([loadAiSessionTitleGenerationEnabled(), loadBackendAccessToken(), loadAuthEnabled(), loadBackendRefreshToken(), loadBackendUrl(), loadComposerDropEnabled(), loadOpenApiApiKey(), loadOpenRouterApiKey(), loadEmitStreamingFunctionCallEvents(), loadStreamingEnabled(), loadPassIncludeObfuscation(), loadUseChatCoordinatorWorker(), loadUseChatMathWorker(), loadUseChatNetworkWorkerForRequests(), loadUseChatToolWorker(), loadVoiceDictationEnabled()]);
8168
8578
  return {
8169
8579
  aiSessionTitleGenerationEnabled,
8580
+ authAccessToken,
8581
+ authEnabled,
8582
+ authRefreshToken,
8583
+ backendUrl,
8170
8584
  composerDropEnabled,
8171
8585
  emitStreamingFunctionCallEvents,
8172
8586
  openApiApiKey,
@@ -8212,7 +8626,7 @@ const toSummarySession = session => {
8212
8626
  };
8213
8627
  };
8214
8628
  const getSavedSelectedProjectId = savedState => {
8215
- if (!isObject(savedState)) {
8629
+ if (!isObject$1(savedState)) {
8216
8630
  return undefined;
8217
8631
  }
8218
8632
  const {
@@ -8224,7 +8638,7 @@ const getSavedSelectedProjectId = savedState => {
8224
8638
  return selectedProjectId;
8225
8639
  };
8226
8640
  const getSavedProjects = savedState => {
8227
- if (!isObject(savedState)) {
8641
+ if (!isObject$1(savedState)) {
8228
8642
  return undefined;
8229
8643
  }
8230
8644
  const {
@@ -8234,7 +8648,7 @@ const getSavedProjects = savedState => {
8234
8648
  return undefined;
8235
8649
  }
8236
8650
  const validProjects = projects.filter(project => {
8237
- if (!isObject(project)) {
8651
+ if (!isObject$1(project)) {
8238
8652
  return false;
8239
8653
  }
8240
8654
  return typeof project.id === 'string' && typeof project.name === 'string' && typeof project.uri === 'string';
@@ -8251,7 +8665,7 @@ const ensureBlankProject = (projects, fallbackBlankProject) => {
8251
8665
  return [fallbackBlankProject, ...projects];
8252
8666
  };
8253
8667
  const getSavedProjectListScrollTop = savedState => {
8254
- if (!isObject(savedState)) {
8668
+ if (!isObject$1(savedState)) {
8255
8669
  return undefined;
8256
8670
  }
8257
8671
  const {
@@ -8263,7 +8677,7 @@ const getSavedProjectListScrollTop = savedState => {
8263
8677
  return projectListScrollTop;
8264
8678
  };
8265
8679
  const getSavedProjectExpandedIds = savedState => {
8266
- if (!isObject(savedState)) {
8680
+ if (!isObject$1(savedState)) {
8267
8681
  return undefined;
8268
8682
  }
8269
8683
  const {
@@ -8279,7 +8693,7 @@ const getSavedProjectExpandedIds = savedState => {
8279
8693
  return ids;
8280
8694
  };
8281
8695
  const getSavedLastNormalViewMode = savedState => {
8282
- if (!isObject(savedState)) {
8696
+ if (!isObject$1(savedState)) {
8283
8697
  return undefined;
8284
8698
  }
8285
8699
  const {
@@ -8291,7 +8705,7 @@ const getSavedLastNormalViewMode = savedState => {
8291
8705
  return lastNormalViewMode;
8292
8706
  };
8293
8707
  const getSavedComposerValue = savedState => {
8294
- if (!isObject(savedState)) {
8708
+ if (!isObject$1(savedState)) {
8295
8709
  return undefined;
8296
8710
  }
8297
8711
  const {
@@ -8308,6 +8722,10 @@ const loadContent = async (state, savedState) => {
8308
8722
  const savedComposerValue = getSavedComposerValue(savedState);
8309
8723
  const {
8310
8724
  aiSessionTitleGenerationEnabled,
8725
+ authAccessToken,
8726
+ authEnabled,
8727
+ authRefreshToken,
8728
+ backendUrl,
8311
8729
  composerDropEnabled,
8312
8730
  emitStreamingFunctionCallEvents,
8313
8731
  openApiApiKey,
@@ -8369,6 +8787,11 @@ const loadContent = async (state, savedState) => {
8369
8787
  return {
8370
8788
  ...state,
8371
8789
  aiSessionTitleGenerationEnabled,
8790
+ authAccessToken,
8791
+ authEnabled,
8792
+ authRefreshToken,
8793
+ authStatus: authAccessToken ? 'signed-in' : 'signed-out',
8794
+ backendUrl,
8372
8795
  chatListScrollTop,
8373
8796
  composerDropActive: false,
8374
8797
  composerDropEnabled,
@@ -8400,6 +8823,36 @@ const loadContent = async (state, savedState) => {
8400
8823
  };
8401
8824
  };
8402
8825
 
8826
+ const isObject = value => {
8827
+ return !!value && typeof value === 'object';
8828
+ };
8829
+ const getDelay = payload => {
8830
+ if (!isObject(payload)) {
8831
+ return 0;
8832
+ }
8833
+ const {
8834
+ delay
8835
+ } = payload;
8836
+ return typeof delay === 'number' && delay > 0 ? delay : 0;
8837
+ };
8838
+ const mockBackendAuthResponse = (state, payload) => {
8839
+ const delay = getDelay(payload);
8840
+ if (payload.type === 'error') {
8841
+ setNextLoginResponse({
8842
+ delay,
8843
+ message: payload.message || 'Backend authentication failed.',
8844
+ type: 'error'
8845
+ });
8846
+ return state;
8847
+ }
8848
+ setNextLoginResponse({
8849
+ delay,
8850
+ response: payload,
8851
+ type: 'success'
8852
+ });
8853
+ return state;
8854
+ };
8855
+
8403
8856
  const mockOpenApiRequestGetAll = _state => {
8404
8857
  return getAll();
8405
8858
  };
@@ -8744,6 +9197,29 @@ const getCss = (composerHeight, listItemHeight, chatMessageFontSize, chatMessage
8744
9197
  .TokenProperty {
8745
9198
  color: var(--ColorChartsOrange, #ef9f76);
8746
9199
  }
9200
+
9201
+ .ChatToolCallQuestionText {
9202
+ margin-bottom: 6px;
9203
+ }
9204
+
9205
+ .ChatToolCallQuestionOptions {
9206
+ display: flex;
9207
+ flex-wrap: wrap;
9208
+ gap: 6px;
9209
+ }
9210
+
9211
+ .ChatToolCallQuestionOption {
9212
+ background: color-mix(in srgb, var(--ColorBadgeBackground, #2f3640) 70%, transparent);
9213
+ border: 1px solid color-mix(in srgb, var(--ColorBorder, #3a3d41) 70%, transparent);
9214
+ border-radius: 999px;
9215
+ color: var(--ColorForeground, #d5dbe3);
9216
+ display: inline-block;
9217
+ max-width: 100%;
9218
+ overflow: hidden;
9219
+ padding: 2px 8px;
9220
+ text-overflow: ellipsis;
9221
+ white-space: nowrap;
9222
+ }
8747
9223
  `;
8748
9224
  if (!renderHtmlCss.trim()) {
8749
9225
  return baseCss;
@@ -8800,6 +9276,7 @@ const renderFocusContext = (oldState, newState) => {
8800
9276
  const Actions = 'Actions';
8801
9277
  const ChatActions = 'ChatActions';
8802
9278
  const ChatName = 'ChatName';
9279
+ const ChatAuthError = 'ChatAuthError';
8803
9280
  const ChatSendArea = 'ChatSendArea';
8804
9281
  const ChatViewDropOverlay = 'ChatViewDropOverlay';
8805
9282
  const ChatViewDropOverlayActive = 'ChatViewDropOverlayActive';
@@ -8814,6 +9291,7 @@ const ButtonSecondary = 'ButtonSecondary';
8814
9291
  const Empty = '';
8815
9292
  const FileIcon = 'FileIcon';
8816
9293
  const IconButton = 'IconButton';
9294
+ const IconButtonDisabled = 'IconButtonDisabled';
8817
9295
  const ImageElement = 'ImageElement';
8818
9296
  const InputBox = 'InputBox';
8819
9297
  const Label = 'Label';
@@ -8844,6 +9322,9 @@ const ChatMessageContent = 'ChatMessageContent';
8844
9322
  const ChatToolCalls = 'ChatToolCalls';
8845
9323
  const ChatToolCallsLabel = 'ChatToolCallsLabel';
8846
9324
  const ChatToolCallReadFileLink = 'ChatToolCallReadFileLink';
9325
+ const ChatToolCallQuestionOption = 'ChatToolCallQuestionOption';
9326
+ const ChatToolCallQuestionOptions = 'ChatToolCallQuestionOptions';
9327
+ const ChatToolCallQuestionText = 'ChatToolCallQuestionText';
8847
9328
  const ChatToolCallRenderHtmlLabel = 'ChatToolCallRenderHtmlLabel';
8848
9329
  const ChatToolCallRenderHtmlContent = 'ChatToolCallRenderHtmlContent';
8849
9330
  const ChatToolCallRenderHtmlBody = 'ChatToolCallRenderHtmlBody';
@@ -9034,8 +9515,9 @@ const getImageAltText = alt => {
9034
9515
  }
9035
9516
  return `${alt} (image could not be loaded)`;
9036
9517
  };
9037
- const isFileUri = href => {
9038
- return href.trim().toLowerCase().startsWith('file://');
9518
+ const isFileReferenceUri = href => {
9519
+ const normalized = href.trim().toLowerCase();
9520
+ return normalized.startsWith('file://') || normalized.startsWith('vscode-references://');
9039
9521
  };
9040
9522
  const getInlineNodeDom = (inlineNode, useChatMathWorker = false) => {
9041
9523
  if (inlineNode.type === 'text') {
@@ -9082,7 +9564,7 @@ const getInlineNodeDom = (inlineNode, useChatMathWorker = false) => {
9082
9564
  if (inlineNode.type === 'math-inline-dom') {
9083
9565
  return inlineNode.dom;
9084
9566
  }
9085
- if (isFileUri(inlineNode.href)) {
9567
+ if (isFileReferenceUri(inlineNode.href)) {
9086
9568
  return [{
9087
9569
  childCount: 1,
9088
9570
  className: ChatMessageLink,
@@ -9183,6 +9665,19 @@ const pythonRules = [{
9183
9665
  className: TokenKeyword,
9184
9666
  regex: /\b(?:def|class|return|if|elif|else|for|while|in|import|from|as|with|try|except|finally|raise|lambda|yield|pass|break|continue|True|False|None|and|or|not|is)\b/
9185
9667
  }];
9668
+ const jsonRules = [{
9669
+ className: TokenProperty,
9670
+ regex: /"[^"\\]*(?:\\.[^"\\]*)*"(?=\s*:)/
9671
+ }, {
9672
+ className: TokenString,
9673
+ regex: /"[^"\\]*(?:\\.[^"\\]*)*"/
9674
+ }, {
9675
+ className: TokenNumber,
9676
+ regex: /-?\b\d+\.?\d*(?:[eE][+-]?\d+)?\b/
9677
+ }, {
9678
+ className: TokenKeyword,
9679
+ regex: /\b(?:true|false|null)\b/
9680
+ }];
9186
9681
  const tokenize = (code, rules) => {
9187
9682
  const tokens = [];
9188
9683
  let pos = 0;
@@ -9239,6 +9734,9 @@ const highlightCode = (code, language) => {
9239
9734
  if (normalized === 'py' || normalized === 'python') {
9240
9735
  return tokenize(code, pythonRules);
9241
9736
  }
9737
+ if (normalized === 'json') {
9738
+ return tokenize(code, jsonRules);
9739
+ }
9242
9740
  return [{
9243
9741
  className: '',
9244
9742
  text: code
@@ -9507,6 +10005,15 @@ const getOpenRouterTooManyRequestsDom = () => {
9507
10005
  })];
9508
10006
  };
9509
10007
 
10008
+ const RE_QUERY_OR_HASH = /[?#].*$/;
10009
+ const RE_TRAILING_SLASH = /\/$/;
10010
+ const getFileNameFromUri = uri => {
10011
+ const stripped = uri.replace(RE_QUERY_OR_HASH, '').replace(RE_TRAILING_SLASH, '');
10012
+ const slashIndex = Math.max(stripped.lastIndexOf('/'), stripped.lastIndexOf('\\\\'));
10013
+ const fileName = slashIndex === -1 ? stripped : stripped.slice(slashIndex + 1);
10014
+ return fileName || uri;
10015
+ };
10016
+
9510
10017
  const getToolCallArgumentPreview = rawArguments => {
9511
10018
  if (!rawArguments.trim()) {
9512
10019
  return '""';
@@ -9534,13 +10041,69 @@ const getToolCallArgumentPreview = rawArguments => {
9534
10041
  return rawArguments;
9535
10042
  };
9536
10043
 
9537
- const RE_QUERY_OR_HASH = /[?#].*$/;
9538
- const RE_TRAILING_SLASH = /\/$/;
9539
- const getFileNameFromUri = uri => {
9540
- const stripped = uri.replace(RE_QUERY_OR_HASH, '').replace(RE_TRAILING_SLASH, '');
9541
- const slashIndex = Math.max(stripped.lastIndexOf('/'), stripped.lastIndexOf('\\\\'));
9542
- const fileName = slashIndex === -1 ? stripped : stripped.slice(slashIndex + 1);
9543
- return fileName || uri;
10044
+ const getToolCallStatusLabel = toolCall => {
10045
+ if (toolCall.status === 'not-found') {
10046
+ return ' (not-found)';
10047
+ }
10048
+ if (toolCall.status === 'error') {
10049
+ if (toolCall.errorMessage) {
10050
+ return ` (error: ${toolCall.errorMessage})`;
10051
+ }
10052
+ return ' (error)';
10053
+ }
10054
+ return '';
10055
+ };
10056
+
10057
+ const parseAskQuestionArguments = rawArguments => {
10058
+ let parsed;
10059
+ try {
10060
+ parsed = JSON.parse(rawArguments);
10061
+ } catch {
10062
+ return {
10063
+ answers: [],
10064
+ question: ''
10065
+ };
10066
+ }
10067
+ if (!parsed || typeof parsed !== 'object') {
10068
+ return {
10069
+ answers: [],
10070
+ question: ''
10071
+ };
10072
+ }
10073
+ const question = Reflect.get(parsed, 'question');
10074
+ const rawAnswers = Reflect.get(parsed, 'answers');
10075
+ const rawChoices = Reflect.get(parsed, 'choices');
10076
+ const rawOptions = Reflect.get(parsed, 'options');
10077
+ const arrayValue = Array.isArray(rawAnswers) ? rawAnswers : Array.isArray(rawChoices) ? rawChoices : Array.isArray(rawOptions) ? rawOptions : [];
10078
+ const answers = arrayValue.filter(value => typeof value === 'string');
10079
+ return {
10080
+ answers,
10081
+ question: typeof question === 'string' ? question : ''
10082
+ };
10083
+ };
10084
+ const getToolCallAskQuestionVirtualDom = toolCall => {
10085
+ const parsed = parseAskQuestionArguments(toolCall.arguments);
10086
+ const statusLabel = getToolCallStatusLabel(toolCall);
10087
+ const questionLabel = parsed.question.trim() ? parsed.question : '(empty question)';
10088
+ const answers = parsed.answers.length > 0 ? parsed.answers : ['(no answers)'];
10089
+ const childCount = 2;
10090
+ return [{
10091
+ childCount,
10092
+ className: ChatOrderedListItem,
10093
+ type: Li
10094
+ }, {
10095
+ childCount: 1,
10096
+ className: ChatToolCallQuestionText,
10097
+ type: Div
10098
+ }, text(`ask_question: ${questionLabel}${statusLabel}`), {
10099
+ childCount: answers.length,
10100
+ className: ChatToolCallQuestionOptions,
10101
+ type: Div
10102
+ }, ...answers.flatMap(answer => [{
10103
+ childCount: 1,
10104
+ className: ChatToolCallQuestionOption,
10105
+ type: Span
10106
+ }, text(answer.trim() ? answer : '(empty answer)')])];
9544
10107
  };
9545
10108
 
9546
10109
  const isCompleteJson = value => {
@@ -9613,19 +10176,6 @@ const getReadFileTarget = rawArguments => {
9613
10176
  };
9614
10177
  };
9615
10178
 
9616
- const getToolCallStatusLabel = toolCall => {
9617
- if (toolCall.status === 'not-found') {
9618
- return ' (not-found)';
9619
- }
9620
- if (toolCall.status === 'error') {
9621
- if (toolCall.errorMessage) {
9622
- return ` (error: ${toolCall.errorMessage})`;
9623
- }
9624
- return ' (error)';
9625
- }
9626
- return '';
9627
- };
9628
-
9629
10179
  const getToolCallReadFileVirtualDom = toolCall => {
9630
10180
  const target = getReadFileTarget(toolCall.arguments);
9631
10181
  if (!target) {
@@ -9966,9 +10516,6 @@ const getToolCallDisplayName = name => {
9966
10516
  };
9967
10517
  const getToolCallLabel = toolCall => {
9968
10518
  const displayName = getToolCallDisplayName(toolCall.name);
9969
- if (toolCall.name === 'getWorkspaceUri' && toolCall.result) {
9970
- return `${displayName} ${toolCall.result}`;
9971
- }
9972
10519
  const argumentPreview = getToolCallArgumentPreview(toolCall.arguments);
9973
10520
  const statusLabel = getToolCallStatusLabel(toolCall);
9974
10521
  if (argumentPreview === '{}') {
@@ -9976,7 +10523,36 @@ const getToolCallLabel = toolCall => {
9976
10523
  }
9977
10524
  return `${displayName} ${argumentPreview}${statusLabel}`;
9978
10525
  };
10526
+ const getToolCallGetWorkspaceUriVirtualDom = toolCall => {
10527
+ if (!toolCall.result) {
10528
+ return [];
10529
+ }
10530
+ const statusLabel = getToolCallStatusLabel(toolCall);
10531
+ const fileName = getFileNameFromUri(toolCall.result);
10532
+ return [{
10533
+ childCount: statusLabel ? 4 : 3,
10534
+ className: ChatOrderedListItem,
10535
+ title: toolCall.result,
10536
+ type: Li
10537
+ }, {
10538
+ childCount: 0,
10539
+ className: FileIcon,
10540
+ type: Div
10541
+ }, text('get_workspace_uri '), {
10542
+ childCount: 1,
10543
+ className: ChatToolCallReadFileLink,
10544
+ 'data-uri': toolCall.result,
10545
+ onClick: HandleClickReadFile,
10546
+ type: Span
10547
+ }, text(fileName), ...(statusLabel ? [text(statusLabel)] : [])];
10548
+ };
9979
10549
  const getToolCallDom = toolCall => {
10550
+ if (toolCall.name === 'getWorkspaceUri') {
10551
+ const virtualDom = getToolCallGetWorkspaceUriVirtualDom(toolCall);
10552
+ if (virtualDom.length > 0) {
10553
+ return virtualDom;
10554
+ }
10555
+ }
9980
10556
  if (toolCall.name === 'read_file' || toolCall.name === 'list_files' || toolCall.name === 'list_file') {
9981
10557
  const virtualDom = getToolCallReadFileVirtualDom(toolCall);
9982
10558
  if (virtualDom.length > 0) {
@@ -9989,6 +10565,12 @@ const getToolCallDom = toolCall => {
9989
10565
  return virtualDom;
9990
10566
  }
9991
10567
  }
10568
+ if (toolCall.name === 'ask_question') {
10569
+ const virtualDom = getToolCallAskQuestionVirtualDom(toolCall);
10570
+ if (virtualDom.length > 0) {
10571
+ return virtualDom;
10572
+ }
10573
+ }
9992
10574
  const label = getToolCallLabel(toolCall);
9993
10575
  return [{
9994
10576
  childCount: 1,
@@ -10186,7 +10768,7 @@ const getProjectListDom = (projects, sessions, projectExpandedIds, selectedProje
10186
10768
  }, text('+ Add Project')];
10187
10769
  };
10188
10770
 
10189
- const getChatModeChatFocusVirtualDom = (sessions, selectedSessionId, composerValue, openRouterApiKeyInput, openApiApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openRouterApiKeyState = 'idle', composerHeight = 28, composerFontSize = 13, composerFontFamily = 'system-ui', composerLineHeight = 20, messagesScrollTop = 0, composerDropActive = false, composerDropEnabled = true, projects = [], projectExpandedIds = [], selectedProjectId = '', projectListScrollTop = 0, voiceDictationEnabled = false, useChatMathWorker = false, parsedMessages = []) => {
10771
+ const getChatModeChatFocusVirtualDom = (sessions, selectedSessionId, composerValue, openRouterApiKeyInput, openApiApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openRouterApiKeyState = 'idle', composerHeight = 28, composerFontSize = 13, composerFontFamily = 'system-ui', composerLineHeight = 20, messagesScrollTop = 0, composerDropActive = false, composerDropEnabled = true, projects = [], projectExpandedIds = [], selectedProjectId = '', projectListScrollTop = 0, voiceDictationEnabled = false, useChatMathWorker = false, parsedMessages = [], authEnabled = false, authStatus = 'signed-out', authErrorMessage = '') => {
10190
10772
  const selectedSession = sessions.find(session => session.id === selectedSessionId);
10191
10773
  const messages = selectedSession ? selectedSession.messages : [];
10192
10774
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
@@ -10229,7 +10811,8 @@ const getBackButtonVirtualDom = () => {
10229
10811
  const getHeaderActionVirtualDom = item => {
10230
10812
  return [{
10231
10813
  childCount: 1,
10232
- className: IconButton,
10814
+ className: mergeClassNames(IconButton, item.disabled ? IconButtonDisabled : ''),
10815
+ disabled: item.disabled,
10233
10816
  name: item.name,
10234
10817
  onClick: item.onClick,
10235
10818
  title: item.title,
@@ -10241,8 +10824,22 @@ const getHeaderActionVirtualDom = item => {
10241
10824
  }];
10242
10825
  };
10243
10826
 
10244
- const getChatHeaderActionsDom = viewMode => {
10827
+ const getChatHeaderActionsDom = (viewMode, authEnabled = false, authStatus = 'signed-out') => {
10245
10828
  const toggleTitle = viewMode === 'chat-focus' ? normalChatMode() : chatFocusMode();
10829
+ const isSigningIn = authStatus === 'signing-in';
10830
+ const authAction = authEnabled && authStatus !== 'signed-in' ? {
10831
+ disabled: isSigningIn,
10832
+ icon: 'MaskIcon MaskIconAccount',
10833
+ name: Login,
10834
+ onClick: HandleClick,
10835
+ title: isSigningIn ? loggingInToBackend() : loginToBackend()
10836
+ } : authEnabled ? {
10837
+ disabled: false,
10838
+ icon: 'MaskIcon MaskIconSignOut',
10839
+ name: Logout,
10840
+ onClick: HandleClick,
10841
+ title: logoutFromBackend()
10842
+ } : undefined;
10246
10843
  const items = [{
10247
10844
  icon: 'MaskIcon MaskIconLayoutPanelLeft',
10248
10845
  name: ToggleChatFocus,
@@ -10263,7 +10860,7 @@ const getChatHeaderActionsDom = viewMode => {
10263
10860
  name: Settings,
10264
10861
  onClick: HandleClickSettings,
10265
10862
  title: settings()
10266
- }, {
10863
+ }, ...(authAction ? [authAction] : []), {
10267
10864
  icon: 'MaskIcon MaskIconClose',
10268
10865
  name: CloseChat,
10269
10866
  onClick: HandleClickClose,
@@ -10276,9 +10873,10 @@ const getChatHeaderActionsDom = viewMode => {
10276
10873
  }, ...items.flatMap(getHeaderActionVirtualDom)];
10277
10874
  };
10278
10875
 
10279
- const getChatHeaderDomDetailMode = selectedSessionTitle => {
10876
+ const getChatHeaderDomDetailMode = (selectedSessionTitle, authEnabled = false, authStatus = 'signed-out', authErrorMessage = '') => {
10877
+ const hasAuthError = authEnabled && !!authErrorMessage;
10280
10878
  return [{
10281
- childCount: 2,
10879
+ childCount: hasAuthError ? 3 : 2,
10282
10880
  className: ChatHeader,
10283
10881
  type: Div
10284
10882
  }, {
@@ -10289,10 +10887,14 @@ const getChatHeaderDomDetailMode = selectedSessionTitle => {
10289
10887
  childCount: 1,
10290
10888
  className: Label,
10291
10889
  type: Span
10292
- }, text(selectedSessionTitle), ...getChatHeaderActionsDom('detail')];
10890
+ }, text(selectedSessionTitle), ...getChatHeaderActionsDom('detail', authEnabled, authStatus), ...(hasAuthError ? [{
10891
+ childCount: 1,
10892
+ className: ChatAuthError,
10893
+ type: Span
10894
+ }, text(authErrorMessage)] : [])];
10293
10895
  };
10294
10896
 
10295
- const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue, openRouterApiKeyInput, openApiApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openRouterApiKeyState = 'idle', composerHeight = 28, composerFontSize = 13, composerFontFamily = 'system-ui', composerLineHeight = 20, messagesScrollTop = 0, composerDropActive = false, composerDropEnabled = true, voiceDictationEnabled = false, useChatMathWorker = false, parsedMessages = []) => {
10897
+ const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue, openRouterApiKeyInput, openApiApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openRouterApiKeyState = 'idle', composerHeight = 28, composerFontSize = 13, composerFontFamily = 'system-ui', composerLineHeight = 20, messagesScrollTop = 0, composerDropActive = false, composerDropEnabled = true, voiceDictationEnabled = false, useChatMathWorker = false, parsedMessages = [], authEnabled = false, authStatus = 'signed-out', authErrorMessage = '') => {
10296
10898
  const selectedSession = sessions.find(session => session.id === selectedSessionId);
10297
10899
  const selectedSessionTitle = selectedSession?.title || chatTitle();
10298
10900
  const messages = selectedSession ? selectedSession.messages : [];
@@ -10303,7 +10905,7 @@ const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue,
10303
10905
  onDragEnter: HandleDragEnterChatView,
10304
10906
  onDragOver: HandleDragOverChatView,
10305
10907
  type: Div
10306
- }, ...getChatHeaderDomDetailMode(selectedSessionTitle), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openRouterApiKeyState, messagesScrollTop, useChatMathWorker), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, voiceDictationEnabled), ...(isDropOverlayVisible ? [{
10908
+ }, ...getChatHeaderDomDetailMode(selectedSessionTitle, authEnabled, authStatus, authErrorMessage), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openRouterApiKeyState, messagesScrollTop, useChatMathWorker), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, voiceDictationEnabled), ...(isDropOverlayVisible ? [{
10307
10909
  childCount: 1,
10308
10910
  className: mergeClassNames(ChatViewDropOverlay, ChatViewDropOverlayActive),
10309
10911
  name: ComposerDropTarget,
@@ -10317,16 +10919,21 @@ const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue,
10317
10919
  }] : [])];
10318
10920
  };
10319
10921
 
10320
- const getChatHeaderListModeDom = () => {
10922
+ const getChatHeaderListModeDom = (authEnabled = false, authStatus = 'signed-out', authErrorMessage = '') => {
10923
+ const hasAuthError = authEnabled && !!authErrorMessage;
10321
10924
  return [{
10322
- childCount: 2,
10925
+ childCount: hasAuthError ? 3 : 2,
10323
10926
  className: ChatHeader,
10324
10927
  type: Div
10325
10928
  }, {
10326
10929
  childCount: 1,
10327
10930
  className: Label,
10328
10931
  type: Span
10329
- }, text(chats()), ...getChatHeaderActionsDom('list')];
10932
+ }, text(chats()), ...getChatHeaderActionsDom('list', authEnabled, authStatus), ...(hasAuthError ? [{
10933
+ childCount: 1,
10934
+ className: ChatAuthError,
10935
+ type: Span
10936
+ }, text(authErrorMessage)] : [])];
10330
10937
  };
10331
10938
 
10332
10939
  const getEmptyChatSessionsDom = () => {
@@ -10384,7 +10991,7 @@ const getChatListDom = (sessions, selectedSessionId, chatListScrollTop = 0) => {
10384
10991
  }, ...sessions.flatMap(getSessionDom)];
10385
10992
  };
10386
10993
 
10387
- const getChatModeListVirtualDom = (sessions, selectedSessionId, composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, composerHeight = 28, composerFontSize = 13, composerFontFamily = 'system-ui', composerLineHeight = 20, chatListScrollTop = 0, composerDropActive = false, composerDropEnabled = true, voiceDictationEnabled = false) => {
10994
+ const getChatModeListVirtualDom = (sessions, selectedSessionId, composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, composerHeight = 28, composerFontSize = 13, composerFontFamily = 'system-ui', composerLineHeight = 20, chatListScrollTop = 0, composerDropActive = false, composerDropEnabled = true, voiceDictationEnabled = false, authEnabled = false, authStatus = 'signed-out', authErrorMessage = '') => {
10388
10995
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
10389
10996
  return [{
10390
10997
  childCount: isDropOverlayVisible ? 4 : 3,
@@ -10392,7 +10999,7 @@ const getChatModeListVirtualDom = (sessions, selectedSessionId, composerValue, m
10392
10999
  onDragEnter: HandleDragEnterChatView,
10393
11000
  onDragOver: HandleDragOverChatView,
10394
11001
  type: Div
10395
- }, ...getChatHeaderListModeDom(), ...getChatListDom(sessions, selectedSessionId, chatListScrollTop), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, voiceDictationEnabled), ...(isDropOverlayVisible ? [{
11002
+ }, ...getChatHeaderListModeDom(authEnabled, authStatus, authErrorMessage), ...getChatListDom(sessions, selectedSessionId, chatListScrollTop), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, voiceDictationEnabled), ...(isDropOverlayVisible ? [{
10396
11003
  childCount: 1,
10397
11004
  className: mergeClassNames(ChatViewDropOverlay, ChatViewDropOverlayActive),
10398
11005
  name: ComposerDropTarget,
@@ -10429,15 +11036,15 @@ const getFallbackParsedMessages = sessions => {
10429
11036
  }
10430
11037
  return parsedMessages;
10431
11038
  };
10432
- const getChatVirtualDom = (sessions, selectedSessionId, composerValue, openRouterApiKeyInput, viewMode, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openApiApiKeyInput, openRouterApiKeyState, composerHeight, composerFontSize, composerFontFamily, composerLineHeight, chatListScrollTop, messagesScrollTop, composerDropActive = false, composerDropEnabled = true, projects = [], projectExpandedIds = [], selectedProjectId = '', projectListScrollTop = 0, voiceDictationEnabled = false, useChatMathWorker = false, parsedMessages) => {
11039
+ const getChatVirtualDom = (sessions, selectedSessionId, composerValue, openRouterApiKeyInput, viewMode, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openApiApiKeyInput, openRouterApiKeyState, composerHeight, composerFontSize, composerFontFamily, composerLineHeight, chatListScrollTop, messagesScrollTop, composerDropActive = false, composerDropEnabled = true, projects = [], projectExpandedIds = [], selectedProjectId = '', projectListScrollTop = 0, voiceDictationEnabled = false, useChatMathWorker = false, parsedMessages, authEnabled = false, authStatus = 'signed-out', authErrorMessage = '') => {
10433
11040
  const effectiveParsedMessages = parsedMessages || getFallbackParsedMessages(sessions);
10434
11041
  switch (viewMode) {
10435
11042
  case 'chat-focus':
10436
- return getChatModeChatFocusVirtualDom(sessions, selectedSessionId, composerValue, openRouterApiKeyInput, openApiApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openRouterApiKeyState, composerHeight, composerFontSize, composerFontFamily, composerLineHeight, messagesScrollTop, composerDropActive, composerDropEnabled, projects, projectExpandedIds, selectedProjectId, projectListScrollTop, voiceDictationEnabled, useChatMathWorker, effectiveParsedMessages);
11043
+ return getChatModeChatFocusVirtualDom(sessions, selectedSessionId, composerValue, openRouterApiKeyInput, openApiApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openRouterApiKeyState, composerHeight, composerFontSize, composerFontFamily, composerLineHeight, messagesScrollTop, composerDropActive, composerDropEnabled, projects, projectExpandedIds, selectedProjectId, projectListScrollTop, voiceDictationEnabled, useChatMathWorker, effectiveParsedMessages, authEnabled, authStatus, authErrorMessage);
10437
11044
  case 'detail':
10438
- return getChatModeDetailVirtualDom(sessions, selectedSessionId, composerValue, openRouterApiKeyInput, openApiApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openRouterApiKeyState, composerHeight, composerFontSize, composerFontFamily, composerLineHeight, messagesScrollTop, composerDropActive, composerDropEnabled, voiceDictationEnabled, useChatMathWorker, effectiveParsedMessages);
11045
+ return getChatModeDetailVirtualDom(sessions, selectedSessionId, composerValue, openRouterApiKeyInput, openApiApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openRouterApiKeyState, composerHeight, composerFontSize, composerFontFamily, composerLineHeight, messagesScrollTop, composerDropActive, composerDropEnabled, voiceDictationEnabled, useChatMathWorker, effectiveParsedMessages, authEnabled, authStatus, authErrorMessage);
10439
11046
  case 'list':
10440
- return getChatModeListVirtualDom(sessions, selectedSessionId, composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, composerHeight, composerFontSize, composerFontFamily, composerLineHeight, chatListScrollTop, composerDropActive, composerDropEnabled, voiceDictationEnabled);
11047
+ return getChatModeListVirtualDom(sessions, selectedSessionId, composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, composerHeight, composerFontSize, composerFontFamily, composerLineHeight, chatListScrollTop, composerDropActive, composerDropEnabled, voiceDictationEnabled, authEnabled, authStatus, authErrorMessage);
10441
11048
  default:
10442
11049
  return getChatModeUnsupportedVirtualDom();
10443
11050
  }
@@ -10445,6 +11052,9 @@ const getChatVirtualDom = (sessions, selectedSessionId, composerValue, openRoute
10445
11052
 
10446
11053
  const renderItems = (oldState, newState) => {
10447
11054
  const {
11055
+ authEnabled,
11056
+ authErrorMessage,
11057
+ authStatus,
10448
11058
  chatListScrollTop,
10449
11059
  composerDropActive,
10450
11060
  composerDropEnabled,
@@ -10478,7 +11088,7 @@ const renderItems = (oldState, newState) => {
10478
11088
  if (initial) {
10479
11089
  return [SetDom2, uid, []];
10480
11090
  }
10481
- const dom = getChatVirtualDom(sessions, selectedSessionId, composerValue, openRouterApiKeyInput, viewMode, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openApiApiKeyInput, openRouterApiKeyState, composerHeight, composerFontSize, composerFontFamily, composerLineHeight, chatListScrollTop, messagesScrollTop, composerDropActive, composerDropEnabled, projects, projectExpandedIds, selectedProjectId, projectListScrollTop, voiceDictationEnabled, useChatMathWorker, parsedMessages);
11091
+ const dom = getChatVirtualDom(sessions, selectedSessionId, composerValue, openRouterApiKeyInput, viewMode, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openApiApiKeyInput, openRouterApiKeyState, composerHeight, composerFontSize, composerFontFamily, composerLineHeight, chatListScrollTop, messagesScrollTop, composerDropActive, composerDropEnabled, projects, projectExpandedIds, selectedProjectId, projectListScrollTop, voiceDictationEnabled, useChatMathWorker, parsedMessages, authEnabled, authStatus, authErrorMessage);
10482
11092
  return [SetDom2, uid, dom];
10483
11093
  };
10484
11094
 
@@ -10777,6 +11387,20 @@ const saveState = state => {
10777
11387
  };
10778
11388
  };
10779
11389
 
11390
+ const setAuthEnabled = (state, authEnabled) => {
11391
+ return {
11392
+ ...state,
11393
+ authEnabled
11394
+ };
11395
+ };
11396
+
11397
+ const setBackendUrl = (state, backendUrl) => {
11398
+ return {
11399
+ ...state,
11400
+ backendUrl
11401
+ };
11402
+ };
11403
+
10780
11404
  const dummySessions = [{
10781
11405
  id: 'session-a',
10782
11406
  messages: [],
@@ -10806,6 +11430,13 @@ const setEmitStreamingFunctionCallEvents = (state, emitStreamingFunctionCallEven
10806
11430
  };
10807
11431
  };
10808
11432
 
11433
+ const setQuestionToolEnabled = (state, questionToolEnabled) => {
11434
+ return {
11435
+ ...state,
11436
+ questionToolEnabled
11437
+ };
11438
+ };
11439
+
10809
11440
  const setStreamingEnabled = (state, streamingEnabled) => {
10810
11441
  return {
10811
11442
  ...state,
@@ -10870,6 +11501,7 @@ const commandMap = {
10870
11501
  'Chat.deleteSessionAtIndex': wrapCommand(deleteSessionAtIndex),
10871
11502
  'Chat.diff2': diff2,
10872
11503
  'Chat.enterNewLine': wrapCommand(handleNewline),
11504
+ 'Chat.getAuthState': wrapGetter(getAuthState),
10873
11505
  'Chat.getCommandIds': getCommandIds,
10874
11506
  'Chat.getKeyBindings': getKeyBindings,
10875
11507
  'Chat.getMenuEntries': getMenuEntries,
@@ -10905,6 +11537,7 @@ const commandMap = {
10905
11537
  'Chat.initialize': initialize,
10906
11538
  'Chat.loadContent': wrapCommand(loadContent),
10907
11539
  'Chat.loadContent2': wrapCommand(loadContent),
11540
+ 'Chat.mockBackendAuthResponse': wrapCommand(mockBackendAuthResponse),
10908
11541
  'Chat.mockOpenApiRequestGetAll': wrapGetter(mockOpenApiRequestGetAll),
10909
11542
  'Chat.mockOpenApiRequestReset': wrapCommand(mockOpenApiRequestReset),
10910
11543
  'Chat.mockOpenApiSetHttpErrorResponse': wrapCommand(mockOpenApiSetHttpErrorResponse),
@@ -10920,9 +11553,12 @@ const commandMap = {
10920
11553
  'Chat.reset': wrapCommand(reset),
10921
11554
  'Chat.resize': wrapCommand(resize),
10922
11555
  'Chat.saveState': wrapGetter(saveState),
11556
+ 'Chat.setAuthEnabled': wrapCommand(setAuthEnabled),
11557
+ 'Chat.setBackendUrl': wrapCommand(setBackendUrl),
10923
11558
  'Chat.setChatList': wrapCommand(setChatList),
10924
11559
  'Chat.setEmitStreamingFunctionCallEvents': wrapCommand(setEmitStreamingFunctionCallEvents),
10925
11560
  'Chat.setOpenRouterApiKey': wrapCommand(setOpenRouterApiKey),
11561
+ 'Chat.setQuestionToolEnabled': wrapCommand(setQuestionToolEnabled),
10926
11562
  'Chat.setStreamingEnabled': wrapCommand(setStreamingEnabled),
10927
11563
  'Chat.setUseChatCoordinatorWorker': wrapCommand(setUseChatCoordinatorWorker),
10928
11564
  'Chat.setUseChatMathWorker': wrapCommand(setUseChatMathWorker),