@lvce-editor/chat-view 7.6.0 → 7.9.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.
@@ -1290,6 +1290,9 @@ const {
1290
1290
  const openExternal = async url => {
1291
1291
  return invoke$1('Open.openExternal', url);
1292
1292
  };
1293
+ const openUrl = async (url, platform) => {
1294
+ return invoke$1('Open.openUrl', url, platform);
1295
+ };
1293
1296
 
1294
1297
  const {
1295
1298
  invoke,
@@ -2318,13 +2321,11 @@ const parseToolEnablement = value => {
2318
2321
  };
2319
2322
  const validateToolEnablement = value => {
2320
2323
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
2321
- // eslint-disable-next-line @typescript-eslint/only-throw-error
2322
2324
  throw new TypeError('Tool enablement must be an object map of tool names to booleans.');
2323
2325
  }
2324
2326
  const toolEnablement = {};
2325
2327
  for (const [key, enabled] of Object.entries(value)) {
2326
2328
  if (typeof enabled !== 'boolean') {
2327
- // eslint-disable-next-line @typescript-eslint/only-throw-error
2328
2329
  throw new TypeError(`Tool enablement for "${key}" must be a boolean.`);
2329
2330
  }
2330
2331
  toolEnablement[key] = enabled;
@@ -2404,6 +2405,7 @@ const createDefaultState = () => {
2404
2405
  gitBranchPickerVisible: false,
2405
2406
  headerHeight: 50,
2406
2407
  height: 0,
2408
+ useOwnBackend: false,
2407
2409
  ...responsivePickerState,
2408
2410
  initial: true,
2409
2411
  inputSource: 'script',
@@ -3919,6 +3921,7 @@ const openFolder = async () => {
3919
3921
  }
3920
3922
  };
3921
3923
 
3924
+ const isFileSystemEntry = entry => !!entry;
3922
3925
  const parseEntries = value => {
3923
3926
  if (!Array.isArray(value)) {
3924
3927
  return [];
@@ -3937,7 +3940,7 @@ const parseEntries = value => {
3937
3940
  };
3938
3941
  }
3939
3942
  return undefined;
3940
- }).filter(entry => !!entry);
3943
+ }).filter(isFileSystemEntry);
3941
3944
  };
3942
3945
 
