@lvce-editor/chat-view 6.7.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.
@@ -1714,6 +1714,7 @@ const createDefaultState = () => {
1714
1714
  name: '_blank',
1715
1715
  uri: ''
1716
1716
  }],
1717
+ questionToolEnabled: false,
1717
1718
  renamingSessionId: '',
1718
1719
  selectedModelId: defaultModelId,
1719
1720
  selectedProjectId: defaultProjectId,
@@ -1729,10 +1730,10 @@ const createDefaultState = () => {
1729
1730
  tokensUsed: 0,
1730
1731
  uid: 0,
1731
1732
  usageOverviewEnabled: false,
1732
- useChatCoordinatorWorker: true,
1733
+ useChatCoordinatorWorker: false,
1733
1734
  useChatMathWorker: true,
1734
1735
  useChatNetworkWorkerForRequests: false,
1735
- useChatToolWorker: false,
1736
+ useChatToolWorker: true,
1736
1737
  useMockApi: false,
1737
1738
  userName: '',
1738
1739
  userSubscriptionPlan: '',
@@ -3344,6 +3345,20 @@ const getAiResponse$1 = async options => {
3344
3345
  const execute = async (name, rawArguments, options) => {
3345
3346
  return invoke$3('ChatTool.execute', name, rawArguments, options);
3346
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
+ };
3347
3362
 
3348
3363
  const getToolErrorPayload = error => {
3349
3364
  const rawStack = error && typeof error === 'object' ? Reflect.get(error, 'stack') : undefined;
@@ -3522,12 +3537,19 @@ const parseToolArguments = rawArguments => {
3522
3537
  }
3523
3538
  };
3524
3539
 
3540
+ const stringifyToolOutput = output => {
3541
+ if (typeof output === 'string') {
3542
+ return output;
3543
+ }
3544
+ return JSON.stringify(output) ?? 'null';
3545
+ };
3525
3546
  const executeChatTool = async (name, rawArguments, options) => {
3526
3547
  if (options.useChatToolWorker) {
3527
- return execute(name, rawArguments, {
3548
+ const workerOutput = await execute(name, rawArguments, {
3528
3549
  assetDir: options.assetDir,
3529
3550
  platform: options.platform
3530
3551
  });
3552
+ return stringifyToolOutput(workerOutput);
3531
3553
  }
3532
3554
  const args = parseToolArguments(rawArguments);
3533
3555
  if (name === 'read_file') {
@@ -3545,6 +3567,9 @@ const executeChatTool = async (name, rawArguments, options) => {
3545
3567
  if (name === 'render_html') {
3546
3568
  return executeRenderHtmlTool(args);
3547
3569
  }
3570
+ if (name === 'ask_question') {
3571
+ return executeAskQuestionTool(args);
3572
+ }
3548
3573
  return JSON.stringify({
3549
3574
  error: `Unknown tool: ${name}`
3550
3575
  });
@@ -3553,7 +3578,7 @@ const executeChatTool = async (name, rawArguments, options) => {
3553
3578
  const getReadFileTool = () => {
3554
3579
  return {
3555
3580
  function: {
3556
- 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).',
3557
3582
  name: 'read_file',
3558
3583
  parameters: {
3559
3584
  additionalProperties: false,
@@ -3656,8 +3681,53 @@ const getRenderHtmlTool = () => {
3656
3681
  type: 'function'
3657
3682
  };
3658
3683
  };
3659
- const getBasicChatTools = () => {
3660
- 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
+ }
3661
3731
  };
3662
3732
 
3663
3733
  const getClientRequestIdHeader = () => {
@@ -4767,9 +4837,10 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
4767
4837
  onEventStreamFinished,
4768
4838
  onTextChunk,
4769
4839
  onToolCallsChunk,
4840
+ questionToolEnabled = false,
4770
4841
  stream,
4771
4842
  useChatNetworkWorkerForRequests = false,
4772
- useChatToolWorker = false,
4843
+ useChatToolWorker = true,
4773
4844
  webSearchEnabled = false
4774
4845
  } = options ?? {
4775
4846
  stream: false
@@ -4778,7 +4849,7 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
4778
4849
  content: message.text,
4779
4850
  role: message.role
4780
4851
  }));
4781
- const tools = getBasicChatTools();
4852
+ const tools = await getBasicChatTools(questionToolEnabled);
4782
4853
  const maxToolIterations = 4;
4783
4854
  let previousResponseId;
4784
4855
  for (let i = 0; i <= maxToolIterations; i++) {
@@ -5356,12 +5427,12 @@ const getOpenRouterLimitInfo = async (openRouterApiKey, openRouterApiBaseUrl, us
5356
5427
  }
5357
5428
  return normalizedLimitInfo;
5358
5429
  };
5359
- 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) => {
5360
5431
  const completionMessages = messages.map(message => ({
5361
5432
  content: message.text,
5362
5433
  role: message.role
5363
5434
  }));
5364
- const tools = getBasicChatTools();
5435
+ const tools = await getBasicChatTools(questionToolEnabled);
5365
5436
  const maxToolIterations = 4;
5366
5437
  for (let i = 0; i <= maxToolIterations; i++) {
5367
5438
  let parsed;
@@ -5683,11 +5754,12 @@ const getAiResponse = async ({
5683
5754
  openRouterApiKey,
5684
5755
  passIncludeObfuscation = false,
5685
5756
  platform,
5757
+ questionToolEnabled = false,
5686
5758
  selectedModelId,
5687
5759
  streamingEnabled = true,
5688
5760
  useChatCoordinatorWorker = false,
5689
5761
  useChatNetworkWorkerForRequests = false,
5690
- useChatToolWorker = false,
5762
+ useChatToolWorker = true,
5691
5763
  useMockApi,
5692
5764
  userText,
5693
5765
  webSearchEnabled = false
@@ -5710,6 +5782,7 @@ const getAiResponse = async ({
5710
5782
  openRouterApiKey,
5711
5783
  passIncludeObfuscation,
5712
5784
  platform,
5785
+ questionToolEnabled,
5713
5786
  selectedModelId,
5714
5787
  streamingEnabled,
5715
5788
  useChatNetworkWorkerForRequests,
@@ -5761,7 +5834,7 @@ const getAiResponse = async ({
5761
5834
  capture({
5762
5835
  headers,
5763
5836
  method: 'POST',
5764
- payload: getOpenAiParams(openAiInput, modelId, streamingEnabled, passIncludeObfuscation, getBasicChatTools(), webSearchEnabled, previousResponseId),
5837
+ payload: getOpenAiParams(openAiInput, modelId, streamingEnabled, passIncludeObfuscation, await getBasicChatTools(questionToolEnabled), webSearchEnabled, previousResponseId),
5765
5838
  url: getOpenApiApiEndpoint(openApiApiBaseUrl)
5766
5839
  });
5767
5840
  const result = await getMockOpenApiAssistantText(streamingEnabled, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished);
@@ -5805,6 +5878,7 @@ const getAiResponse = async ({
5805
5878
  ...(onToolCallsChunk ? {
5806
5879
  onToolCallsChunk
5807
5880
  } : {}),
5881
+ questionToolEnabled,
5808
5882
  stream: streamingEnabled,
5809
5883
  useChatNetworkWorkerForRequests,
5810
5884
  useChatToolWorker,
@@ -5834,7 +5908,7 @@ const getAiResponse = async ({
5834
5908
  text = getOpenRouterErrorMessage(result);
5835
5909
  }
5836
5910
  } else if (openRouterApiKey) {
5837
- 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);
5838
5912
  if (result.type === 'success') {
5839
5913
  const {
5840
5914
  text: assistantText
@@ -6051,7 +6125,7 @@ const isAlphaNumeric = value => {
6051
6125
  };
6052
6126
  const sanitizeLinkUrl = url => {
6053
6127
  const normalized = url.trim().toLowerCase();
6054
- 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://')) {
6055
6129
  return url;
6056
6130
  }
6057
6131
  return '#';
@@ -6063,6 +6137,73 @@ const sanitizeImageUrl = url => {
6063
6137
  }
6064
6138
  return '#';
6065
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
+ };
6066
6207
  const parseLinkToken = (value, start) => {
6067
6208
  if (value[start] !== '[') {
6068
6209
  return undefined;
@@ -6291,7 +6432,7 @@ const parseMathToken = (value, start) => {
6291
6432
  return undefined;
6292
6433
  };
6293
6434
  const parseInlineToken = (value, start) => {
6294
- 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);
6295
6436
  };
6296
6437
  const parseInlineNodes = value => {
6297
6438
  const nodes = [];
@@ -7394,6 +7535,7 @@ const handleSubmit = async state => {
7394
7535
  openRouterApiKey,
7395
7536
  passIncludeObfuscation,
7396
7537
  platform,
7538
+ questionToolEnabled,
7397
7539
  selectedModelId,
7398
7540
  selectedSessionId,
7399
7541
  sessions,
@@ -7562,6 +7704,9 @@ const handleSubmit = async state => {
7562
7704
  openRouterApiKey,
7563
7705
  passIncludeObfuscation,
7564
7706
  platform,
7707
+ ...(typeof questionToolEnabled === 'boolean' ? {
7708
+ questionToolEnabled
7709
+ } : {}),
7565
7710
  selectedModelId,
7566
7711
  streamingEnabled,
7567
7712
  useChatCoordinatorWorker,
@@ -7875,11 +8020,18 @@ const handleClickNew = async state => {
7875
8020
  return focusInput(clearedState);
7876
8021
  };
7877
8022
 
8023
+ const normalizeFileReferenceUri = uri => {
8024
+ if (uri.startsWith('vscode-references://')) {
8025
+ return `file://${uri.slice('vscode-references://'.length)}`;
8026
+ }
8027
+ return uri;
8028
+ };
7878
8029
  const handleClickReadFile = async uri => {
7879
8030
  if (!uri) {
7880
8031
  return;
7881
8032
  }
7882
- await invoke$1('Main.openUri', uri);
8033
+ const normalizedUri = normalizeFileReferenceUri(uri);
8034
+ await invoke$1('Main.openUri', normalizedUri);
7883
8035
  };
7884
8036
 
7885
8037
  const handleClickSessionDebug = async state => {
@@ -8406,9 +8558,9 @@ const loadUseChatNetworkWorkerForRequests = async () => {
8406
8558
  const loadUseChatToolWorker = async () => {
8407
8559
  try {
8408
8560
  const savedUseChatToolWorker = await get('chatView.useChatToolWorker');
8409
- return typeof savedUseChatToolWorker === 'boolean' ? savedUseChatToolWorker : false;
8561
+ return typeof savedUseChatToolWorker === 'boolean' ? savedUseChatToolWorker : true;
8410
8562
  } catch {
8411
- return false;
8563
+ return true;
8412
8564
  }
8413
8565
  };
8414
8566
 
@@ -9045,6 +9197,29 @@ const getCss = (composerHeight, listItemHeight, chatMessageFontSize, chatMessage
9045
9197
  .TokenProperty {
9046
9198
  color: var(--ColorChartsOrange, #ef9f76);
9047
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
+ }
9048
9223
  `;
9049
9224
  if (!renderHtmlCss.trim()) {
9050
9225
  return baseCss;
@@ -9147,6 +9322,9 @@ const ChatMessageContent = 'ChatMessageContent';
9147
9322
  const ChatToolCalls = 'ChatToolCalls';
9148
9323
  const ChatToolCallsLabel = 'ChatToolCallsLabel';
9149
9324
  const ChatToolCallReadFileLink = 'ChatToolCallReadFileLink';
9325
+ const ChatToolCallQuestionOption = 'ChatToolCallQuestionOption';
9326
+ const ChatToolCallQuestionOptions = 'ChatToolCallQuestionOptions';
9327
+ const ChatToolCallQuestionText = 'ChatToolCallQuestionText';
9150
9328
  const ChatToolCallRenderHtmlLabel = 'ChatToolCallRenderHtmlLabel';
9151
9329
  const ChatToolCallRenderHtmlContent = 'ChatToolCallRenderHtmlContent';
9152
9330
  const ChatToolCallRenderHtmlBody = 'ChatToolCallRenderHtmlBody';
@@ -9337,8 +9515,9 @@ const getImageAltText = alt => {
9337
9515
  }
9338
9516
  return `${alt} (image could not be loaded)`;
9339
9517
  };
9340
- const isFileUri = href => {
9341
- 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://');
9342
9521
  };
9343
9522
  const getInlineNodeDom = (inlineNode, useChatMathWorker = false) => {
9344
9523
  if (inlineNode.type === 'text') {
@@ -9385,7 +9564,7 @@ const getInlineNodeDom = (inlineNode, useChatMathWorker = false) => {
9385
9564
  if (inlineNode.type === 'math-inline-dom') {
9386
9565
  return inlineNode.dom;
9387
9566
  }
9388
- if (isFileUri(inlineNode.href)) {
9567
+ if (isFileReferenceUri(inlineNode.href)) {
9389
9568
  return [{
9390
9569
  childCount: 1,
9391
9570
  className: ChatMessageLink,
@@ -9486,6 +9665,19 @@ const pythonRules = [{
9486
9665
  className: TokenKeyword,
9487
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/
9488
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
+ }];
9489
9681
  const tokenize = (code, rules) => {
9490
9682
  const tokens = [];
9491
9683
  let pos = 0;
@@ -9542,6 +9734,9 @@ const highlightCode = (code, language) => {
9542
9734
  if (normalized === 'py' || normalized === 'python') {
9543
9735
  return tokenize(code, pythonRules);
9544
9736
  }
9737
+ if (normalized === 'json') {
9738
+ return tokenize(code, jsonRules);
9739
+ }
9545
9740
  return [{
9546
9741
  className: '',
9547
9742
  text: code
@@ -9810,6 +10005,15 @@ const getOpenRouterTooManyRequestsDom = () => {
9810
10005
  })];
9811
10006
  };
9812
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
+
9813
10017
  const getToolCallArgumentPreview = rawArguments => {
9814
10018
  if (!rawArguments.trim()) {
9815
10019
  return '""';
@@ -9837,13 +10041,69 @@ const getToolCallArgumentPreview = rawArguments => {
9837
10041
  return rawArguments;
9838
10042
  };
9839
10043
 
9840
- const RE_QUERY_OR_HASH = /[?#].*$/;
9841
- const RE_TRAILING_SLASH = /\/$/;
9842
- const getFileNameFromUri = uri => {
9843
- const stripped = uri.replace(RE_QUERY_OR_HASH, '').replace(RE_TRAILING_SLASH, '');
9844
- const slashIndex = Math.max(stripped.lastIndexOf('/'), stripped.lastIndexOf('\\\\'));
9845
- const fileName = slashIndex === -1 ? stripped : stripped.slice(slashIndex + 1);
9846
- 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)')])];
9847
10107
  };
9848
10108
 
9849
10109
  const isCompleteJson = value => {
@@ -9916,19 +10176,6 @@ const getReadFileTarget = rawArguments => {
9916
10176
  };
9917
10177
  };
9918
10178
 
9919
- const getToolCallStatusLabel = toolCall => {
9920
- if (toolCall.status === 'not-found') {
9921
- return ' (not-found)';
9922
- }
9923
- if (toolCall.status === 'error') {
9924
- if (toolCall.errorMessage) {
9925
- return ` (error: ${toolCall.errorMessage})`;
9926
- }
9927
- return ' (error)';
9928
- }
9929
- return '';
9930
- };
9931
-
9932
10179
  const getToolCallReadFileVirtualDom = toolCall => {
9933
10180
  const target = getReadFileTarget(toolCall.arguments);
9934
10181
  if (!target) {
@@ -10269,9 +10516,6 @@ const getToolCallDisplayName = name => {
10269
10516
  };
10270
10517
  const getToolCallLabel = toolCall => {
10271
10518
  const displayName = getToolCallDisplayName(toolCall.name);
10272
- if (toolCall.name === 'getWorkspaceUri' && toolCall.result) {
10273
- return `${displayName} ${toolCall.result}`;
10274
- }
10275
10519
  const argumentPreview = getToolCallArgumentPreview(toolCall.arguments);
10276
10520
  const statusLabel = getToolCallStatusLabel(toolCall);
10277
10521
  if (argumentPreview === '{}') {
@@ -10279,7 +10523,36 @@ const getToolCallLabel = toolCall => {
10279
10523
  }
10280
10524
  return `${displayName} ${argumentPreview}${statusLabel}`;
10281
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
+ };
10282
10549
  const getToolCallDom = toolCall => {
10550
+ if (toolCall.name === 'getWorkspaceUri') {
10551
+ const virtualDom = getToolCallGetWorkspaceUriVirtualDom(toolCall);
10552
+ if (virtualDom.length > 0) {
10553
+ return virtualDom;
10554
+ }
10555
+ }
10283
10556
  if (toolCall.name === 'read_file' || toolCall.name === 'list_files' || toolCall.name === 'list_file') {
10284
10557
  const virtualDom = getToolCallReadFileVirtualDom(toolCall);
10285
10558
  if (virtualDom.length > 0) {
@@ -10292,6 +10565,12 @@ const getToolCallDom = toolCall => {
10292
10565
  return virtualDom;
10293
10566
  }
10294
10567
  }
10568
+ if (toolCall.name === 'ask_question') {
10569
+ const virtualDom = getToolCallAskQuestionVirtualDom(toolCall);
10570
+ if (virtualDom.length > 0) {
10571
+ return virtualDom;
10572
+ }
10573
+ }
10295
10574
  const label = getToolCallLabel(toolCall);
10296
10575
  return [{
10297
10576
  childCount: 1,
@@ -11151,6 +11430,13 @@ const setEmitStreamingFunctionCallEvents = (state, emitStreamingFunctionCallEven
11151
11430
  };
11152
11431
  };
11153
11432
 
11433
+ const setQuestionToolEnabled = (state, questionToolEnabled) => {
11434
+ return {
11435
+ ...state,
11436
+ questionToolEnabled
11437
+ };
11438
+ };
11439
+
11154
11440
  const setStreamingEnabled = (state, streamingEnabled) => {
11155
11441
  return {
11156
11442
  ...state,
@@ -11272,6 +11558,7 @@ const commandMap = {
11272
11558
  'Chat.setChatList': wrapCommand(setChatList),
11273
11559
  'Chat.setEmitStreamingFunctionCallEvents': wrapCommand(setEmitStreamingFunctionCallEvents),
11274
11560
  'Chat.setOpenRouterApiKey': wrapCommand(setOpenRouterApiKey),
11561
+ 'Chat.setQuestionToolEnabled': wrapCommand(setQuestionToolEnabled),
11275
11562
  'Chat.setStreamingEnabled': wrapCommand(setStreamingEnabled),
11276
11563
  'Chat.setUseChatCoordinatorWorker': wrapCommand(setUseChatCoordinatorWorker),
11277
11564
  'Chat.setUseChatMathWorker': wrapCommand(setUseChatMathWorker),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/chat-view",
3
- "version": "6.7.0",
3
+ "version": "6.8.0",
4
4
  "description": "Chat View Worker",
5
5
  "repository": {
6
6
  "type": "git",