3943
3946
  const getRelativePath = (fromPath, toPath) => {
@@ -3987,17 +3990,19 @@ const toGitUri = (baseUri, ...segments) => {
3987
3990
 
3988
3991
  const FileTypeFile = 1;
3989
3992
  const FileTypeDirectory = 2;
3990
- const collectBranchNames = async (workspaceUri, refsHeadsUri, prefix, branches) => {
3993
+ const collectBranchNames = async (workspaceUri, refsHeadsUri, prefix) => {
3991
3994
  const entries = await readDir(workspaceUri, refsHeadsUri);
3995
+ const branches = [];
3992
3996
  for (const entry of entries) {
3993
3997
  if (entry.type === FileTypeDirectory) {
3994
- await collectBranchNames(workspaceUri, toGitUri(refsHeadsUri, entry.name), `${prefix}${entry.name}/`, branches);
3998
+ branches.push(...(await collectBranchNames(workspaceUri, toGitUri(refsHeadsUri, entry.name), `${prefix}${entry.name}/`)));
3995
3999
  continue;
3996
4000
  }
3997
4001
  if (entry.type === FileTypeFile) {
3998
- branches.add(`${prefix}${entry.name}`);
4002
+ branches.push(`${prefix}${entry.name}`);
3999
4003
  }
4000
4004
  }
4005
+ return branches;
4001
4006
  };
4002
4007
 
4003
4008
  const decodeFileContent = content => {
@@ -4048,7 +4053,6 @@ const parseCurrentBranch = headContent => {
4048
4053
  const getGitBranches = async workspaceUri => {
4049
4054
  const gitDirUri = await getGitDirUri(workspaceUri);
4050
4055
  if (!gitDirUri) {
4051
- // eslint-disable-next-line @typescript-eslint/only-throw-error
4052
4056
  throw new globalThis.Error('Git repository not found.');
4053
4057
  }
4054
4058
  const branches = new Set();
@@ -4063,12 +4067,14 @@ const getGitBranches = async workspaceUri => {
4063
4067
  // Keep trying to discover branches from refs even if HEAD cannot be read.
4064
4068
  }
4065
4069
  try {
4066
- await collectBranchNames(workspaceUri, toGitUri(gitDirUri, 'refs', 'heads'), '', branches);
4070
+ const discoveredBranches = await collectBranchNames(workspaceUri, toGitUri(gitDirUri, 'refs', 'heads'), '');
4071
+ for (const branch of discoveredBranches) {
4072
+ branches.add(branch);
4073
+ }
4067
4074
  } catch {
4068
4075
  // Repositories without local refs should still open and surface any current branch we found.
4069
4076
  }
4070
4077
  if (branches.size === 0) {
4071
- // eslint-disable-next-line @typescript-eslint/only-throw-error
4072
4078
  throw new globalThis.Error('No local git branches found.');
4073
4079
  }
4074
4080
  return [...branches].toSorted((a, b) => a.localeCompare(b)).map(name => ({
@@ -4359,8 +4365,24 @@ const getBackendAuthUrl = (backendUrl, path) => {
4359
4365
  return `${trimTrailingSlashes(backendUrl)}${path}`;
4360
4366
  };
4361
4367
 
4362
- const getBackendLoginUrl = backendUrl => {
4363
- return getBackendAuthUrl(backendUrl, '/auth/login');
4368
+ const getCurrentHref = async () => {
4369
+ try {
4370
+ return await invoke('Layout.getHref');
4371
+ } catch {
4372
+ // ignore
4373
+ }
4374
+ if (!globalThis.location || typeof globalThis.location.href !== 'string' || !globalThis.location.href) {
4375
+ return '';
4376
+ }
4377
+ return globalThis.location.href;
4378
+ };
4379
+ const getBackendLoginUrl = async backendUrl => {
4380
+ const loginUrl = new URL(getBackendAuthUrl(backendUrl, '/login'));
4381
+ const redirectUri = await getCurrentHref();
4382
+ if (redirectUri) {
4383
+ loginUrl.searchParams.set('redirect_uri', redirectUri);
4384
+ }
4385
+ return loginUrl.toString();
4364
4386
  };
4365
4387
 
4366
4388
  const getLoggedOutBackendAuthState = (authErrorMessage = '') => {
@@ -4395,16 +4417,59 @@ const logoutFromBackend = async backendUrl => {
4395
4417
  }
4396
4418
  };
4397
4419
 
4420
+ let nextLoginResponse;
4421
+ let nextRefreshResponse;
4422
+ const setNextLoginResponse = response => {
4423
+ nextLoginResponse = response;
4424
+ };
4425
+ const setNextRefreshResponse = response => {
4426
+ nextRefreshResponse = response;
4427
+ };
4428
+ const hasPendingMockLoginResponse = () => {
4429
+ return !!nextLoginResponse;
4430
+ };
4431
+ const hasPendingMockRefreshResponse = () => {
4432
+ return !!nextRefreshResponse;
4433
+ };
4434
+ const consumeNextLoginResponse = async () => {
4435
+ if (!nextLoginResponse) {
4436
+ return undefined;
4437
+ }
4438
+ const response = nextLoginResponse;
4439
+ nextLoginResponse = undefined;
4440
+ if (response.delay > 0) {
4441
+ await new Promise(resolve => setTimeout(resolve, response.delay));
4442
+ }
4443
+ if (response.type === 'error') {
4444
+ throw new Error(response.message);
4445
+ }
4446
+ return response.response;
4447
+ };
4448
+ const consumeNextRefreshResponse = async () => {
4449
+ if (!nextRefreshResponse) {
4450
+ return undefined;
4451
+ }
4452
+ const response = nextRefreshResponse;
4453
+ nextRefreshResponse = undefined;
4454
+ if (response.delay > 0) {
4455
+ await new Promise(resolve => setTimeout(resolve, response.delay));
4456
+ }
4457
+ if (response.type === 'error') {
4458
+ throw new Error(response.message);
4459
+ }
4460
+ return response.response;
4461
+ };
4462
+
4398
4463
  const getBackendRefreshUrl = backendUrl => {
4399
4464
  return getBackendAuthUrl(backendUrl, '/auth/refresh');
4400
4465
  };
4401
4466
 
4402
- const isObject$2 = value => {
4467
+ const isObject$3 = value => {
4403
4468
  return !!value && typeof value === 'object';
4404
4469
  };
4405
4470
 
4406
4471
  const isBackendAuthResponse = value => {
4407
- return isObject$2(value);
4472
+ return isObject$3(value);
4408
4473
  };
4409
4474
 
4410
4475
  const getNumber = (value, fallback = 0) => {
@@ -4438,6 +4503,10 @@ const syncBackendAuth = async backendUrl => {
4438
4503
  return getLoggedOutBackendAuthState('Backend URL is missing.');
4439
4504
  }
4440
4505
  try {
4506
+ if (hasPendingMockRefreshResponse()) {
4507
+ const mockResponse = await consumeNextRefreshResponse();
4508
+ return parseBackendAuthResponse(mockResponse);
4509
+ }
4441
4510
  const response = await fetch(getBackendRefreshUrl(backendUrl), {
4442
4511
  credentials: 'include',
4443
4512
  headers: {
@@ -4492,29 +4561,6 @@ const waitForBackendLogin = async (backendUrl, timeoutMs = 30_000, pollIntervalM
4492
4561
  return getLoggedOutBackendAuthState(lastErrorMessage);
4493
4562
  };
4494
4563
 
4495
- let nextLoginResponse;
4496
- const setNextLoginResponse = response => {
4497
- nextLoginResponse = response;
4498
- };
4499
- const hasPendingMockLoginResponse = () => {
4500
- return !!nextLoginResponse;
4501
- };
4502
- const consumeNextLoginResponse = async () => {
4503
- if (!nextLoginResponse) {
4504
- return undefined;
4505
- }
4506
- const response = nextLoginResponse;
4507
- nextLoginResponse = undefined;
4508
- if (response.delay > 0) {
4509
- await new Promise(resolve => setTimeout(resolve, response.delay));
4510
- }
4511
- if (response.type === 'error') {
4512
- // eslint-disable-next-line @typescript-eslint/only-throw-error
4513
- throw new Error(response.message);
4514
- }
4515
- return response.response;
4516
- };
4517
-
4518
4564
  const isLoginResponse = value => {
4519
4565
  if (!value || typeof value !== 'object') {
4520
4566
  return false;
@@ -4550,10 +4596,8 @@ const handleClickLogin = async state => {
4550
4596
  set(state.uid, state, signingInState);
4551
4597
  await invoke('Chat.rerender');
4552
4598
  }
4553
- let usedMockResponse = false;
4554
4599
  try {
4555
- usedMockResponse = hasPendingMockLoginResponse();
4556
- if (usedMockResponse) {
4600
+ if (hasPendingMockLoginResponse()) {
4557
4601
  const response = await consumeNextLoginResponse();
4558
4602
  if (!isLoginResponse(response)) {
4559
4603
  return {
@@ -4571,7 +4615,8 @@ const handleClickLogin = async state => {
4571
4615
  }
4572
4616
  return getLoggedInState(signingInState, response);
4573
4617
  }
4574
- await invoke('Main.openUri', getBackendLoginUrl(state.backendUrl));
4618
+ const url = await getBackendLoginUrl(state.backendUrl);
4619
+ await openUrl(url, state.platform);
4575
4620
  const authState = await waitForBackendLogin(state.backendUrl);
4576
4621
  return {
4577
4622
  ...signingInState,
@@ -4821,11 +4866,9 @@ const withWriteFileLineCounts = async (workerOutput, rawArguments) => {
4821
4866
  };
4822
4867
  const executeChatTool = async (name, rawArguments, options) => {
4823
4868
  if (!isToolEnabled(options.toolEnablement, name)) {
4824
- // eslint-disable-next-line @typescript-eslint/only-throw-error
4825
4869
  throw new Error(`Tool "${name}" is disabled in chat.toolEnablement preferences.`);
4826
4870
  }
4827
4871
  if (!options.useChatToolWorker) {
4828
- // eslint-disable-next-line @typescript-eslint/only-throw-error
4829
4872
  throw new Error('Chat tools must be executed in a web worker environment. Please set useChatToolWorker to true in the options.');
4830
4873
  }
4831
4874
  const executionOptions = {
@@ -4939,6 +4982,28 @@ const getBasicChatTools = async (agentMode = defaultAgentMode, questionToolEnabl
4939
4982
  }
4940
4983
  };
4941
4984
 
4985
+ const getBackendErrorMessage = errorResult => {
4986
+ switch (errorResult.details) {
4987
+ case 'http-error':
4988
+ {
4989
+ const errorMessage = errorResult.errorMessage?.trim();
4990
+ if (typeof errorResult.statusCode === 'number') {
4991
+ const prefix = `Backend completion request failed (status ${errorResult.statusCode}).`;
4992
+ if (!errorMessage) {
4993
+ return prefix;
4994
+ }
4995
+ return `${prefix} ${errorMessage}`;
4996
+ }
4997
+ if (errorMessage) {
4998
+ return `Backend completion request failed. ${errorMessage}`;
4999
+ }
5000
+ return backendCompletionFailedMessage;
5001
+ }
5002
+ case 'request-failed':
5003
+ return backendCompletionFailedMessage;
5004
+ }
5005
+ };
5006
+
4942
5007
  const getAttachmentTextPart = attachment => {
4943
5008
  switch (attachment.displayType) {
4944
5009
  case 'file':
@@ -5022,7 +5087,7 @@ const reset$1 = () => {
5022
5087
  finished = false;
5023
5088
  errorResult = undefined;
5024
5089
  };
5025
- const setHttpErrorResponse = (statusCode, body) => {
5090
+ const setHttpErrorResponse$1 = (statusCode, body) => {
5026
5091
  const rawError = body && typeof body === 'object' ? Reflect.get(body, 'error') : undefined;
5027
5092
  const errorCode = rawError && typeof rawError === 'object' ? Reflect.get(rawError, 'code') : undefined;
5028
5093
  const errorMessage = rawError && typeof rawError === 'object' ? Reflect.get(rawError, 'message') : undefined;
@@ -5051,7 +5116,7 @@ const setRequestFailedResponse = (isOffline = false) => {
5051
5116
  type: 'error'
5052
5117
  };
5053
5118
  };
5054
- const takeErrorResponse = () => {
5119
+ const takeErrorResponse$1 = () => {
5055
5120
  const error = errorResult;
5056
5121
  errorResult = undefined;
5057
5122
  return error;
@@ -5183,7 +5248,7 @@ const emitToolCalls = async (toolCallAccumulator, onToolCallsChunk) => {
5183
5248
  await onToolCallsChunk(toolCalls);
5184
5249
  };
5185
5250
  const getMockOpenApiAssistantText = async (stream, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished) => {
5186
- const error = takeErrorResponse();
5251
+ const error = takeErrorResponse$1();
5187
5252
  if (error) {
5188
5253
  return error;
5189
5254
  }
@@ -6182,6 +6247,7 @@ const getResponseFromSseEvents = events => {
6182
6247
  return `data: ${data}\n\n`;
6183
6248
  });
6184
6249
  const stream = new ReadableStream({
6250
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
6185
6251
  start(controller) {
6186
6252
  for (const chunk of chunks) {
6187
6253
  controller.enqueue(new TextEncoder().encode(chunk));
@@ -7106,6 +7172,20 @@ const isOpenRouterModel = (selectedModelId, models) => {
7106
7172
  return selectedModelId.toLowerCase().startsWith('openrouter/');
7107
7173
  };
7108
7174
 
7175
+ let errorResponse;
7176
+ const setHttpErrorResponse = (statusCode, body) => {
7177
+ errorResponse = {
7178
+ body,
7179
+ statusCode,
7180
+ type: 'http-error'
7181
+ };
7182
+ };
7183
+ const takeErrorResponse = () => {
7184
+ const response = errorResponse;
7185
+ errorResponse = undefined;
7186
+ return response;
7187
+ };
7188
+
7109
7189
  /* eslint-disable prefer-destructuring */
7110
7190
 
7111
7191
  const trailingSlashesRegex = /\/+$/;
@@ -7113,17 +7193,57 @@ const getBackendCompletionsEndpoint = backendUrl => {
7113
7193
  const trimmedBackendUrl = backendUrl.replace(trailingSlashesRegex, '');
7114
7194
  return `${trimmedBackendUrl}/v1/chat/completions`;
7115
7195
  };
7116
- const getEffectiveBackendModelId = selectedModelId => {
7117
- const separatorIndex = selectedModelId.indexOf('/');
7118
- if (separatorIndex === -1) {
7119
- return selectedModelId;
7120
- }
7121
- return selectedModelId.slice(separatorIndex + 1);
7122
- };
7123
7196
  const hasImageAttachments = messages => {
7124
7197
  return messages.some(message => message.attachments?.some(attachment => attachment.displayType === 'image'));
7125
7198
  };
7199
+ const isObject$2 = value => {
7200
+ return !!value && typeof value === 'object';
7201
+ };
7202
+ const getBackendErrorMessageFromBody = body => {
7203
+ if (!isObject$2(body)) {
7204
+ return undefined;
7205
+ }
7206
+ const directMessage = Reflect.get(body, 'message');
7207
+ if (typeof directMessage === 'string' && directMessage) {
7208
+ return directMessage;
7209
+ }
7210
+ const directError = Reflect.get(body, 'error');
7211
+ if (typeof directError === 'string' && directError) {
7212
+ return directError;
7213
+ }
7214
+ if (!isObject$2(directError)) {
7215
+ return undefined;
7216
+ }
7217
+ const nestedMessage = Reflect.get(directError, 'message');
7218
+ if (typeof nestedMessage === 'string' && nestedMessage) {
7219
+ return nestedMessage;
7220
+ }
7221
+ return undefined;
7222
+ };
7223
+ const getBackendStatusCodeFromBody = body => {
7224
+ if (!isObject$2(body)) {
7225
+ return undefined;
7226
+ }
7227
+ const statusCode = Reflect.get(body, 'statusCode');
7228
+ if (typeof statusCode === 'number') {
7229
+ return statusCode;
7230
+ }
7231
+ return undefined;
7232
+ };
7126
7233
  const getBackendAssistantText = async (messages, selectedModelId, backendUrl, authAccessToken, systemPrompt) => {
7234
+ const mockError = takeErrorResponse();
7235
+ if (mockError) {
7236
+ const errorMessage = getBackendErrorMessageFromBody(mockError.body);
7237
+ return getBackendErrorMessage({
7238
+ details: 'http-error',
7239
+ ...(typeof mockError.statusCode === 'number' ? {
7240
+ statusCode: mockError.statusCode
7241
+ } : {}),
7242
+ ...(errorMessage ? {
7243
+ errorMessage
7244
+ } : {})
7245
+ });
7246
+ }
7127
7247
  let response;
7128
7248
  try {
7129
7249
  response = await fetch(getBackendCompletionsEndpoint(backendUrl), {
@@ -7135,7 +7255,8 @@ const getBackendAssistantText = async (messages, selectedModelId, backendUrl, au
7135
7255
  content: message.text,
7136
7256
  role: message.role
7137
7257
  }))],
7138
- model: getEffectiveBackendModelId(selectedModelId),
7258
+ model: selectedModelId,
7259
+ selectedModelId,
7139
7260
  stream: false
7140
7261
  }),
7141
7262
  headers: {
@@ -7146,20 +7267,37 @@ const getBackendAssistantText = async (messages, selectedModelId, backendUrl, au
7146
7267
  method: 'POST'
7147
7268
  });
7148
7269
  } catch {
7149
- return backendCompletionFailedMessage;
7270
+ return getBackendErrorMessage({
7271
+ details: 'request-failed'
7272
+ });
7150
7273
  }
7151
7274
  if (!response.ok) {
7275
+ const payload = await response.json().catch(() => undefined);
7276
+ const errorMessage = getBackendErrorMessageFromBody(payload);
7277
+ const statusCode = response.status || getBackendStatusCodeFromBody(payload);
7278
+ return getBackendErrorMessage({
7279
+ details: 'http-error',
7280
+ ...(typeof statusCode === 'number' ? {
7281
+ statusCode
7282
+ } : {}),
7283
+ ...(errorMessage ? {
7284
+ errorMessage
7285
+ } : {})
7286
+ });
7287
+ }
7288
+ let json;
7289
+ try {
7290
+ json = await response.json();
7291
+ } catch {
7152
7292
  return backendCompletionFailedMessage;
7153
7293
  }
7154
- const json = await response.json();
7155
- const content = json.choices?.[0]?.message?.content;
7294
+ const content = json.text || json.message?.content || json.choices?.[0]?.message?.content;
7156
7295
  return typeof content === 'string' && content ? content : backendCompletionFailedMessage;
7157
7296
  };
7158
7297
  const getAiResponse = async ({
7159
7298
  agentMode = defaultAgentMode,
7160
7299
  assetDir,
7161
7300
  authAccessToken,
7162
- authEnabled = false,
7163
7301
  backendUrl = '',
7164
7302
  maxToolCalls = defaultMaxToolCalls,
7165
7303
  messageId,
@@ -7190,12 +7328,13 @@ const getAiResponse = async ({
7190
7328
  useChatNetworkWorkerForRequests = false,
7191
7329
  useChatToolWorker = true,
7192
7330
  useMockApi,
7331
+ useOwnBackend = false,
7193
7332
  userText,
7194
7333
  webSearchEnabled = false,
7195
7334
  workspaceUri
7196
7335
  }) => {
7197
7336
  useChatCoordinatorWorker = false; // TODO enable this
7198
- if (useChatCoordinatorWorker && !authEnabled) {
7337
+ if (useChatCoordinatorWorker && !useOwnBackend) {
7199
7338
  try {
7200
7339
  const result = await getAiResponse$1({
7201
7340
  agentMode,
@@ -7256,7 +7395,7 @@ const getAiResponse = async ({
7256
7395
  if (hasImageAttachments(messages) && !supportsImages) {
7257
7396
  text = getImageNotSupportedMessage(selectedModel?.name);
7258
7397
  }
7259
- if (!text && authEnabled) {
7398
+ if (!text && useOwnBackend) {
7260
7399
  if (!backendUrl) {
7261
7400
  text = backendUrlRequiredMessage;
7262
7401
  } else if (authAccessToken) {
@@ -7281,10 +7420,11 @@ const getAiResponse = async ({
7281
7420
  const maxToolIterations = safeMaxToolCalls - 1;
7282
7421
  let previousResponseId;
7283
7422
  for (let i = 0; i <= maxToolIterations; i++) {
7423
+ const tools1 = await getBasicChatTools(agentMode, questionToolEnabled, toolEnablement);
7284
7424
  const request = {
7285
7425
  headers,
7286
7426
  method: 'POST',
7287
- payload: getOpenAiParams(openAiInput, modelId, streamingEnabled, passIncludeObfuscation, await getBasicChatTools(agentMode, questionToolEnabled, toolEnablement), agentMode === 'plan' ? false : webSearchEnabled, safeMaxToolCalls, systemPrompt, previousResponseId, reasoningEffort, supportsReasoningEffort),
7427
+ payload: getOpenAiParams(openAiInput, modelId, streamingEnabled, passIncludeObfuscation, tools1, agentMode === 'plan' ? false : webSearchEnabled, safeMaxToolCalls, systemPrompt, previousResponseId, reasoningEffort, supportsReasoningEffort),
7288
7428
  url: getOpenApiApiEndpoint(openApiApiBaseUrl)
7289
7429
  };
7290
7430
  capture(request);
@@ -7583,6 +7723,7 @@ const handleClickSaveOpenApiApiKey = async state => {
7583
7723
  useChatCoordinatorWorker: updatedState.useChatCoordinatorWorker,
7584
7724
  useChatNetworkWorkerForRequests: updatedState.useChatNetworkWorkerForRequests,
7585
7725
  useMockApi: updatedState.useMockApi,
7726
+ useOwnBackend: updatedState.useOwnBackend,
7586
7727
  userText: previousUserMessage.text
7587
7728
  });
7588
7729
  const parsedMessages = await parseAndStoreMessageContent(updatedState.parsedMessages, assistantMessage);
@@ -7671,6 +7812,7 @@ const handleClickSaveOpenRouterApiKey = async state => {
7671
7812
  useChatCoordinatorWorker: updatedState.useChatCoordinatorWorker,
7672
7813
  useChatNetworkWorkerForRequests: updatedState.useChatNetworkWorkerForRequests,
7673
7814
  useMockApi: updatedState.useMockApi,
7815
+ useOwnBackend: updatedState.useOwnBackend,
7674
7816
  userText: previousUserMessage.text
7675
7817
  });
7676
7818
  const parsedMessages = await parseAndStoreMessageContent(updatedState.parsedMessages, assistantMessage);
@@ -7782,7 +7924,6 @@ Assistant: ${assistantText}`;
7782
7924
  const getAiSessionTitle = async (state, userText, assistantText) => {
7783
7925
  const {
7784
7926
  authAccessToken,
7785
- authEnabled,
7786
7927
  backendUrl,
7787
7928
  models,
7788
7929
  openApiApiBaseUrl,
@@ -7790,21 +7931,28 @@ const getAiSessionTitle = async (state, userText, assistantText) => {
7790
7931
  openRouterApiBaseUrl,
7791
7932
  openRouterApiKey,
7792
7933
  selectedModelId,
7793
- useMockApi
7934
+ useMockApi,
7935
+ useOwnBackend
7794
7936
  } = state;
7795
7937
  if (useMockApi) {
7796
7938
  return '';
7797
7939
  }
7798
7940
  const usesOpenApiModel = isOpenApiModel(selectedModelId, models);
7799
7941
  const usesOpenRouterModel = isOpenRouterModel(selectedModelId, models);
7800
- if (!authEnabled && usesOpenApiModel && !openApiApiKey) {
7801
- return '';
7802
- }
7803
- if (!authEnabled && usesOpenRouterModel && !openRouterApiKey) {
7804
- return '';
7805
- }
7806
- if (!authEnabled && !usesOpenApiModel && !usesOpenRouterModel) {
7807
- return '';
7942
+ if (useOwnBackend) {
7943
+ if (!backendUrl || !authAccessToken) {
7944
+ return '';
7945
+ }
7946
+ } else {
7947
+ if (usesOpenApiModel && !openApiApiKey) {
7948
+ return '';
7949
+ }
7950
+ if (usesOpenRouterModel && !openRouterApiKey) {
7951
+ return '';
7952
+ }
7953
+ if (!usesOpenApiModel && !usesOpenRouterModel) {
7954
+ return '';
7955
+ }
7808
7956
  }
7809
7957
  const titlePrompt = getTitlePrompt(userText, assistantText);
7810
7958
  const promptMessage = {
@@ -7819,7 +7967,6 @@ const getAiSessionTitle = async (state, userText, assistantText) => {
7819
7967
  const titleResponse = await getAiResponse({
7820
7968
  assetDir: state.assetDir,
7821
7969
  authAccessToken,
7822
- authEnabled,
7823
7970
  backendUrl,
7824
7971
  maxToolCalls: state.maxToolCalls,
7825
7972
  messages: [promptMessage],
@@ -7840,6 +7987,7 @@ const getAiSessionTitle = async (state, userText, assistantText) => {
7840
7987
  useChatNetworkWorkerForRequests: state.useChatNetworkWorkerForRequests,
7841
7988
  useChatToolWorker: state.useChatToolWorker,
7842
7989
  useMockApi,
7990
+ useOwnBackend,
7843
7991
  userText: titlePrompt,
7844
7992
  webSearchEnabled: false
7845
7993
  });
@@ -8280,7 +8428,8 @@ const withProvisionedBackgroundSession = async (state, session) => {
8280
8428
  };
8281
8429
  };
8282
8430
  const handleSubmit = async state => {
8283
- const authState = state.authEnabled && state.backendUrl ? await syncBackendAuth(state.backendUrl) : undefined;
8431
+ const shouldSyncBackendAuth = (state.authEnabled || state.useOwnBackend) && !!state.backendUrl;
8432
+ const authState = shouldSyncBackendAuth ? await syncBackendAuth(state.backendUrl) : undefined;
8284
8433
  const effectiveState = authState ? {
8285
8434
  ...state,
8286
8435
  ...authState
@@ -8316,6 +8465,7 @@ const handleSubmit = async state => {
8316
8465
  useChatNetworkWorkerForRequests,
8317
8466
  useChatToolWorker,
8318
8467
  useMockApi,
8468
+ useOwnBackend,
8319
8469
  viewMode,
8320
8470
  webSearchEnabled
8321
8471
  } = effectiveState;
@@ -8533,6 +8683,7 @@ const handleSubmit = async state => {
8533
8683
  useChatNetworkWorkerForRequests,
8534
8684
  useChatToolWorker,
8535
8685
  useMockApi,
8686
+ useOwnBackend,
8536
8687
  userText,
8537
8688
  webSearchEnabled,
8538
8689
  workspaceUri
@@ -9889,7 +10040,6 @@ const createExtensionHostRpc = async () => {
9889
10040
  });
9890
10041
  return rpc;
9891
10042
  } catch (error) {
9892
- // eslint-disable-next-line @typescript-eslint/only-throw-error
9893
10043
  throw new VError(error, `Failed to create extension host rpc`);
9894
10044
  }
9895
10045
  };
@@ -10320,6 +10470,15 @@ const loadUseChatToolWorker = async () => {
10320
10470
  }
10321
10471
  };
10322
10472
 
10473
+ const loadUseOwnBackend = async () => {
10474
+ try {
10475
+ const savedUseOwnBackend = await get('chat.useOwnBackend');
10476
+ return typeof savedUseOwnBackend === 'boolean' ? savedUseOwnBackend : false;
10477
+ } catch {
10478
+ return false;
10479
+ }
10480
+ };
10481
+
10323
10482
  const loadVoiceDictationEnabled = async () => {
10324
10483
  try {
10325
10484
  const savedVoiceDictationEnabled = await get('chatView.voiceDictationEnabled');
@@ -10330,7 +10489,7 @@ const loadVoiceDictationEnabled = async () => {
10330
10489
  };
10331
10490
 
10332
10491
  const loadPreferences = async () => {
10333
- const [aiSessionTitleGenerationEnabled, authEnabled, backendUrl, chatHistoryEnabled, composerDropEnabled, openApiApiKey, openRouterApiKey, emitStreamingFunctionCallEvents, reasoningPickerEnabled, scrollDownButtonEnabled, searchEnabled, streamingEnabled, todoListToolEnabled, toolEnablement, passIncludeObfuscation, useChatCoordinatorWorker, useChatMathWorker, useChatNetworkWorkerForRequests, useChatToolWorker, voiceDictationEnabled] = await Promise.all([loadAiSessionTitleGenerationEnabled(), loadAuthEnabled(), loadBackendUrl(), loadChatHistoryEnabled(), loadComposerDropEnabled(), loadOpenApiApiKey(), loadOpenRouterApiKey(), loadEmitStreamingFunctionCallEvents(), loadReasoningPickerEnabled(), loadScrollDownButtonEnabled(), loadSearchEnabled(), loadStreamingEnabled(), loadTodoListToolEnabled(), loadToolEnablement(), loadPassIncludeObfuscation(), loadUseChatCoordinatorWorker(), loadUseChatMathWorker(), loadUseChatNetworkWorkerForRequests(), loadUseChatToolWorker(), loadVoiceDictationEnabled()]);
10492
+ const [aiSessionTitleGenerationEnabled, authEnabled, backendUrl, chatHistoryEnabled, composerDropEnabled, openApiApiKey, openRouterApiKey, emitStreamingFunctionCallEvents, reasoningPickerEnabled, scrollDownButtonEnabled, searchEnabled, streamingEnabled, todoListToolEnabled, toolEnablement, passIncludeObfuscation, useChatCoordinatorWorker, useChatMathWorker, useChatNetworkWorkerForRequests, useChatToolWorker, useOwnBackend, voiceDictationEnabled] = await Promise.all([loadAiSessionTitleGenerationEnabled(), loadAuthEnabled(), loadBackendUrl(), loadChatHistoryEnabled(), loadComposerDropEnabled(), loadOpenApiApiKey(), loadOpenRouterApiKey(), loadEmitStreamingFunctionCallEvents(), loadReasoningPickerEnabled(), loadScrollDownButtonEnabled(), loadSearchEnabled(), loadStreamingEnabled(), loadTodoListToolEnabled(), loadToolEnablement(), loadPassIncludeObfuscation(), loadUseChatCoordinatorWorker(), loadUseChatMathWorker(), loadUseChatNetworkWorkerForRequests(), loadUseChatToolWorker(), loadUseOwnBackend(), loadVoiceDictationEnabled()]);
10334
10493
  return {
10335
10494
  aiSessionTitleGenerationEnabled,
10336
10495
  authEnabled,
@@ -10351,6 +10510,7 @@ const loadPreferences = async () => {
10351
10510
  useChatMathWorker,
10352
10511
  useChatNetworkWorkerForRequests,
10353
10512
  useChatToolWorker,
10513
+ useOwnBackend,
10354
10514
  voiceDictationEnabled
10355
10515
  };
10356
10516
  };
@@ -10422,9 +10582,10 @@ const loadContent = async (state, savedState) => {
10422
10582
  useChatMathWorker,
10423
10583
  useChatNetworkWorkerForRequests,
10424
10584
  useChatToolWorker,
10585
+ useOwnBackend,
10425
10586
  voiceDictationEnabled
10426
10587
  } = await loadPreferences();
10427
- const authState = authEnabled && backendUrl ? await syncBackendAuth(backendUrl) : getLoggedOutBackendAuthState();
10588
+ const authState = authEnabled || useOwnBackend ? backendUrl ? await syncBackendAuth(backendUrl) : getLoggedOutBackendAuthState() : getLoggedOutBackendAuthState();
10428
10589
  const legacySavedSessions = getSavedSessions(savedState);
10429
10590
  const storedSessions = await listChatSessions();
10430
10591
  let sessions = storedSessions;
@@ -10532,6 +10693,7 @@ const loadContent = async (state, savedState) => {
10532
10693
  useChatMathWorker,
10533
10694
  useChatNetworkWorkerForRequests,
10534
10695
  useChatToolWorker,
10696
+ useOwnBackend,
10535
10697
  userName: authState.userName,
10536
10698
  userState: authState.userState,
10537
10699
  userSubscriptionPlan: authState.userSubscriptionPlan,
@@ -10557,15 +10719,19 @@ const getDelay = payload => {
10557
10719
  };
10558
10720
  const mockBackendAuthResponse = (state, payload) => {
10559
10721
  const delay = getDelay(payload);
10722
+ const setNextResponse = payload.request === 'refresh' ? setNextRefreshResponse : setNextLoginResponse;
10560
10723
  if (payload.type === 'error') {
10561
- setNextLoginResponse({
10724
+ setNextResponse({
10562
10725
  delay,
10726
+ ...(payload.errorName ? {
10727
+ errorName: payload.errorName
10728
+ } : {}),
10563
10729
  message: payload.message || 'Backend authentication failed.',
10564
10730
  type: 'error'
10565
10731
  });
10566
10732
  return state;
10567
10733
  }
10568
- setNextLoginResponse({
10734
+ setNextResponse({
10569
10735
  delay,
10570
10736
  response: payload,
10571
10737
  type: 'success'
@@ -10573,6 +10739,11 @@ const mockBackendAuthResponse = (state, payload) => {
10573
10739
  return state;
10574
10740
  };
10575
10741
 
10742
+ const mockBackendSetHttpErrorResponse = (state, statusCode, body) => {
10743
+ setHttpErrorResponse(statusCode, body);
10744
+ return state;
10745
+ };
10746
+
10576
10747
  const mockOpenApiRequestGetAll = _state => {
10577
10748
  return getAll();
10578
10749
  };
@@ -10583,7 +10754,7 @@ const mockOpenApiRequestReset = state => {
10583
10754
  };
10584
10755
 
10585
10756
  const mockOpenApiSetHttpErrorResponse = (state, statusCode, body) => {
10586
- setHttpErrorResponse(statusCode, body);
10757
+ setHttpErrorResponse$1(statusCode, body);
10587
10758
  return state;
10588
10759
  };
10589
10760
 
@@ -10685,6 +10856,35 @@ const openMockSession = async (state, mockSessionId, mockChatMessages, options)
10685
10856
  });
10686
10857
  };
10687
10858
 
10859
+ const getMockSession = index => {
10860
+ const sessionNumber = index + 1;
10861
+ return {
10862
+ id: `session-${sessionNumber}`,
10863
+ messages: [],
10864
+ title: `Chat ${sessionNumber}`
10865
+ };
10866
+ };
10867
+ const openMockSessions = async (state, count) => {
10868
+ if (!Number.isSafeInteger(count) || count < 0) {
10869
+ return state;
10870
+ }
10871
+ const sessions = Array.from({
10872
+ length: count
10873
+ }, (_, index) => getMockSession(index));
10874
+ return {
10875
+ ...state,
10876
+ chatListScrollTop: 0,
10877
+ composerAttachments: [],
10878
+ composerAttachmentsHeight: 0,
10879
+ listFocusedIndex: -1,
10880
+ parsedMessages: [],
10881
+ renamingSessionId: '',
10882
+ selectedSessionId: '',
10883
+ sessions,
10884
+ viewMode: 'list'
10885
+ };
10886
+ };
10887
+
10688
10888
  const pasteInput = async state => {
10689
10889
  const text = await readText();
10690
10890
  return handleInput(state, Composer, text, 'script');
@@ -14986,7 +15186,6 @@ const getRenderer = diffType => {
14986
15186
  case RenderValue:
14987
15187
  return renderValue;
14988
15188
  default:
14989
- // eslint-disable-next-line @typescript-eslint/only-throw-error
14990
15189
  throw new Error('unknown renderer');
14991
15190
  }
14992
15191
  };
@@ -15518,6 +15717,18 @@ const setUseChatNetworkWorkerForRequests = async (state, useChatNetworkWorkerFor
15518
15717
  };
15519
15718
  };
15520
15719
 
15720
+ const setUseOwnBackend = async (state, useOwnBackend, persist = true) => {
15721
+ if (persist) {
15722
+ await update({
15723
+ 'chat.useOwnBackend': useOwnBackend
15724
+ });
15725
+ }
15726
+ return {
15727
+ ...state,
15728
+ useOwnBackend
15729
+ };
15730
+ };
15731
+
15521
15732
  const defaultMockApiCommandId = 'ChatE2e.mockApi';
15522
15733
  const useMockApi = (state, value, mockApiCommandId = defaultMockApiCommandId) => {
15523
15734
  if (!value) {
@@ -15618,6 +15829,7 @@ const commandMap = {
15618
15829
  'Chat.loadContent': wrapCommand(loadContent),
15619
15830
  'Chat.loadContent2': wrapCommand(loadContent),
15620
15831
  'Chat.mockBackendAuthResponse': wrapCommand(mockBackendAuthResponse),
15832
+ 'Chat.mockBackendSetHttpErrorResponse': wrapCommand(mockBackendSetHttpErrorResponse),
15621
15833
  'Chat.mockOpenApiRequestGetAll': wrapGetter(mockOpenApiRequestGetAll),
15622
15834
  'Chat.mockOpenApiRequestReset': wrapCommand(mockOpenApiRequestReset),
15623
15835
  'Chat.mockOpenApiSetHttpErrorResponse': wrapCommand(mockOpenApiSetHttpErrorResponse),
@@ -15629,6 +15841,7 @@ const commandMap = {
15629
15841
  'Chat.openGitBranchPicker': wrapCommand(openGitBranchPicker),
15630
15842
  'Chat.openMockProject': wrapCommand(openMockProject),
15631
15843
  'Chat.openMockSession': wrapCommand(openMockSession),
15844
+ 'Chat.openMockSessions': wrapCommand(openMockSessions),
15632
15845
  'Chat.openModelPicker': wrapCommand(openModelPicker),
15633
15846
  'Chat.openReasoningEffortPicker': wrapCommand(openReasoningEffortPicker),
15634
15847
  'Chat.openRunModePicker': wrapCommand(openRunModePicker),
@@ -15662,6 +15875,7 @@ const commandMap = {
15662
15875
  'Chat.setUseChatCoordinatorWorker': wrapCommand(setUseChatCoordinatorWorker),
15663
15876
  'Chat.setUseChatMathWorker': wrapCommand(setUseChatMathWorker),
15664
15877
  'Chat.setUseChatNetworkWorkerForRequests': wrapCommand(setUseChatNetworkWorkerForRequests),
15878
+ 'Chat.setUseOwnBackend': wrapCommand(setUseOwnBackend),
15665
15879
  'Chat.showComposerAttachmentPreviewOverlay': wrapCommand(showComposerAttachmentPreviewOverlay),
15666
15880
  'Chat.terminate': terminate,
15667
15881
  'Chat.useMockApi': wrapCommand(useMockApi)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/chat-view",
3
- "version": "7.6.0",
3
+ "version": "7.9.0",
4
4
  "description": "Chat View Worker",
5
5
  "repository": {
6
6
  "type": "git",