@lvce-editor/chat-view 7.4.0 → 7.6.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.
@@ -1350,6 +1350,9 @@ const sendMessagePortToChatStorageWorker$1 = async port => {
1350
1350
  const activateByEvent$1 = (event, assetDir, platform) => {
1351
1351
  return invoke('ExtensionHostManagement.activateByEvent', event, assetDir, platform);
1352
1352
  };
1353
+ const getWorkspacePath = () => {
1354
+ return invoke('Workspace.getPath');
1355
+ };
1353
1356
  const sendMessagePortToTextMeasurementWorker$1 = async port => {
1354
1357
  const command = 'TextMeasurement.handleMessagePort';
1355
1358
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToTextMeasurementWorker', port, command, 0);
@@ -1896,6 +1899,7 @@ const Commit = 'Commit';
1896
1899
  const OpenTerminal = 'Open Terminal';
1897
1900
  const ShowDiff = 'Show Diff';
1898
1901
  const CreatePullRequest$2 = 'Create PR';
1902
+ const Stop$1 = 'stop';
1899
1903
  const Save = 'Save';
1900
1904
  const Saving = 'Saving...';
1901
1905
  const GetApiKey = 'Get API Key';
@@ -2046,6 +2050,9 @@ const showDiff = () => {
2046
2050
  const createPullRequest = () => {
2047
2051
  return i18nString(CreatePullRequest$2);
2048
2052
  };
2053
+ const stop = () => {
2054
+ return i18nString(Stop$1);
2055
+ };
2049
2056
  const save = () => {
2050
2057
  return i18nString(Save);
2051
2058
  };
@@ -2222,6 +2229,7 @@ Use available project context to provide accurate, practical coding help.
2222
2229
  Prefer using available tools to inspect and modify files in the current workspace.
2223
2230
  When asked to create or update code, read relevant files first and apply changes directly in files instead of only pasting raw code in chat.
2224
2231
  Only provide raw code snippets when explicitly requested or when file editing tools are unavailable.
2232
+ When mentioning inline commands, file names, identifiers, or short code fragments in responses, wrap them in markdown backticks, for example \`nvm install 24.14.1\`.
2225
2233
  When displaying code blocks in responses, use markdown triple backticks (\`\`\`) fences.
2226
2234
  When referencing workspace files in responses (including "files added/changed" lists), use markdown links so users can click them.
2227
2235
  Prefer file links like [src/index.ts]({{workspaceUri}}/src/index.ts) and avoid plain text file paths when a link is appropriate.
@@ -2291,11 +2299,16 @@ const getReasoningEffortLabel = reasoningEffort => {
2291
2299
  }
2292
2300
  };
2293
2301
 
2302
+ const defaultToolEnablement = {
2303
+ run_in_terminal: false
2304
+ };
2294
2305
  const parseToolEnablement = value => {
2295
2306
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
2296
- return {};
2307
+ return defaultToolEnablement;
2297
2308
  }
2298
- const toolEnablement = {};
2309
+ const toolEnablement = {
2310
+ ...defaultToolEnablement
2311
+ };
2299
2312
  for (const [key, enabled] of Object.entries(value)) {
2300
2313
  if (typeof enabled === 'boolean') {
2301
2314
  toolEnablement[key] = enabled;
@@ -2320,10 +2333,13 @@ const validateToolEnablement = value => {
2320
2333
  };
2321
2334
  const isToolEnabled = (toolEnablement, toolName) => {
2322
2335
  if (!toolEnablement) {
2323
- return true;
2336
+ return defaultToolEnablement[toolName] ?? true;
2324
2337
  }
2325
2338
  const enabled = toolEnablement[toolName];
2326
- return typeof enabled === 'boolean' ? enabled : true;
2339
+ if (typeof enabled === 'boolean') {
2340
+ return enabled;
2341
+ }
2342
+ return defaultToolEnablement[toolName] ?? true;
2327
2343
  };
2328
2344
  const filterEnabledTools = (tools, toolEnablement) => {
2329
2345
  return tools.filter(tool => isToolEnabled(toolEnablement, tool.function.name));
@@ -2399,7 +2415,7 @@ const createDefaultState = () => {
2399
2415
  maxToolCalls: defaultMaxToolCalls,
2400
2416
  messagesAutoScrollEnabled: true,
2401
2417
  messagesScrollTop: 0,
2402
- mockAiResponseDelay: 800,
2418
+ mockAiResponseDelay: 0,
2403
2419
  mockApiCommandId: '',
2404
2420
  mockOpenApiRequests: [],
2405
2421
  modelPickerHeaderHeight,
@@ -2431,6 +2447,8 @@ const createDefaultState = () => {
2431
2447
  name: '_blank',
2432
2448
  uri: ''
2433
2449
  }],
2450
+ projectSidebarResizing: false,
2451
+ projectSidebarWidth: 280,
2434
2452
  questionToolEnabled: false,
2435
2453
  reasoningEffort: defaultReasoningEffort,
2436
2454
  reasoningEffortPickerOpen: false,
@@ -2450,6 +2468,7 @@ const createDefaultState = () => {
2450
2468
  id: defaultSessionId,
2451
2469
  messages: [],
2452
2470
  projectId: defaultProjectId,
2471
+ status: 'idle',
2453
2472
  title: defaultSessionTitle()
2454
2473
  }],
2455
2474
  showRunMode: true,
@@ -2525,6 +2544,9 @@ const listChatSessions = async () => {
2525
2544
  ...(session.pullRequestUrl ? {
2526
2545
  pullRequestUrl: session.pullRequestUrl
2527
2546
  } : {}),
2547
+ ...(session.status ? {
2548
+ status: session.status
2549
+ } : {}),
2528
2550
  title: session.title,
2529
2551
  ...(session.workspaceUri ? {
2530
2552
  workspaceUri: session.workspaceUri
@@ -2553,6 +2575,9 @@ const getChatSession = async id => {
2553
2575
  ...(session.pullRequestUrl ? {
2554
2576
  pullRequestUrl: session.pullRequestUrl
2555
2577
  } : {}),
2578
+ ...(session.status ? {
2579
+ status: session.status
2580
+ } : {}),
2556
2581
  title: session.title,
2557
2582
  ...(session.workspaceUri ? {
2558
2583
  workspaceUri: session.workspaceUri
@@ -2574,6 +2599,9 @@ const saveChatSession = async session => {
2574
2599
  ...(session.pullRequestUrl ? {
2575
2600
  pullRequestUrl: session.pullRequestUrl
2576
2601
  } : {}),
2602
+ ...(session.status ? {
2603
+ status: session.status
2604
+ } : {}),
2577
2605
  title: session.title,
2578
2606
  ...(session.workspaceUri ? {
2579
2607
  workspaceUri: session.workspaceUri
@@ -2835,7 +2863,7 @@ const getRenderHtmlCss = (sessions, selectedSessionId) => {
2835
2863
  const isEqual$1 = (oldState, newState) => {
2836
2864
  const oldRenderHtmlCss = getRenderHtmlCss(oldState.sessions, oldState.selectedSessionId);
2837
2865
  const newRenderHtmlCss = getRenderHtmlCss(newState.sessions, newState.selectedSessionId);
2838
- return oldState.initial === newState.initial && oldState.chatMessageFontFamily === newState.chatMessageFontFamily && oldState.chatMessageFontSize === newState.chatMessageFontSize && oldState.chatMessageLineHeight === newState.chatMessageLineHeight && oldState.chatFocusContentMaxWidth === newState.chatFocusContentMaxWidth && oldState.chatSendAreaPaddingTop === newState.chatSendAreaPaddingTop && oldState.chatSendAreaPaddingLeft === newState.chatSendAreaPaddingLeft && oldState.chatSendAreaPaddingRight === newState.chatSendAreaPaddingRight && oldState.chatSendAreaPaddingBottom === newState.chatSendAreaPaddingBottom && oldState.composerHeight === newState.composerHeight && oldState.composerAttachmentsHeight === newState.composerAttachmentsHeight && oldState.modelPickerHeight === newState.modelPickerHeight && oldState.composerLineHeight === newState.composerLineHeight && oldState.composerFontFamily === newState.composerFontFamily && oldState.composerFontSize === newState.composerFontSize && oldState.listItemHeight === newState.listItemHeight && oldState.textAreaPaddingTop === newState.textAreaPaddingTop && oldState.textAreaPaddingLeft === newState.textAreaPaddingLeft && oldState.textAreaPaddingRight === newState.textAreaPaddingRight && oldState.textAreaPaddingBottom === newState.textAreaPaddingBottom && oldRenderHtmlCss === newRenderHtmlCss;
2866
+ return oldState.initial === newState.initial && oldState.chatMessageFontFamily === newState.chatMessageFontFamily && oldState.chatMessageFontSize === newState.chatMessageFontSize && oldState.chatMessageLineHeight === newState.chatMessageLineHeight && oldState.chatFocusContentMaxWidth === newState.chatFocusContentMaxWidth && oldState.projectSidebarWidth === newState.projectSidebarWidth && oldState.chatSendAreaPaddingTop === newState.chatSendAreaPaddingTop && oldState.chatSendAreaPaddingLeft === newState.chatSendAreaPaddingLeft && oldState.chatSendAreaPaddingRight === newState.chatSendAreaPaddingRight && oldState.chatSendAreaPaddingBottom === newState.chatSendAreaPaddingBottom && oldState.composerHeight === newState.composerHeight && oldState.composerAttachmentsHeight === newState.composerAttachmentsHeight && oldState.modelPickerHeight === newState.modelPickerHeight && oldState.composerLineHeight === newState.composerLineHeight && oldState.composerFontFamily === newState.composerFontFamily && oldState.composerFontSize === newState.composerFontSize && oldState.listItemHeight === newState.listItemHeight && oldState.textAreaPaddingTop === newState.textAreaPaddingTop && oldState.textAreaPaddingLeft === newState.textAreaPaddingLeft && oldState.textAreaPaddingRight === newState.textAreaPaddingRight && oldState.textAreaPaddingBottom === newState.textAreaPaddingBottom && oldRenderHtmlCss === newRenderHtmlCss;
2839
2867
  };
2840
2868
 
2841
2869
  const diffFocus = (oldState, newState) => {
@@ -3459,6 +3487,7 @@ const FocusOpenInVsCode = 'focus-open-in-vscode';
3459
3487
  const FocusOpenTerminal = 'focus-open-terminal';
3460
3488
  const FocusShowDiff = 'focus-show-diff';
3461
3489
  const Send = 'send';
3490
+ const Stop = 'stop';
3462
3491
  const ScrollDown = 'scroll-down';
3463
3492
  const Back = 'back';
3464
3493
  const ModelPickerToggle = 'model-picker-toggle';
@@ -3890,37 +3919,27 @@ const openFolder = async () => {
3890
3919
  }
3891
3920
  };
3892
3921
 
3893
- // cspell:ignore gitdir worktrees
3894
- const FileTypeFile = 1;
3895
- const FileTypeDirectory = 2;
3896
- const slashAtEndRegex = /\/$/;
3897
- const gitDirPointerRegex = /^gitdir:\s*(.+)$/m;
3898
- const headRefRegex = /^ref:\s+refs\/heads\/(.+)$/m;
3899
- const toGitUri = (baseUri, ...segments) => {
3900
- const url = new URL(baseUri.endsWith('/') ? baseUri : `${baseUri}/`);
3901
- for (const segment of segments) {
3902
- url.pathname = `${url.pathname.replace(slashAtEndRegex, '')}/${segment.split('/').map(part => encodeURIComponent(part)).join('/')}`;
3903
- }
3904
- return url.toString();
3905
- };
3906
- const decodeFileContent = content => {
3907
- if (typeof content === 'string') {
3908
- return content;
3909
- }
3910
- if (content instanceof Uint8Array) {
3911
- return new TextDecoder().decode(content);
3912
- }
3913
- if (Array.isArray(content)) {
3914
- return new TextDecoder().decode(new Uint8Array(content));
3915
- }
3916
- return '';
3917
- };
3918
- const toFileSystemPath = uri => {
3919
- if (!uri.startsWith('file://')) {
3920
- return uri;
3922
+ const parseEntries = value => {
3923
+ if (!Array.isArray(value)) {
3924
+ return [];
3921
3925
  }
3922
- return decodeURIComponent(new URL(uri).pathname);
3926
+ return value.map(entry => {
3927
+ if (Array.isArray(entry) && typeof entry[0] === 'string' && typeof entry[1] === 'number') {
3928
+ return {
3929
+ name: entry[0],
3930
+ type: entry[1]
3931
+ };
3932
+ }
3933
+ if (entry && typeof entry === 'object' && typeof Reflect.get(entry, 'name') === 'string' && typeof Reflect.get(entry, 'type') === 'number') {
3934
+ return {
3935
+ name: Reflect.get(entry, 'name'),
3936
+ type: Reflect.get(entry, 'type')
3937
+ };
3938
+ }
3939
+ return undefined;
3940
+ }).filter(entry => !!entry);
3923
3941
  };
3942
+
3924
3943
  const getRelativePath = (fromPath, toPath) => {
3925
3944
  if (!fromPath.startsWith('/') || !toPath.startsWith('/')) {
3926
3945
  return toPath;
@@ -3935,6 +3954,14 @@ const getRelativePath = (fromPath, toPath) => {
3935
3954
  const childSegments = toParts.slice(commonPrefixLength);
3936
3955
  return [...parentSegments, ...childSegments].join('/') || '.';
3937
3956
  };
3957
+
3958
+ const toFileSystemPath = uri => {
3959
+ if (!uri.startsWith('file://')) {
3960
+ return uri;
3961
+ }
3962
+ return decodeURIComponent(new URL(uri).pathname);
3963
+ };
3964
+
3938
3965
  const toFileSystemTarget = (workspaceUri, uri) => {
3939
3966
  const workspacePath = toFileSystemPath(workspaceUri);
3940
3967
  const fileSystemPath = toFileSystemPath(uri);
@@ -3943,34 +3970,56 @@ const toFileSystemTarget = (workspaceUri, uri) => {
3943
3970
  }
3944
3971
  return getRelativePath(workspacePath, fileSystemPath);
3945
3972
  };
3946
- const parseEntries = value => {
3947
- if (!Array.isArray(value)) {
3948
- return [];
3973
+
3974
+ const readDir = async (workspaceUri, uri) => {
3975
+ const result = await invoke('FileSystem.readDirWithFileTypes', toFileSystemTarget(workspaceUri, uri));
3976
+ return parseEntries(result);
3977
+ };
3978
+
3979
+ const slashAtEndRegex = /\/$/;
3980
+ const toGitUri = (baseUri, ...segments) => {
3981
+ const url = new URL(baseUri.endsWith('/') ? baseUri : `${baseUri}/`);
3982
+ for (const segment of segments) {
3983
+ url.pathname = `${url.pathname.replace(slashAtEndRegex, '')}/${segment.split('/').map(part => encodeURIComponent(part)).join('/')}`;
3949
3984
  }
3950
- return value.map(entry => {
3951
- if (Array.isArray(entry) && typeof entry[0] === 'string' && typeof entry[1] === 'number') {
3952
- return {
3953
- name: entry[0],
3954
- type: entry[1]
3955
- };
3985
+ return url.toString();
3986
+ };
3987
+
3988
+ const FileTypeFile = 1;
3989
+ const FileTypeDirectory = 2;
3990
+ const collectBranchNames = async (workspaceUri, refsHeadsUri, prefix, branches) => {
3991
+ const entries = await readDir(workspaceUri, refsHeadsUri);
3992
+ for (const entry of entries) {
3993
+ if (entry.type === FileTypeDirectory) {
3994
+ await collectBranchNames(workspaceUri, toGitUri(refsHeadsUri, entry.name), `${prefix}${entry.name}/`, branches);
3995
+ continue;
3956
3996
  }
3957
- if (entry && typeof entry === 'object' && typeof Reflect.get(entry, 'name') === 'string' && typeof Reflect.get(entry, 'type') === 'number') {
3958
- return {
3959
- name: Reflect.get(entry, 'name'),
3960
- type: Reflect.get(entry, 'type')
3961
- };
3997
+ if (entry.type === FileTypeFile) {
3998
+ branches.add(`${prefix}${entry.name}`);
3962
3999
  }
3963
- return undefined;
3964
- }).filter(entry => !!entry);
4000
+ }
3965
4001
  };
3966
- const readDir = async (workspaceUri, uri) => {
3967
- const result = await invoke('FileSystem.readDirWithFileTypes', toFileSystemTarget(workspaceUri, uri));
3968
- return parseEntries(result);
4002
+
4003
+ const decodeFileContent = content => {
4004
+ if (typeof content === 'string') {
4005
+ return content;
4006
+ }
4007
+ if (content instanceof Uint8Array) {
4008
+ return new TextDecoder().decode(content);
4009
+ }
4010
+ if (Array.isArray(content)) {
4011
+ return new TextDecoder().decode(new Uint8Array(content));
4012
+ }
4013
+ return '';
3969
4014
  };
4015
+
3970
4016
  const readTextFile = async (workspaceUri, uri) => {
3971
4017
  const result = await invoke('FileSystem.readFile', toFileSystemTarget(workspaceUri, uri));
3972
4018
  return decodeFileContent(result);
3973
4019
  };
4020
+
4021
+ // cspell:ignore gitdir worktrees
4022
+ const gitDirPointerRegex = /^gitdir:\s*(.+)$/m;
3974
4023
  const getGitDirUri = async workspaceUri => {
3975
4024
  const gitUri = toGitUri(workspaceUri, '.git');
3976
4025
  try {
@@ -3986,18 +4035,8 @@ const getGitDirUri = async workspaceUri => {
3986
4035
  }
3987
4036
  return new URL(match[1].trim(), workspaceUri.endsWith('/') ? workspaceUri : `${workspaceUri}/`).toString();
3988
4037
  };
3989
- const collectBranchNames = async (workspaceUri, refsHeadsUri, prefix, branches) => {
3990
- const entries = await readDir(workspaceUri, refsHeadsUri);
3991
- for (const entry of entries) {
3992
- if (entry.type === FileTypeDirectory) {
3993
- await collectBranchNames(workspaceUri, toGitUri(refsHeadsUri, entry.name), `${prefix}${entry.name}/`, branches);
3994
- continue;
3995
- }
3996
- if (entry.type === FileTypeFile) {
3997
- branches.add(`${prefix}${entry.name}`);
3998
- }
3999
- }
4000
- };
4038
+
4039
+ const headRefRegex = /^ref:\s+refs\/heads\/(.+)$/m;
4001
4040
  const parseCurrentBranch = headContent => {
4002
4041
  const match = headRefRegex.exec(headContent.trim());
4003
4042
  if (match) {
@@ -4005,13 +4044,7 @@ const parseCurrentBranch = headContent => {
4005
4044
  }
4006
4045
  return '';
4007
4046
  };
4008
- const hasGitRepository = async workspaceUri => {
4009
- try {
4010
- return Boolean(await getGitDirUri(workspaceUri));
4011
- } catch {
4012
- return false;
4013
- }
4014
- };
4047
+
4015
4048
  const getGitBranches = async workspaceUri => {
4016
4049
  const gitDirUri = await getGitDirUri(workspaceUri);
4017
4050
  if (!gitDirUri) {
@@ -4044,6 +4077,14 @@ const getGitBranches = async workspaceUri => {
4044
4077
  }));
4045
4078
  };
4046
4079
 
4080
+ const hasGitRepository = async workspaceUri => {
4081
+ try {
4082
+ return Boolean(await getGitDirUri(workspaceUri));
4083
+ } catch {
4084
+ return false;
4085
+ }
4086
+ };
4087
+
4047
4088
  const getSelectedSession = (sessions, selectedSessionId) => {
4048
4089
  return sessions.find(session => session.id === selectedSessionId);
4049
4090
  };
@@ -4206,12 +4247,26 @@ const handleClickCreateProject = async state => {
4206
4247
  };
4207
4248
  };
4208
4249
 
4250
+ const getChatSessionStatus = session => {
4251
+ if (session.status) {
4252
+ return session.status;
4253
+ }
4254
+ const hasInProgressAssistantMessage = session.messages.some(message => message.role === 'assistant' && message.inProgress);
4255
+ if (hasInProgressAssistantMessage) {
4256
+ return 'in-progress';
4257
+ }
4258
+ const hasAssistantMessage = session.messages.some(message => message.role === 'assistant');
4259
+ if (hasAssistantMessage) {
4260
+ return 'finished';
4261
+ }
4262
+ return 'idle';
4263
+ };
4264
+
4209
4265
  const canCreatePullRequest = session => {
4210
4266
  if (!session?.branchName || !session.workspaceUri || session.pullRequestUrl) {
4211
4267
  return false;
4212
4268
  }
4213
- const hasInProgressAssistantMessage = session.messages.some(message => message.role === 'assistant' && message.inProgress);
4214
- if (hasInProgressAssistantMessage) {
4269
+ if (getChatSessionStatus(session) !== 'finished') {
4215
4270
  return false;
4216
4271
  }
4217
4272
  return session.messages.some(message => message.role === 'assistant');
@@ -4642,6 +4697,30 @@ const hasToolError = value => {
4642
4697
  const error = Reflect.get(value, 'error');
4643
4698
  return typeof error === 'string' && error.trim().length > 0;
4644
4699
  };
4700
+ const getTrackedToolExecutionValue = value => {
4701
+ if (typeof value !== 'string') {
4702
+ return value;
4703
+ }
4704
+ try {
4705
+ return JSON.parse(value);
4706
+ } catch {
4707
+ return value;
4708
+ }
4709
+ };
4710
+ const getStoredToolExecutionStatus = result => {
4711
+ let parsed = result;
4712
+ if (typeof result === 'string') {
4713
+ try {
4714
+ parsed = JSON.parse(result);
4715
+ } catch {
4716
+ return 'error';
4717
+ }
4718
+ }
4719
+ if (!parsed || typeof parsed !== 'object') {
4720
+ return 'error';
4721
+ }
4722
+ return hasToolError(parsed) ? 'error' : 'success';
4723
+ };
4645
4724
  const parseWriteFileArguments = rawArguments => {
4646
4725
  let parsed = rawArguments;
4647
4726
  if (typeof rawArguments === 'string') {
@@ -4749,15 +4828,60 @@ const executeChatTool = async (name, rawArguments, options) => {
4749
4828
  // eslint-disable-next-line @typescript-eslint/only-throw-error
4750
4829
  throw new Error('Chat tools must be executed in a web worker environment. Please set useChatToolWorker to true in the options.');
4751
4830
  }
4752
- const workerOutput = await execute(name, rawArguments, {
4831
+ const executionOptions = {
4753
4832
  assetDir: options.assetDir,
4754
4833
  platform: options.platform,
4755
4834
  ...(options.workspaceUri ? {
4756
4835
  workspaceUri: options.workspaceUri
4757
4836
  } : {})
4758
- });
4759
- const outputWithLineCounts = name === 'write_file' ? await withWriteFileLineCounts(workerOutput, rawArguments) : workerOutput;
4760
- return stringifyToolOutput(outputWithLineCounts);
4837
+ };
4838
+ const executionId = options.toolCallId || `${name}-${Date.now()}`;
4839
+ const startedAt = new Date().toISOString();
4840
+ if (options.sessionId) {
4841
+ await appendChatViewEvent({
4842
+ arguments: getTrackedToolExecutionValue(rawArguments),
4843
+ id: executionId,
4844
+ name,
4845
+ options: executionOptions,
4846
+ sessionId: options.sessionId,
4847
+ time: startedAt,
4848
+ timestamp: startedAt,
4849
+ type: 'tool-execution-started'
4850
+ });
4851
+ }
4852
+ try {
4853
+ const workerOutput = await execute(name, rawArguments, executionOptions);
4854
+ const outputWithLineCounts = name === 'write_file' ? await withWriteFileLineCounts(workerOutput, rawArguments) : workerOutput;
4855
+ const result = stringifyToolOutput(outputWithLineCounts);
4856
+ if (options.sessionId) {
4857
+ await appendChatViewEvent({
4858
+ id: executionId,
4859
+ name,
4860
+ result: outputWithLineCounts,
4861
+ sessionId: options.sessionId,
4862
+ status: getStoredToolExecutionStatus(outputWithLineCounts),
4863
+ timestamp: new Date().toISOString(),
4864
+ type: 'tool-execution-finished'
4865
+ });
4866
+ }
4867
+ return result;
4868
+ } catch (error) {
4869
+ const errorResult = {
4870
+ error: error instanceof Error ? error.message : String(error)
4871
+ };
4872
+ if (options.sessionId) {
4873
+ await appendChatViewEvent({
4874
+ id: executionId,
4875
+ name,
4876
+ result: errorResult,
4877
+ sessionId: options.sessionId,
4878
+ status: 'error',
4879
+ timestamp: new Date().toISOString(),
4880
+ type: 'tool-execution-finished'
4881
+ });
4882
+ }
4883
+ throw error;
4884
+ }
4761
4885
  };
4762
4886
 
4763
4887
  const getAskQuestionTool = () => {
@@ -4839,6 +4963,7 @@ const getAttachmentTextPart = attachment => {
4839
4963
  };
4840
4964
  }
4841
4965
  };
4966
+
4842
4967
  const getChatMessageOpenAiContent = message => {
4843
4968
  if (!message.attachments || message.attachments.length === 0) {
4844
4969
  return message.text;
@@ -4870,7 +4995,9 @@ const getClientRequestIdHeader = () => {
4870
4995
  };
4871
4996
 
4872
4997
  const getMockAiResponse = async (userMessage, delayInMs) => {
4873
- await delay(delayInMs);
4998
+ if (delayInMs > 0) {
4999
+ await delay(delayInMs);
5000
+ }
4874
5001
  return `Mock AI response: I received "${userMessage}".`;
4875
5002
  };
4876
5003
 
@@ -5354,6 +5481,51 @@ const makeStreamingApiRequest = async options => {
5354
5481
  return invoke$6('ChatNetwork.makeStreamingApiRequest', options);
5355
5482
  };
5356
5483
 
5484
+ const getNumericCount = parsed => {
5485
+ const count = Reflect.get(parsed, 'count');
5486
+ if (typeof count === 'number' && Number.isFinite(count)) {
5487
+ return count;
5488
+ }
5489
+ const matchCount = Reflect.get(parsed, 'matchCount');
5490
+ if (typeof matchCount === 'number' && Number.isFinite(matchCount)) {
5491
+ return matchCount;
5492
+ }
5493
+ return undefined;
5494
+ };
5495
+ const getArrayCount = parsed => {
5496
+ const matches = Reflect.get(parsed, 'matches');
5497
+ if (Array.isArray(matches)) {
5498
+ return matches.length;
5499
+ }
5500
+ const files = Reflect.get(parsed, 'files');
5501
+ if (Array.isArray(files)) {
5502
+ return files.length;
5503
+ }
5504
+ const results = Reflect.get(parsed, 'results');
5505
+ if (Array.isArray(results)) {
5506
+ return results.length;
5507
+ }
5508
+ return undefined;
5509
+ };
5510
+ const getGlobMatchCount = result => {
5511
+ if (!result) {
5512
+ return undefined;
5513
+ }
5514
+ let parsed;
5515
+ try {
5516
+ parsed = JSON.parse(result);
5517
+ } catch {
5518
+ return undefined;
5519
+ }
5520
+ if (Array.isArray(parsed)) {
5521
+ return parsed.length;
5522
+ }
5523
+ if (!parsed || typeof parsed !== 'object') {
5524
+ return undefined;
5525
+ }
5526
+ return getNumericCount(parsed) ?? getArrayCount(parsed);
5527
+ };
5528
+
5357
5529
  const getTextContent = content => {
5358
5530
  if (typeof content === 'string') {
5359
5531
  return content;
@@ -5518,6 +5690,9 @@ const getToolCallResult = (name, content) => {
5518
5690
  }
5519
5691
  return content;
5520
5692
  }
5693
+ if (name === 'glob') {
5694
+ return getGlobMatchCount(content) === undefined ? undefined : content;
5695
+ }
5521
5696
  if (name !== 'getWorkspaceUri') {
5522
5697
  return undefined;
5523
5698
  }
@@ -6029,6 +6204,7 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
6029
6204
  onToolCallsChunk,
6030
6205
  questionToolEnabled = false,
6031
6206
  reasoningEffort,
6207
+ sessionId,
6032
6208
  stream,
6033
6209
  supportsReasoningEffort = false,
6034
6210
  systemPrompt = '',
@@ -6145,6 +6321,10 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
6145
6321
  const content = await executeChatTool(toolCall.name, toolCall.arguments, {
6146
6322
  assetDir,
6147
6323
  platform,
6324
+ ...(sessionId ? {
6325
+ sessionId
6326
+ } : {}),
6327
+ toolCallId: toolCall.callId,
6148
6328
  ...(toolEnablement ? {
6149
6329
  toolEnablement
6150
6330
  } : {}),
@@ -6298,6 +6478,10 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
6298
6478
  const content = await executeChatTool(toolCall.name, toolCall.arguments, {
6299
6479
  assetDir,
6300
6480
  platform,
6481
+ ...(sessionId ? {
6482
+ sessionId
6483
+ } : {}),
6484
+ toolCallId: toolCall.callId,
6301
6485
  ...(toolEnablement ? {
6302
6486
  toolEnablement
6303
6487
  } : {}),
@@ -6378,6 +6562,10 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
6378
6562
  const content = typeof name === 'string' ? await executeChatTool(name, rawArguments, {
6379
6563
  assetDir,
6380
6564
  platform,
6565
+ ...(sessionId ? {
6566
+ sessionId
6567
+ } : {}),
6568
+ toolCallId: id,
6381
6569
  useChatToolWorker,
6382
6570
  workspaceUri
6383
6571
  }) : '{}';
@@ -6653,7 +6841,7 @@ const getOpenRouterLimitInfo = async (openRouterApiKey, openRouterApiBaseUrl, us
6653
6841
  }
6654
6842
  return normalizedLimitInfo;
6655
6843
  };
6656
- const getOpenRouterAssistantText = async (messages, modelId, openRouterApiKey, openRouterApiBaseUrl, assetDir, platform, useChatNetworkWorkerForRequests = false, useChatToolWorker = true, questionToolEnabled = false, systemPrompt = '', workspaceUri = '', agentMode = defaultAgentMode, toolEnablement) => {
6844
+ const getOpenRouterAssistantText = async (messages, modelId, openRouterApiKey, openRouterApiBaseUrl, assetDir, platform, useChatNetworkWorkerForRequests = false, useChatToolWorker = true, questionToolEnabled = false, systemPrompt = '', workspaceUri = '', agentMode = defaultAgentMode, toolEnablement, sessionId) => {
6657
6845
  const effectiveAgentMode = typeof agentMode === 'boolean' ? defaultAgentMode : agentMode;
6658
6846
  const completionMessages = [...(systemPrompt ? [{
6659
6847
  content: systemPrompt,
@@ -6815,6 +7003,10 @@ const getOpenRouterAssistantText = async (messages, modelId, openRouterApiKey, o
6815
7003
  const content = typeof name === 'string' ? await executeChatTool(name, rawArguments, {
6816
7004
  assetDir,
6817
7005
  platform,
7006
+ ...(sessionId ? {
7007
+ sessionId
7008
+ } : {}),
7009
+ toolCallId: id,
6818
7010
  ...(toolEnablement ? {
6819
7011
  toolEnablement
6820
7012
  } : {}),
@@ -6990,6 +7182,7 @@ const getAiResponse = async ({
6990
7182
  questionToolEnabled = false,
6991
7183
  reasoningEffort,
6992
7184
  selectedModelId,
7185
+ sessionId,
6993
7186
  streamingEnabled = true,
6994
7187
  systemPrompt = '',
6995
7188
  toolEnablement,
@@ -7001,6 +7194,7 @@ const getAiResponse = async ({
7001
7194
  webSearchEnabled = false,
7002
7195
  workspaceUri
7003
7196
  }) => {
7197
+ useChatCoordinatorWorker = false; // TODO enable this
7004
7198
  if (useChatCoordinatorWorker && !authEnabled) {
7005
7199
  try {
7006
7200
  const result = await getAiResponse$1({
@@ -7115,6 +7309,10 @@ const getAiResponse = async ({
7115
7309
  const content = await executeChatTool(toolCall.name, toolCall.arguments, {
7116
7310
  assetDir,
7117
7311
  platform,
7312
+ ...(sessionId ? {
7313
+ sessionId
7314
+ } : {}),
7315
+ toolCallId: toolCall.callId,
7118
7316
  ...(toolEnablement ? {
7119
7317
  toolEnablement
7120
7318
  } : {}),
@@ -7173,6 +7371,9 @@ const getAiResponse = async ({
7173
7371
  ...(reasoningEffort ? {
7174
7372
  reasoningEffort
7175
7373
  } : {}),
7374
+ ...(sessionId ? {
7375
+ sessionId
7376
+ } : {}),
7176
7377
  stream: streamingEnabled,
7177
7378
  supportsReasoningEffort,
7178
7379
  systemPrompt,
@@ -7210,7 +7411,7 @@ const getAiResponse = async ({
7210
7411
  text = getOpenRouterErrorMessage(result);
7211
7412
  }
7212
7413
  } else if (openRouterApiKey) {
7213
- const result = await getOpenRouterAssistantText(messages, modelId, openRouterApiKey, openRouterApiBaseUrl, assetDir, platform, useChatNetworkWorkerForRequests, useChatToolWorker, questionToolEnabled, systemPrompt, workspaceUri, agentMode, toolEnablement);
7414
+ const result = await getOpenRouterAssistantText(messages, modelId, openRouterApiKey, openRouterApiBaseUrl, assetDir, platform, useChatNetworkWorkerForRequests, useChatToolWorker, questionToolEnabled, systemPrompt, workspaceUri, agentMode, toolEnablement, sessionId);
7214
7415
  if (result.type === 'success') {
7215
7416
  const {
7216
7417
  text: assistantText
@@ -7792,24 +7993,31 @@ const updateMessageToolCallsInSelectedSession = (sessions, parsedMessages, selec
7792
7993
  };
7793
7994
  };
7794
7995
 
7795
- const handleToolCallsChunkFunction = async (uid, assistantMessageId, toolCalls, handleTextChunkState) => {
7796
- const selectedSession = getSelectedSession(handleTextChunkState.latestState.sessions, handleTextChunkState.latestState.selectedSessionId);
7996
+ const handleToolCallsChunkFunction = async (uid, sessionId, assistantMessageId, toolCalls, handleTextChunkState) => {
7997
+ const liveState = get$1(uid)?.newState || handleTextChunkState.latestState;
7998
+ const selectedSession = liveState.sessions.find(session => session.id === sessionId);
7797
7999
  if (!selectedSession) {
7798
8000
  return {
7799
- latestState: handleTextChunkState.latestState,
7800
- previousState: handleTextChunkState.previousState
8001
+ latestState: liveState,
8002
+ previousState: liveState
8003
+ };
8004
+ }
8005
+ if (getChatSessionStatus(selectedSession) === 'stopped') {
8006
+ return {
8007
+ latestState: liveState,
8008
+ previousState: liveState
7801
8009
  };
7802
8010
  }
7803
8011
  const assistantMessage = getMessageById(selectedSession.messages, assistantMessageId);
7804
8012
  if (!assistantMessage) {
7805
8013
  return {
7806
- latestState: handleTextChunkState.latestState,
7807
- previousState: handleTextChunkState.previousState
8014
+ latestState: liveState,
8015
+ previousState: liveState
7808
8016
  };
7809
8017
  }
7810
- const updated = updateMessageToolCallsInSelectedSession(handleTextChunkState.latestState.sessions, handleTextChunkState.latestState.parsedMessages, handleTextChunkState.latestState.selectedSessionId, assistantMessageId, toolCalls);
7811
- const nextState = getNextHandleTextChunkState(handleTextChunkState.latestState, updated.parsedMessages, updated.sessions);
7812
- await setAndRerenderHandleTextChunkState(uid, handleTextChunkState.previousState, nextState);
8018
+ const updated = updateMessageToolCallsInSelectedSession(liveState.sessions, liveState.parsedMessages, sessionId, assistantMessageId, toolCalls);
8019
+ const nextState = getNextHandleTextChunkState(liveState, updated.parsedMessages, updated.sessions);
8020
+ await setAndRerenderHandleTextChunkState(uid, liveState, nextState);
7813
8021
  return {
7814
8022
  latestState: nextState,
7815
8023
  previousState: nextState
@@ -7846,32 +8054,39 @@ const updateMessageTextInSelectedSession = async (sessions, parsedMessages, sele
7846
8054
  sessions: updatedSessions
7847
8055
  };
7848
8056
  };
7849
- const handleTextChunkFunction = async (uid, assistantMessageId, chunk, handleTextChunkState) => {
7850
- const selectedSession = handleTextChunkState.latestState.sessions.find(session => session.id === handleTextChunkState.latestState.selectedSessionId);
8057
+ const handleTextChunkFunction = async (uid, sessionId, assistantMessageId, chunk, handleTextChunkState) => {
8058
+ const liveState = get$1(uid)?.newState || handleTextChunkState.latestState;
8059
+ const selectedSession = liveState.sessions.find(session => session.id === sessionId);
7851
8060
  if (!selectedSession) {
7852
8061
  return {
7853
- latestState: handleTextChunkState.latestState,
7854
- previousState: handleTextChunkState.previousState
8062
+ latestState: liveState,
8063
+ previousState: liveState
8064
+ };
8065
+ }
8066
+ if (getChatSessionStatus(selectedSession) === 'stopped') {
8067
+ return {
8068
+ latestState: liveState,
8069
+ previousState: liveState
7855
8070
  };
7856
8071
  }
7857
8072
  const assistantMessage = selectedSession.messages.find(message => message.id === assistantMessageId);
7858
8073
  if (!assistantMessage) {
7859
8074
  return {
7860
- latestState: handleTextChunkState.latestState,
7861
- previousState: handleTextChunkState.previousState
8075
+ latestState: liveState,
8076
+ previousState: liveState
7862
8077
  };
7863
8078
  }
7864
8079
  const updatedText = assistantMessage.text + chunk;
7865
- const updated = await updateMessageTextInSelectedSession(handleTextChunkState.latestState.sessions, handleTextChunkState.latestState.parsedMessages, handleTextChunkState.latestState.selectedSessionId, assistantMessageId, updatedText, true);
8080
+ const updated = await updateMessageTextInSelectedSession(liveState.sessions, liveState.parsedMessages, sessionId, assistantMessageId, updatedText, true);
7866
8081
  const nextState = {
7867
- ...handleTextChunkState.latestState,
7868
- ...(handleTextChunkState.latestState.messagesAutoScrollEnabled ? {
7869
- messagesScrollTop: getNextAutoScrollTop(handleTextChunkState.latestState.messagesScrollTop)
8082
+ ...liveState,
8083
+ ...(liveState.messagesAutoScrollEnabled ? {
8084
+ messagesScrollTop: getNextAutoScrollTop(liveState.messagesScrollTop)
7870
8085
  } : {}),
7871
8086
  parsedMessages: updated.parsedMessages,
7872
8087
  sessions: updated.sessions
7873
8088
  };
7874
- set(uid, handleTextChunkState.previousState, nextState);
8089
+ set(uid, liveState, nextState);
7875
8090
  await invoke('Chat.rerender');
7876
8091
  return {
7877
8092
  latestState: nextState,
@@ -7970,6 +8185,32 @@ const withUpdatedMessageScrollTop = state => {
7970
8185
  };
7971
8186
  };
7972
8187
  const workspaceUriPlaceholder = '{{workspaceUri}}';
8188
+ const getLiveState = uid => {
8189
+ const entry = get$1(uid);
8190
+ return entry?.newState;
8191
+ };
8192
+ const updateSessionStatus = (sessions, sessionId, status) => {
8193
+ return sessions.map(session => {
8194
+ if (session.id !== sessionId) {
8195
+ return session;
8196
+ }
8197
+ return {
8198
+ ...session,
8199
+ status
8200
+ };
8201
+ });
8202
+ };
8203
+ const isSessionStopped = (uid, sessionId) => {
8204
+ const liveState = getLiveState(uid);
8205
+ if (!liveState) {
8206
+ return false;
8207
+ }
8208
+ const session = liveState.sessions.find(item => item.id === sessionId);
8209
+ if (!session) {
8210
+ return false;
8211
+ }
8212
+ return getChatSessionStatus(session) === 'stopped';
8213
+ };
7973
8214
  const clearComposerAttachments = async (sessionId, attachmentIds) => {
7974
8215
  if (!sessionId) {
7975
8216
  return;
@@ -7995,11 +8236,22 @@ const getWorkspaceUri = (state, session) => {
7995
8236
  }
7996
8237
  return getProjectUri(state, session?.projectId || state.selectedProjectId);
7997
8238
  };
7998
- const getEffectiveSystemPrompt = (state, session) => {
7999
- const resolvedSystemPrompt = state.systemPrompt.replaceAll(workspaceUriPlaceholder, getWorkspaceUri(state, session) || 'unknown');
8000
- const currentDateInstructions = `Current date: ${getCurrentDate()}.
8001
-
8002
- Do not assume your knowledge cutoff is the same as the current date.`;
8239
+ const resolveWorkspaceUri = async (state, session) => {
8240
+ const workspaceUri = getWorkspaceUri(state, session);
8241
+ if (workspaceUri) {
8242
+ return workspaceUri;
8243
+ }
8244
+ try {
8245
+ return await getWorkspacePath();
8246
+ } catch {
8247
+ return '';
8248
+ }
8249
+ };
8250
+ const getEffectiveSystemPrompt = (state, workspaceUri) => {
8251
+ const resolvedSystemPrompt = state.systemPrompt.replaceAll(workspaceUriPlaceholder, workspaceUri || 'unknown');
8252
+ const currentDateInstructions = `Current date: ${getCurrentDate()}.
8253
+
8254
+ Do not assume your knowledge cutoff is the same as the current date.`;
8003
8255
  if (!resolvedSystemPrompt) {
8004
8256
  return currentDateInstructions;
8005
8257
  }
@@ -8134,6 +8386,7 @@ const handleSubmit = async state => {
8134
8386
  id: newSessionId,
8135
8387
  messages: streamingEnabled ? [userMessage, inProgressAssistantMessage] : [userMessage],
8136
8388
  projectId: state.selectedProjectId,
8389
+ status: 'in-progress',
8137
8390
  title: `Chat ${workingSessions.length + 1}`
8138
8391
  };
8139
8392
  const provisionedSession = await withProvisionedBackgroundSession(state, newSession);
@@ -8173,7 +8426,8 @@ const handleSubmit = async state => {
8173
8426
  }) : workingSessions;
8174
8427
  const updatedWithUser = appendMessageToSelectedSession(workingSessionsWithProvisionedSession, selectedSessionId, userMessage);
8175
8428
  const updatedSessions = streamingEnabled ? appendMessageToSelectedSession(updatedWithUser, selectedSessionId, inProgressAssistantMessage) : updatedWithUser;
8176
- const selectedSession = updatedSessions.find(session => session.id === selectedSessionId);
8429
+ const updatedSessionsWithStatus = updateSessionStatus(updatedSessions, selectedSessionId, 'in-progress');
8430
+ const selectedSession = updatedSessionsWithStatus.find(session => session.id === selectedSessionId);
8177
8431
  if (selectedSession) {
8178
8432
  await saveChatSession(selectedSession);
8179
8433
  }
@@ -8189,7 +8443,7 @@ const handleSubmit = async state => {
8189
8443
  lastSubmittedSessionId: selectedSessionId,
8190
8444
  nextMessageId: nextMessageId + 1,
8191
8445
  parsedMessages,
8192
- sessions: updatedSessions
8446
+ sessions: updatedSessionsWithStatus
8193
8447
  }));
8194
8448
  optimisticState = withUpdatedChatInputHistory(optimisticState, userText);
8195
8449
  }
@@ -8204,13 +8458,13 @@ const handleSubmit = async state => {
8204
8458
  mockOpenApiRequests
8205
8459
  } = optimisticState;
8206
8460
  const selectedOptimisticSession = optimisticState.sessions.find(session => session.id === optimisticState.selectedSessionId);
8207
- const systemPrompt = getEffectiveSystemPrompt(optimisticState, selectedOptimisticSession);
8208
- const workspaceUri = getWorkspaceUri(optimisticState, selectedOptimisticSession);
8461
+ const workspaceUri = useMockApi ? getWorkspaceUri(optimisticState, selectedOptimisticSession) : await resolveWorkspaceUri(optimisticState, selectedOptimisticSession);
8462
+ const systemPrompt = getEffectiveSystemPrompt(optimisticState, workspaceUri);
8209
8463
  const messages = (selectedOptimisticSession?.messages ?? []).filter(message => !message.inProgress);
8210
8464
  const mentionContextMessage = await getMentionContextMessage(userText);
8211
8465
  const messagesWithMentionContext = mentionContextMessage ? [...messages, mentionContextMessage] : messages;
8212
8466
  const handleTextChunkFunctionRef = streamingEnabled ? async chunk => {
8213
- handleTextChunkState = await handleTextChunkFunction(state.uid, assistantMessageId, chunk, handleTextChunkState);
8467
+ handleTextChunkState = await handleTextChunkFunction(state.uid, optimisticState.selectedSessionId, assistantMessageId, chunk, handleTextChunkState);
8214
8468
  } : undefined;
8215
8469
  const assistantMessage = await getAiResponse({
8216
8470
  agentMode,
@@ -8226,6 +8480,9 @@ const handleSubmit = async state => {
8226
8480
  models,
8227
8481
  nextMessageId: optimisticState.nextMessageId,
8228
8482
  onDataEvent: async value => {
8483
+ if (isSessionStopped(state.uid, optimisticState.selectedSessionId)) {
8484
+ return;
8485
+ }
8229
8486
  if (!emitStreamingFunctionCallEvents && isStreamingFunctionCallEvent(value)) {
8230
8487
  return;
8231
8488
  }
@@ -8238,6 +8495,9 @@ const handleSubmit = async state => {
8238
8495
  });
8239
8496
  },
8240
8497
  onEventStreamFinished: async () => {
8498
+ if (isSessionStopped(state.uid, optimisticState.selectedSessionId)) {
8499
+ return;
8500
+ }
8241
8501
  await appendChatViewEvent({
8242
8502
  sessionId: optimisticState.selectedSessionId,
8243
8503
  timestamp: new Date().toISOString(),
@@ -8252,7 +8512,7 @@ const handleSubmit = async state => {
8252
8512
  onTextChunk: handleTextChunkFunctionRef
8253
8513
  } : {}),
8254
8514
  onToolCallsChunk: async toolCalls => {
8255
- handleTextChunkState = await handleToolCallsChunkFunction(state.uid, assistantMessageId, toolCalls, handleTextChunkState);
8515
+ handleTextChunkState = await handleToolCallsChunkFunction(state.uid, optimisticState.selectedSessionId, assistantMessageId, toolCalls, handleTextChunkState);
8256
8516
  },
8257
8517
  openApiApiBaseUrl,
8258
8518
  openApiApiKey,
@@ -8265,6 +8525,7 @@ const handleSubmit = async state => {
8265
8525
  } : {}),
8266
8526
  reasoningEffort,
8267
8527
  selectedModelId,
8528
+ sessionId: optimisticState.selectedSessionId,
8268
8529
  streamingEnabled,
8269
8530
  systemPrompt,
8270
8531
  toolEnablement,
@@ -8276,6 +8537,9 @@ const handleSubmit = async state => {
8276
8537
  webSearchEnabled,
8277
8538
  workspaceUri
8278
8539
  });
8540
+ if (isSessionStopped(state.uid, optimisticState.selectedSessionId)) {
8541
+ return getLiveState(state.uid) || handleTextChunkState.latestState;
8542
+ }
8279
8543
  const {
8280
8544
  latestState
8281
8545
  } = handleTextChunkState;
@@ -8298,6 +8562,7 @@ const handleSubmit = async state => {
8298
8562
  }
8299
8563
  }
8300
8564
  }
8565
+ updatedSessions = updateSessionStatus(updatedSessions, latestState.selectedSessionId, 'finished');
8301
8566
  const selectedSession = updatedSessions.find(session => session.id === latestState.selectedSessionId);
8302
8567
  if (selectedSession) {
8303
8568
  await saveChatSession(selectedSession);
@@ -8315,6 +8580,36 @@ const handleClickSend = async state => {
8315
8580
  return handleSubmit(state);
8316
8581
  };
8317
8582
 
8583
+ const handleClickStop = async state => {
8584
+ const selectedSession = state.sessions.find(session => session.id === state.selectedSessionId);
8585
+ if (!selectedSession || getChatSessionStatus(selectedSession) !== 'in-progress') {
8586
+ return state;
8587
+ }
8588
+ const updatedSelectedSession = {
8589
+ ...selectedSession,
8590
+ messages: selectedSession.messages.map(message => {
8591
+ if (message.role !== 'assistant' || !message.inProgress) {
8592
+ return message;
8593
+ }
8594
+ return {
8595
+ ...message,
8596
+ inProgress: false
8597
+ };
8598
+ }),
8599
+ status: 'stopped'
8600
+ };
8601
+ await saveChatSession(updatedSelectedSession);
8602
+ return {
8603
+ ...state,
8604
+ sessions: state.sessions.map(session => {
8605
+ if (session.id !== updatedSelectedSession.id) {
8606
+ return session;
8607
+ }
8608
+ return updatedSelectedSession;
8609
+ })
8610
+ };
8611
+ };
8612
+
8318
8613
  const SwitchGitBranch = 'Chat.switchGitBranch';
8319
8614
  const switchGitBranch = async ({
8320
8615
  assetDir,
@@ -8765,6 +9060,8 @@ const handleClick = async (state, name, id = '', eventX = 0, eventY = 0) => {
8765
9060
  return deleteProject(state, id);
8766
9061
  case name === Send:
8767
9062
  return handleClickSend(state);
9063
+ case name === Stop:
9064
+ return handleClickStop(state);
8768
9065
  case name === ScrollDown:
8769
9066
  return {
8770
9067
  ...state,
@@ -9412,10 +9709,57 @@ const handlePointerDownModelPickerList = async state => {
9412
9709
  return state;
9413
9710
  };
9414
9711
 
9712
+ const handlePointerDownProjectSidebarSash = async state => {
9713
+ if (state.projectSidebarResizing) {
9714
+ return state;
9715
+ }
9716
+ return {
9717
+ ...state,
9718
+ projectSidebarResizing: true
9719
+ };
9720
+ };
9721
+
9722
+ const minimumProjectSidebarWidth = 180;
9723
+ const minimumChatAreaWidth = 320;
9724
+ const getProjectSidebarWidth = (state, clientX) => {
9725
+ const availableWidth = state.width > 0 ? state.width : state.projectSidebarWidth + minimumChatAreaWidth;
9726
+ const maximumProjectSidebarWidth = Math.max(minimumProjectSidebarWidth, availableWidth - minimumChatAreaWidth);
9727
+ const nextWidth = Math.round(clientX - state.x);
9728
+ return Math.min(Math.max(nextWidth, minimumProjectSidebarWidth), maximumProjectSidebarWidth);
9729
+ };
9730
+ const resizeProjectSidebar = (state, clientX) => {
9731
+ const projectSidebarWidth = getProjectSidebarWidth(state, clientX);
9732
+ if (projectSidebarWidth === state.projectSidebarWidth) {
9733
+ return state;
9734
+ }
9735
+ return {
9736
+ ...state,
9737
+ projectSidebarWidth
9738
+ };
9739
+ };
9740
+
9741
+ const handlePointerMoveProjectSidebarSash = async (state, clientX) => {
9742
+ if (!state.projectSidebarResizing) {
9743
+ return state;
9744
+ }
9745
+ return resizeProjectSidebar(state, clientX);
9746
+ };
9747
+
9415
9748
  const handlePointerUpModelPickerList = async (state, eventY = 0) => {
9416
9749
  return state;
9417
9750
  };
9418
9751
 
9752
+ const handlePointerUpProjectSidebarSash = async (state, clientX) => {
9753
+ if (!state.projectSidebarResizing) {
9754
+ return state;
9755
+ }
9756
+ const nextState = resizeProjectSidebar(state, clientX);
9757
+ return {
9758
+ ...nextState,
9759
+ projectSidebarResizing: false
9760
+ };
9761
+ };
9762
+
9419
9763
  const handleProjectAddButtonContextMenu = async (state, button, x, y) => {
9420
9764
  const {
9421
9765
  uid
@@ -9703,6 +10047,19 @@ const getSavedProjects = savedState => {
9703
10047
  return validProjects;
9704
10048
  };
9705
10049
 
10050
+ const getSavedProjectSidebarWidth = savedState => {
10051
+ if (!isObject$1(savedState)) {
10052
+ return undefined;
10053
+ }
10054
+ const {
10055
+ projectSidebarWidth
10056
+ } = savedState;
10057
+ if (typeof projectSidebarWidth !== 'number') {
10058
+ return undefined;
10059
+ }
10060
+ return projectSidebarWidth;
10061
+ };
10062
+
9706
10063
  const getSavedReasoningEffort = savedState => {
9707
10064
  if (!isObject$1(savedState)) {
9708
10065
  return undefined;
@@ -9923,7 +10280,7 @@ const loadToolEnablement = async () => {
9923
10280
  const savedToolEnablement = await get('chat.toolEnablement');
9924
10281
  return parseToolEnablement(savedToolEnablement);
9925
10282
  } catch {
9926
- return {};
10283
+ return parseToolEnablement(undefined);
9927
10284
  }
9928
10285
  };
9929
10286
 
@@ -10099,6 +10456,7 @@ const loadContent = async (state, savedState) => {
10099
10456
  const chatListScrollTop = getSavedChatListScrollTop(savedState) ?? state.chatListScrollTop;
10100
10457
  const messagesScrollTop = getSavedMessagesScrollTop(savedState) ?? state.messagesScrollTop;
10101
10458
  const projectListScrollTop = getSavedProjectListScrollTop(savedState) ?? state.projectListScrollTop;
10459
+ const projectSidebarWidth = getSavedProjectSidebarWidth(savedState) ?? state.projectSidebarWidth;
10102
10460
  const savedProjectExpandedIds = getSavedProjectExpandedIds(savedState);
10103
10461
  const projectExpandedIds = (savedProjectExpandedIds || state.projectExpandedIds).filter(id => projects.some(project => project.id === id));
10104
10462
  const reasoningEffort = getSavedReasoningEffort(savedState) ?? state.reasoningEffort;
@@ -10153,6 +10511,8 @@ const loadContent = async (state, savedState) => {
10153
10511
  projectExpandedIds,
10154
10512
  projectListScrollTop,
10155
10513
  projects,
10514
+ projectSidebarResizing: false,
10515
+ projectSidebarWidth,
10156
10516
  reasoningEffort,
10157
10517
  reasoningEffortPickerOpen: false,
10158
10518
  reasoningPickerEnabled,
@@ -10341,7 +10701,7 @@ const removeComposerAttachment = async (state, attachmentId) => {
10341
10701
  return handleRemoveComposerAttachment(state, attachmentId);
10342
10702
  };
10343
10703
 
10344
- const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, listItemHeight, chatMessageFontSize, chatMessageLineHeight, chatMessageFontFamily, chatFocusContentMaxWidth, textAreaPaddingTop, textAreaPaddingLeft, textAreaPaddingRight, textAreaPaddingBottom, chatSendAreaPaddingTop, chatSendAreaPaddingLeft, chatSendAreaPaddingRight, chatSendAreaPaddingBottom, renderHtmlCss) => {
10704
+ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, listItemHeight, chatMessageFontSize, chatMessageLineHeight, chatMessageFontFamily, chatFocusContentMaxWidth, projectSidebarWidth, textAreaPaddingTop, textAreaPaddingLeft, textAreaPaddingRight, textAreaPaddingBottom, chatSendAreaPaddingTop, chatSendAreaPaddingLeft, chatSendAreaPaddingRight, chatSendAreaPaddingBottom, renderHtmlCss) => {
10345
10705
  const buttonsHeight = 20;
10346
10706
  const gap = 10;
10347
10707
  const contentPadding = 10;
@@ -10366,6 +10726,7 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
10366
10726
  --ChatMessageLineHeight: ${chatMessageLineHeight}px;
10367
10727
  --ChatMessageFontFamily: ${chatMessageFontFamily};
10368
10728
  --ChatFocusContentMaxWidth: ${chatFocusContentMaxWidth}px;
10729
+ --ProjectSidebarWidth: ${projectSidebarWidth}px;
10369
10730
  --RunModePickerHeight: ${runModePickerHeight}px;
10370
10731
  }
10371
10732
 
@@ -10418,6 +10779,14 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
10418
10779
  object-fit: cover;
10419
10780
  }
10420
10781
 
10782
+ .ChatComposerAttachmentPreview,
10783
+ .ChatAttachmentPreview,
10784
+ .ChatMessageImage,
10785
+ .ChatComposerAttachmentPreviewOverlayImage,
10786
+ .ImageElement{
10787
+ cursor: default;
10788
+ }
10789
+
10421
10790
  .ChatComposerAttachmentRemoveButton{
10422
10791
  appearance: none;
10423
10792
  background: transparent;
@@ -10679,7 +11048,41 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
10679
11048
  .Viewlet.Chat.ChatFocus{
10680
11049
  display: flex !important;
10681
11050
  min-width: 0;
11051
+ flex-direction: row !important;
11052
+ }
11053
+
11054
+ .ChatFocus > .ProjectSidebar{
11055
+ display: flex;
11056
+ flex: none;
11057
+ flex-direction: column;
11058
+ inline-size: var(--ProjectSidebarWidth);
11059
+ min-inline-size: 0;
11060
+ }
11061
+
11062
+ .ChatFocus > .ProjectSidebar > .ProjectList{
11063
+ flex: 1;
11064
+ min-height: 0;
11065
+ }
11066
+
11067
+ .ChatFocus > .Sash.SashVertical{
11068
+ position: relative;
11069
+ flex: none;
11070
+ inline-size: 4px;
11071
+ cursor: col-resize;
11072
+ touch-action: none;
11073
+ }
11074
+
11075
+ .ChatFocus > .Sash.SashVertical::before{
11076
+ content: '';
11077
+ position: absolute;
11078
+ inset: 0;
11079
+ background: color-mix(in srgb, var(--vscode-sideBar-border, var(--WidgetBorder, white)) 75%, transparent);
10682
11080
  }
11081
+
11082
+ .ChatFocus > .Sash.SashVertical:hover::before{
11083
+ background: var(--vscode-focusBorder, var(--vscode-button-background));
11084
+ }
11085
+
10683
11086
  .ChatFocusMainArea{
10684
11087
  display: flex;
10685
11088
  flex: 1;
@@ -10794,6 +11197,7 @@ const renderCss = (oldState, newState) => {
10794
11197
  composerHeight,
10795
11198
  listItemHeight,
10796
11199
  modelPickerHeight,
11200
+ projectSidebarWidth,
10797
11201
  selectedSessionId,
10798
11202
  sessions,
10799
11203
  textAreaPaddingBottom,
@@ -10803,7 +11207,7 @@ const renderCss = (oldState, newState) => {
10803
11207
  uid
10804
11208
  } = newState;
10805
11209
  const renderHtmlCss = getRenderHtmlCss(sessions, selectedSessionId);
10806
- const css = getCss(composerHeight, composerAttachmentsHeight, modelPickerHeight, listItemHeight, chatMessageFontSize, chatMessageLineHeight, chatMessageFontFamily, chatFocusContentMaxWidth, textAreaPaddingTop, textAreaPaddingLeft, textAreaPaddingRight, textAreaPaddingBottom, chatSendAreaPaddingTop, chatSendAreaPaddingLeft, chatSendAreaPaddingRight, chatSendAreaPaddingBottom, renderHtmlCss);
11210
+ const css = getCss(composerHeight, composerAttachmentsHeight, modelPickerHeight, listItemHeight, chatMessageFontSize, chatMessageLineHeight, chatMessageFontFamily, chatFocusContentMaxWidth, projectSidebarWidth, textAreaPaddingTop, textAreaPaddingLeft, textAreaPaddingRight, textAreaPaddingBottom, chatSendAreaPaddingTop, chatSendAreaPaddingLeft, chatSendAreaPaddingRight, chatSendAreaPaddingBottom, renderHtmlCss);
10807
11211
  return [SetCss, uid, css];
10808
11212
  };
10809
11213
 
@@ -10982,9 +11386,13 @@ const MaskIconChevronRight = 'MaskIconChevronRight';
10982
11386
  const MaskIconChevronUp = 'MaskIconChevronUp';
10983
11387
  const MaskIconClose = 'MaskIconClose';
10984
11388
  const MaskIconDebugPause = 'MaskIconDebugPause';
11389
+ const MaskIconDiff = 'MaskIconDiff';
11390
+ const MaskIconFolder = 'MaskIconFolder';
11391
+ const MaskIconGitCommit = 'MaskIconGitCommit';
10985
11392
  const MaskIconLayoutPanelLeft = 'MaskIconLayoutPanelLeft';
10986
11393
  const MaskIconSearch = 'MaskIconSearch';
10987
11394
  const MaskIconSettingsGear = 'MaskIconSettingsGear';
11395
+ const MaskIconTerminal = 'MaskIconTerminal';
10988
11396
  const Message = 'Message';
10989
11397
  const MessageAssistant = 'MessageAssistant';
10990
11398
  const MessageUser = 'MessageUser';
@@ -11003,6 +11411,8 @@ const ProjectSessionItem = 'ProjectSessionItem';
11003
11411
  const ProjectSessionItemLabel = 'ProjectSessionItemLabel';
11004
11412
  const ProjectSessionItemSelected = 'ProjectSessionItemSelected';
11005
11413
  const ProjectSidebar = 'ProjectSidebar';
11414
+ const Sash = 'Sash';
11415
+ const SashVertical = 'SashVertical';
11006
11416
  const SearchFieldContainer = 'SearchFieldContainer';
11007
11417
  const RunModePickerContainer = 'RunModePickerContainer';
11008
11418
  const RunModePickerPopOver = 'RunModePickerPopOver';
@@ -11071,6 +11481,9 @@ const HandleContextMenuChatImageAttachment = 58;
11071
11481
  const HandleClickGitBranchPickerToggle = 59;
11072
11482
  const HandleErrorComposerAttachmentPreviewOverlay = 60;
11073
11483
  const HandleClickCustomSelectOverlay = 61;
11484
+ const HandlePointerDownProjectSidebarSash = 62;
11485
+ const HandlePointerMoveProjectSidebarSash = 63;
11486
+ const HandlePointerUpProjectSidebarSash = 64;
11074
11487
 
11075
11488
  const getAddContextButtonDom = () => {
11076
11489
  return [{
@@ -11280,7 +11693,7 @@ const getSendButtonClassName = isSendDisabled => {
11280
11693
  return mergeClassNames(IconButton, isSendDisabled ? SendButtonDisabled : '');
11281
11694
  };
11282
11695
 
11283
- const getSendButtonDom = (isSendDisabled, voiceDictationEnabled) => {
11696
+ const getSendButtonDom = (isSendDisabled, voiceDictationEnabled, isSessionInProgress) => {
11284
11697
  const sendButtonClassName = getSendButtonClassName(isSendDisabled);
11285
11698
  return [...(voiceDictationEnabled ? [{
11286
11699
  childCount: 1,
@@ -11293,7 +11706,15 @@ const getSendButtonDom = (isSendDisabled, voiceDictationEnabled) => {
11293
11706
  childCount: 0,
11294
11707
  className: 'MaskIcon MaskIconMic',
11295
11708
  type: Div
11296
- }] : []), {
11709
+ }] : []), ...(isSessionInProgress ? [{
11710
+ buttonType: 'button',
11711
+ childCount: 1,
11712
+ className: Button,
11713
+ name: Stop,
11714
+ onClick: HandleClick,
11715
+ title: stop(),
11716
+ type: Button$1
11717
+ }, text(stop())] : [{
11297
11718
  buttonType: 'submit',
11298
11719
  childCount: 1,
11299
11720
  className: sendButtonClassName,
@@ -11306,7 +11727,7 @@ const getSendButtonDom = (isSendDisabled, voiceDictationEnabled) => {
11306
11727
  className: 'MaskIcon MaskIconArrowUp',
11307
11728
  role: 'none',
11308
11729
  type: Div
11309
- }];
11730
+ }])];
11310
11731
  };
11311
11732
 
11312
11733
  const getTodoItemClassName = status => {
@@ -11380,34 +11801,51 @@ const getUsageOverviewDom = (tokensUsed, tokensMax) => {
11380
11801
  }, text(usageLabel)];
11381
11802
  };
11382
11803
 
11383
- const getComposerAttachmentLabel = displayType => {
11804
+ const getComposerAttachmentClassName = displayType => {
11384
11805
  switch (displayType) {
11385
11806
  case 'file':
11386
- return 'File';
11807
+ return ChatComposerAttachment;
11387
11808
  case 'image':
11388
- return 'Image';
11809
+ return ChatComposerAttachmentImage;
11389
11810
  case 'invalid-image':
11390
- return 'Invalid image';
11811
+ return ChatComposerAttachmentInvalidImage;
11391
11812
  case 'text-file':
11392
- return 'Text file';
11813
+ return ChatComposerAttachmentTextFile;
11393
11814
  default:
11394
- return displayType;
11815
+ return ChatComposerAttachment;
11395
11816
  }
11396
11817
  };
11397
- const getComposerAttachmentClassName = displayType => {
11818
+
11819
+ const getComposerAttachmentLabel = displayType => {
11398
11820
  switch (displayType) {
11399
11821
  case 'file':
11400
- return ChatComposerAttachment;
11822
+ return 'File';
11401
11823
  case 'image':
11402
- return ChatComposerAttachmentImage;
11824
+ return 'Image';
11403
11825
  case 'invalid-image':
11404
- return ChatComposerAttachmentInvalidImage;
11826
+ return 'Invalid image';
11405
11827
  case 'text-file':
11406
- return ChatComposerAttachmentTextFile;
11828
+ return 'Text file';
11407
11829
  default:
11408
- return ChatComposerAttachment;
11830
+ return displayType;
11831
+ }
11832
+ };
11833
+
11834
+ const getComposerAttachmentPreviewDom = attachment => {
11835
+ if (!attachment.previewSrc) {
11836
+ return [];
11409
11837
  }
11838
+ return [{
11839
+ alt: `Image preview for ${attachment.name}`,
11840
+ childCount: 0,
11841
+ className: ChatComposerAttachmentPreview,
11842
+ name: getComposerAttachmentInputName(attachment.attachmentId),
11843
+ onContextMenu: HandleContextMenuChatImageAttachment,
11844
+ src: attachment.previewSrc,
11845
+ type: Img
11846
+ }];
11410
11847
  };
11848
+
11411
11849
  const getComposerAttachmentRemoveButtonDom = attachment => {
11412
11850
  return [{
11413
11851
  'aria-label': removeAttachment(),
@@ -11423,20 +11861,7 @@ const getComposerAttachmentRemoveButtonDom = attachment => {
11423
11861
  type: Text
11424
11862
  }];
11425
11863
  };
11426
- const getComposerAttachmentPreviewDom = attachment => {
11427
- if (!attachment.previewSrc) {
11428
- return [];
11429
- }
11430
- return [{
11431
- alt: `Image preview for ${attachment.name}`,
11432
- childCount: 0,
11433
- className: ChatComposerAttachmentPreview,
11434
- name: getComposerAttachmentInputName(attachment.attachmentId),
11435
- onContextMenu: HandleContextMenuChatImageAttachment,
11436
- src: attachment.previewSrc,
11437
- type: Img
11438
- }];
11439
- };
11864
+
11440
11865
  const getComposerAttachmentsDom = composerAttachments => {
11441
11866
  if (composerAttachments.length === 0) {
11442
11867
  return [];
@@ -11468,6 +11893,7 @@ const getComposerAttachmentsDom = composerAttachments => {
11468
11893
  }];
11469
11894
  })];
11470
11895
  };
11896
+
11471
11897
  const getComposerTextAreaDom = () => {
11472
11898
  return {
11473
11899
  childCount: 0,
@@ -11482,7 +11908,8 @@ const getComposerTextAreaDom = () => {
11482
11908
  type: TextArea
11483
11909
  };
11484
11910
  };
11485
- const getChatSendAreaDom = (composerValue, composerAttachments, agentMode, agentModePickerOpen, gitBranchPickerVisible, gitBranchPickerOpen, gitBranchPickerErrorMessage, gitBranches, fallbackBranchName, hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton = false, voiceDictationEnabled = false, scrollDownButtonEnabled = false, messagesAutoScrollEnabled = true) => {
11911
+
11912
+ const getChatSendAreaDom = (composerValue, composerAttachments, agentMode, agentModePickerOpen, gitBranchPickerVisible, gitBranchPickerOpen, gitBranchPickerErrorMessage, gitBranches, fallbackBranchName, hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton = false, voiceDictationEnabled = false, isSessionInProgress = false, scrollDownButtonEnabled = false, messagesAutoScrollEnabled = true) => {
11486
11913
  const isSendDisabled = composerValue.trim() === '';
11487
11914
  const showAgentModePicker = hasSpaceForAgentModePicker;
11488
11915
  const showGitBranchPicker = gitBranchPickerVisible || Boolean(fallbackBranchName);
@@ -11512,7 +11939,7 @@ const getChatSendAreaDom = (composerValue, composerAttachments, agentMode, agent
11512
11939
  className: ChatSendAreaPrimaryControls,
11513
11940
  role: 'toolbar',
11514
11941
  type: Div
11515
- }, ...(showAgentModePicker ? getAgentModePickerVirtualDom(agentMode, agentModePickerOpen) : []), ...getChatModelPickerToggleVirtualDom(models, selectedModelId, modelPickerOpen), ...(reasoningPickerEnabled ? getReasoningEffortPickerVirtualDom(reasoningEffort, reasoningEffortPickerOpen) : []), ...(showResponsiveRunModePicker ? getRunModePickerVirtualDom(runMode, runModePickerOpen) : []), ...(usageOverviewEnabled ? getUsageOverviewDom(tokensUsed, tokensMax) : []), ...(addContextButtonEnabled ? getAddContextButtonDom() : []), ...(showCreatePullRequestButton ? getCreatePullRequestButtonDom() : []), ...(showGitBranchPicker ? getGitBranchPickerVirtualDom(gitBranches, gitBranchPickerOpen, gitBranchPickerErrorMessage, fallbackBranchName) : []), ...(showScrollDownButton ? getScrollDownButtonDom() : []), ...getSendButtonDom(isSendDisabled, voiceDictationEnabled)];
11942
+ }, ...(showAgentModePicker ? getAgentModePickerVirtualDom(agentMode, agentModePickerOpen) : []), ...getChatModelPickerToggleVirtualDom(models, selectedModelId, modelPickerOpen), ...(reasoningPickerEnabled ? getReasoningEffortPickerVirtualDom(reasoningEffort, reasoningEffortPickerOpen) : []), ...(showResponsiveRunModePicker ? getRunModePickerVirtualDom(runMode, runModePickerOpen) : []), ...(usageOverviewEnabled ? getUsageOverviewDom(tokensUsed, tokensMax) : []), ...(addContextButtonEnabled ? getAddContextButtonDom() : []), ...(showCreatePullRequestButton ? getCreatePullRequestButtonDom() : []), ...(showGitBranchPicker ? getGitBranchPickerVirtualDom(gitBranches, gitBranchPickerOpen, gitBranchPickerErrorMessage, fallbackBranchName) : []), ...(showScrollDownButton ? getScrollDownButtonDom() : []), ...getSendButtonDom(isSendDisabled, voiceDictationEnabled, isSessionInProgress)];
11516
11943
  };
11517
11944
 
11518
11945
  const authContainerStyle = 'align-items:center;display:flex;gap:8px;min-width:0;';
@@ -11570,26 +11997,57 @@ const getChatHeaderAuthDom = (authEnabled = false, userState = 'loggedOut', user
11570
11997
  }, text(buttonLabel)];
11571
11998
  };
11572
11999
 
12000
+ const getHeaderActionClassName = disabled => {
12001
+ return mergeClassNames(IconButton, disabled ? IconButtonDisabled : '');
12002
+ };
12003
+ const getHeaderActionVirtualDom = item => {
12004
+ return [{
12005
+ childCount: 1,
12006
+ className: getHeaderActionClassName(item.disabled),
12007
+ disabled: item.disabled,
12008
+ name: item.name,
12009
+ onClick: item.onClick,
12010
+ title: item.title,
12011
+ type: Button$1
12012
+ }, {
12013
+ childCount: 0,
12014
+ className: item.icon,
12015
+ type: Div
12016
+ }];
12017
+ };
12018
+
11573
12019
  const focusHeaderStyle = 'align-items:center;border-bottom:1px solid var(--vscode-panel-border, transparent);display:flex;gap:12px;justify-content:space-between;padding:8px 12px;';
11574
12020
  const focusHeaderMetaStyle = 'align-items:baseline;display:flex;gap:8px;min-width:0;overflow:hidden;';
11575
12021
  const focusHeaderTitleStyle = 'margin:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';
11576
12022
  const focusHeaderProjectStyle = 'overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';
11577
12023
  const focusHeaderActionsStyle = 'display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end;';
11578
- const focusHeaderButtonStyle = 'white-space:nowrap;';
11579
- const getFocusHeaderActionButtonDom = (label, name) => {
11580
- return [{
11581
- childCount: 1,
11582
- className: mergeClassNames(Button, ButtonSecondary),
11583
- inputType: 'button',
11584
- name,
11585
- onClick: HandleClick,
11586
- style: focusHeaderButtonStyle,
11587
- title: label,
11588
- type: Button$1
11589
- }, text(label)];
11590
- };
11591
12024
  const getChatHeaderDomFocusMode = (selectedSessionTitle, selectedProjectName, authEnabled = false, userState = 'loggedOut', userName = '') => {
11592
- const items = [[addAction(), FocusAddAction], [openInVsCode(), FocusOpenInVsCode], [commit(), FocusCommit], [openTerminal(), FocusOpenTerminal], [showDiff(), FocusShowDiff]];
12025
+ const items = [{
12026
+ icon: mergeClassNames(MaskIcon, MaskIconAdd),
12027
+ name: FocusAddAction,
12028
+ onClick: HandleClick,
12029
+ title: addAction()
12030
+ }, {
12031
+ icon: mergeClassNames(MaskIcon, MaskIconFolder),
12032
+ name: FocusOpenInVsCode,
12033
+ onClick: HandleClick,
12034
+ title: openInVsCode()
12035
+ }, {
12036
+ icon: mergeClassNames(MaskIcon, MaskIconGitCommit),
12037
+ name: FocusCommit,
12038
+ onClick: HandleClick,
12039
+ title: commit()
12040
+ }, {
12041
+ icon: mergeClassNames(MaskIcon, MaskIconTerminal),
12042
+ name: FocusOpenTerminal,
12043
+ onClick: HandleClick,
12044
+ title: openTerminal()
12045
+ }, {
12046
+ icon: mergeClassNames(MaskIcon, MaskIconDiff),
12047
+ name: FocusShowDiff,
12048
+ onClick: HandleClick,
12049
+ title: showDiff()
12050
+ }];
11593
12051
  const hasProjectName = !!selectedProjectName;
11594
12052
  return [{
11595
12053
  childCount: 2 + (authEnabled ? 1 : 0),
@@ -11618,7 +12076,7 @@ const getChatHeaderDomFocusMode = (selectedSessionTitle, selectedProjectName, au
11618
12076
  role: ToolBar,
11619
12077
  style: focusHeaderActionsStyle,
11620
12078
  type: Div
11621
- }, ...items.flatMap(([label, name]) => getFocusHeaderActionButtonDom(label, name))];
12079
+ }, ...items.flatMap(getHeaderActionVirtualDom)];
11622
12080
  };
11623
12081
 
11624
12082
  const getAgentModeOptionsVirtualDom = selectedAgentMode => {
@@ -11654,10 +12112,6 @@ const getAgentModePickerPopOverVirtualDom = selectedAgentMode => {
11654
12112
  return getCustomSelectPopOverVirtualDom(agentModes.length, agentModePickerHeight, getAgentModeOptionsVirtualDom(selectedAgentMode));
11655
12113
  };
11656
12114
 
11657
- const getUsageCostLabel = model => {
11658
- return `${model.usageCost ?? 1}x`;
11659
- };
11660
-
11661
12115
  const getUsageCostDom = detail => {
11662
12116
  if (detail === '') {
11663
12117
  return [];
@@ -11668,6 +12122,11 @@ const getUsageCostDom = detail => {
11668
12122
  type: Span
11669
12123
  }, text(detail)];
11670
12124
  };
12125
+
12126
+ const getUsageCostLabel = model => {
12127
+ return `${model.usageCost ?? 1}x`;
12128
+ };
12129
+
11671
12130
  const getChatModelListItemVirtualDom = (model, selectedModelId) => {
11672
12131
  const detail = getUsageCostLabel(model);
11673
12132
  const hasDetail = detail !== '';
@@ -11733,6 +12192,7 @@ const getModelPickerHeaderDom = modelPickerSearchValue => {
11733
12192
  value: modelPickerSearchValue
11734
12193
  }];
11735
12194
  };
12195
+
11736
12196
  const getChatModelPickerPopOverVirtualDom = (models, selectedModelId, modelPickerSearchValue) => {
11737
12197
  const visibleModels = getVisibleModels(models, modelPickerSearchValue);
11738
12198
  return [{
@@ -11787,15 +12247,6 @@ const getComposerAttachmentPreviewOverlayVirtualDom = (composerAttachments, atta
11787
12247
  }])];
11788
12248
  };
11789
12249
 
11790
- const runModes = ['local', 'background', 'cloud'];
11791
- const runModePickerHeight = runModes.length * 28;
11792
- const getRunModeOptionsVirtualDom = selectedRunMode => {
11793
- return runModes.flatMap(runMode => getCustomSelectOptionVirtualDom(getRunModePickerItemInputName(runMode), runMode, runMode === selectedRunMode));
11794
- };
11795
- const getRunModePickerPopOverVirtualDom = selectedRunMode => {
11796
- return getCustomSelectPopOverVirtualDom(runModes.length, runModePickerHeight, getRunModeOptionsVirtualDom(selectedRunMode), RunModePickerContainer, RunModePickerPopOver, false);
11797
- };
11798
-
11799
12250
  const getDropOverlayVirtualDom = () => {
11800
12251
  return [{
11801
12252
  childCount: 1,
@@ -11810,6 +12261,16 @@ const getDropOverlayVirtualDom = () => {
11810
12261
  type: Text
11811
12262
  }];
11812
12263
  };
12264
+
12265
+ const runModes = ['local', 'background', 'cloud'];
12266
+ const runModePickerHeight = runModes.length * 28;
12267
+ const getRunModeOptionsVirtualDom = selectedRunMode => {
12268
+ return runModes.flatMap(runMode => getCustomSelectOptionVirtualDom(getRunModePickerItemInputName(runMode), runMode, runMode === selectedRunMode));
12269
+ };
12270
+ const getRunModePickerPopOverVirtualDom = selectedRunMode => {
12271
+ return getCustomSelectPopOverVirtualDom(runModes.length, runModePickerHeight, getRunModeOptionsVirtualDom(selectedRunMode), RunModePickerContainer, RunModePickerPopOver, false);
12272
+ };
12273
+
11813
12274
  const getChatOverlaysVirtualDom = ({
11814
12275
  agentMode,
11815
12276
  agentModePickerVisible,
@@ -12448,14 +12909,16 @@ const getReadFileTarget = rawArguments => {
12448
12909
  }
12449
12910
  const uri = Reflect.get(parsed, 'uri');
12450
12911
  const path = Reflect.get(parsed, 'path');
12912
+ const baseUri = Reflect.get(parsed, 'baseUri');
12451
12913
  const uriValue = typeof uri === 'string' ? uri : '';
12452
12914
  const pathValue = typeof path === 'string' ? path : '';
12453
- const title = uriValue || pathValue;
12915
+ const baseUriValue = typeof baseUri === 'string' ? baseUri : '';
12916
+ const title = uriValue || pathValue || baseUriValue;
12454
12917
  if (!title) {
12455
12918
  return undefined;
12456
12919
  }
12457
- // `read_file` tool calls now use absolute `uri`; keep `path` as a legacy fallback for old transcripts.
12458
- const clickableUri = uriValue || pathValue;
12920
+ // File-like tool calls use absolute `uri` where available; keep `path` as a legacy fallback.
12921
+ const clickableUri = uriValue || pathValue || baseUriValue;
12459
12922
  return {
12460
12923
  clickableUri,
12461
12924
  title
@@ -12466,11 +12929,12 @@ const getToolCallFileNameDom = (fileName, {
12466
12929
  clickableProps = {},
12467
12930
  title
12468
12931
  } = {}) => {
12932
+ const resolvedTitle = title ?? clickableProps['data-uri'];
12469
12933
  return [{
12470
12934
  childCount: 1,
12471
12935
  className: ChatToolCallReadFileLink,
12472
- ...(title === undefined ? {} : {
12473
- title
12936
+ ...(resolvedTitle === undefined ? {} : {
12937
+ title: resolvedTitle
12474
12938
  }),
12475
12939
  ...clickableProps,
12476
12940
  type: Span
@@ -12496,7 +12960,6 @@ const getToolCallCreateDirectoryVirtualDom = toolCall => {
12496
12960
  return [{
12497
12961
  childCount: statusLabel ? 4 : 3,
12498
12962
  className: ChatOrderedListItem,
12499
- title: target.title,
12500
12963
  type: Li
12501
12964
  }, {
12502
12965
  childCount: 0,
@@ -12574,7 +13037,7 @@ const getToolCallLabel = toolCall => {
12574
13037
  if (toolCall.name === 'write_file' && !toolCall.status && hasIncompleteJsonArguments(toolCall.arguments)) {
12575
13038
  return `${displayName} (in progress)`;
12576
13039
  }
12577
- if ((toolCall.name === 'list_files' || toolCall.name === 'grep_search') && !toolCall.status && hasIncompleteJsonArguments(toolCall.arguments)) {
13040
+ if ((toolCall.name === 'list_files' || toolCall.name === 'grep_search' || toolCall.name === 'glob') && !toolCall.status && hasIncompleteJsonArguments(toolCall.arguments)) {
12578
13041
  return displayName;
12579
13042
  }
12580
13043
  const argumentPreview = getToolCallArgumentPreview(toolCall.arguments);
@@ -12620,7 +13083,6 @@ const getToolCallEditFileVirtualDom = toolCall => {
12620
13083
  return [{
12621
13084
  childCount: 3,
12622
13085
  className: ChatOrderedListItem,
12623
- title: target.title,
12624
13086
  type: Li
12625
13087
  }, {
12626
13088
  childCount: 0,
@@ -12649,7 +13111,6 @@ const getToolCallGetWorkspaceUriVirtualDom = toolCall => {
12649
13111
  return [{
12650
13112
  childCount: statusLabel ? 4 : 3,
12651
13113
  className: ChatOrderedListItem,
12652
- title: toolCall.result,
12653
13114
  type: Li
12654
13115
  }, {
12655
13116
  childCount: 0,
@@ -12664,6 +13125,21 @@ const getToolCallGetWorkspaceUriVirtualDom = toolCall => {
12664
13125
  }), ...(statusLabel ? [text(statusLabel)] : [])];
12665
13126
  };
12666
13127
 
13128
+ const getGlobPatternLabel = toolCall => {
13129
+ if (toolCall.name !== 'glob') {
13130
+ return '';
13131
+ }
13132
+ try {
13133
+ const parsed = JSON.parse(toolCall.arguments);
13134
+ if (!parsed || typeof parsed !== 'object') {
13135
+ return '';
13136
+ }
13137
+ const pattern = Reflect.get(parsed, 'pattern');
13138
+ return typeof pattern === 'string' && pattern ? ` "${pattern}"` : '';
13139
+ } catch {
13140
+ return '';
13141
+ }
13142
+ };
12667
13143
  const getToolCallReadFileVirtualDom = toolCall => {
12668
13144
  const target = getReadFileTarget(toolCall.arguments);
12669
13145
  if (!target) {
@@ -12672,14 +13148,16 @@ const getToolCallReadFileVirtualDom = toolCall => {
12672
13148
  const fileName = getFileNameFromUri(target.title);
12673
13149
  const toolNameLabel = `${toolCall.name} `;
12674
13150
  const statusLabel = getToolCallStatusLabel(toolCall);
13151
+ const globPatternLabel = getGlobPatternLabel(toolCall);
13152
+ const globMatchCount = toolCall.name === 'glob' && toolCall.status === 'success' ? getGlobMatchCount(toolCall.result) : undefined;
13153
+ const globMatchLabel = typeof globMatchCount === 'number' ? `, ${globMatchCount} ${globMatchCount === 1 ? 'match' : 'matches'}` : '';
12675
13154
  const fileNameClickableProps = target.clickableUri ? {
12676
13155
  'data-uri': target.clickableUri,
12677
13156
  onClick: HandleClickFileName
12678
13157
  } : {};
12679
13158
  return [{
12680
- childCount: statusLabel ? 4 : 3,
13159
+ childCount: 3 + (globPatternLabel ? 1 : 0) + (globMatchLabel ? 1 : 0) + (statusLabel ? 1 : 0),
12681
13160
  className: ChatOrderedListItem,
12682
- title: target.title,
12683
13161
  type: Li
12684
13162
  }, {
12685
13163
  childCount: 0,
@@ -12690,8 +13168,9 @@ const getToolCallReadFileVirtualDom = toolCall => {
12690
13168
  className: ToolCallName,
12691
13169
  type: Span
12692
13170
  }, text(toolNameLabel), ...getToolCallFileNameDom(fileName, {
12693
- clickableProps: fileNameClickableProps
12694
- }), ...(statusLabel ? [text(statusLabel)] : [])];
13171
+ clickableProps: fileNameClickableProps,
13172
+ title: target.title
13173
+ }), ...(globPatternLabel ? [text(globPatternLabel)] : []), ...(globMatchLabel ? [text(globMatchLabel)] : []), ...(statusLabel ? [text(statusLabel)] : [])];
12695
13174
  };
12696
13175
 
12697
13176
  const maxHtmlLength = 40_000;
@@ -13058,7 +13537,6 @@ const getToolCallWriteFileVirtualDom = toolCall => {
13058
13537
  return [{
13059
13538
  childCount: showDiffStats ? statusLabel ? 6 : 5 : statusLabel ? 4 : 3,
13060
13539
  className: ChatOrderedListItem,
13061
- title: target.title,
13062
13540
  type: Li
13063
13541
  }, {
13064
13542
  childCount: 0,
@@ -13088,7 +13566,7 @@ const getToolCallDom = toolCall => {
13088
13566
  return virtualDom;
13089
13567
  }
13090
13568
  }
13091
- if (toolCall.name === 'read_file' || toolCall.name === 'list_files' || toolCall.name === 'list_file') {
13569
+ if (toolCall.name === 'read_file' || toolCall.name === 'list_files' || toolCall.name === 'list_file' || toolCall.name === 'glob') {
13092
13570
  const virtualDom = getToolCallReadFileVirtualDom(toolCall);
13093
13571
  if (virtualDom.length > 0) {
13094
13572
  return virtualDom;
@@ -13577,17 +14055,27 @@ const getChatModeChatFocusVirtualDom = ({
13577
14055
  const isRunModePickerVisible = showRunMode && hasSpaceForRunModePicker && runModePickerOpen;
13578
14056
  const hasVisibleOverlays = isDropOverlayVisible || isComposerAttachmentPreviewOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
13579
14057
  const chatRootChildCount = 2 + (hasVisibleOverlays ? 1 : 0);
14058
+ const isSelectedSessionInProgress = selectedSession ? getChatSessionStatus(selectedSession) === 'in-progress' : false;
13580
14059
  return [{
13581
- childCount: chatRootChildCount,
14060
+ childCount: chatRootChildCount + 1,
13582
14061
  className: mergeClassNames(Viewlet, Chat, ChatFocus),
13583
14062
  onDragEnter: HandleDragEnterChatView,
13584
14063
  onDragOver: HandleDragOverChatView,
14064
+ onPointerMove: HandlePointerMoveProjectSidebarSash,
14065
+ onPointerUp: HandlePointerUpProjectSidebarSash,
13585
14066
  type: Div
13586
14067
  }, ...getProjectListDom(projects, sessions, projectExpandedIds, selectedProjectId, selectedSessionId, projectListScrollTop, true), {
14068
+ 'aria-orientation': 'vertical',
14069
+ childCount: 0,
14070
+ className: mergeClassNames(Sash, SashVertical),
14071
+ onPointerDown: HandlePointerDownProjectSidebarSash,
14072
+ role: 'separator',
14073
+ type: Div
14074
+ }, {
13587
14075
  childCount: 3,
13588
14076
  className: ChatFocusMainArea,
13589
14077
  type: Div
13590
- }, ...getChatHeaderDomFocusMode(selectedSessionTitle, selectedProjectName, authEnabled, userState, userName), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, messagesScrollTop, useChatMathWorker, true), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, gitBranchPickerVisible, gitBranchPickerOpen, gitBranchPickerErrorMessage, gitBranches, selectedSession?.branchName || '', hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton, voiceDictationEnabled, scrollDownButtonEnabled, messagesAutoScrollEnabled), ...getChatOverlaysVirtualDom({
14078
+ }, ...getChatHeaderDomFocusMode(selectedSessionTitle, selectedProjectName, authEnabled, userState, userName), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, messagesScrollTop, useChatMathWorker, true), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, gitBranchPickerVisible, gitBranchPickerOpen, gitBranchPickerErrorMessage, gitBranches, selectedSession?.branchName || '', hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton, voiceDictationEnabled, isSelectedSessionInProgress, scrollDownButtonEnabled, messagesAutoScrollEnabled), ...getChatOverlaysVirtualDom({
13591
14079
  agentMode,
13592
14080
  agentModePickerVisible: isAgentModePickerVisible,
13593
14081
  composerAttachmentPreviewOverlayAttachmentId,
@@ -13621,25 +14109,6 @@ const getBackButtonVirtualDom = () => {
13621
14109
  }, arrowLeft];
13622
14110
  };
13623
14111
 
13624
- const getHeaderActionClassName = disabled => {
13625
- return mergeClassNames(IconButton, disabled ? IconButtonDisabled : '');
13626
- };
13627
- const getHeaderActionVirtualDom = item => {
13628
- return [{
13629
- childCount: 1,
13630
- className: getHeaderActionClassName(item.disabled),
13631
- disabled: item.disabled,
13632
- name: item.name,
13633
- onClick: item.onClick,
13634
- title: item.title,
13635
- type: Button$1
13636
- }, {
13637
- childCount: 0,
13638
- className: item.icon,
13639
- type: Div
13640
- }];
13641
- };
13642
-
13643
14112
  const getChatHeaderActionsDom = (viewMode, searchEnabled = false) => {
13644
14113
  const toggleTitle = viewMode === 'chat-focus' ? normalChatMode() : chatFocusMode();
13645
14114
  const items = [{
@@ -13762,6 +14231,7 @@ const getChatModeDetailVirtualDom = ({
13762
14231
  voiceDictationEnabled = false
13763
14232
  }) => {
13764
14233
  const selectedSession = sessions.find(session => session.id === selectedSessionId);
14234
+ const isSelectedSessionInProgress = selectedSession ? getChatSessionStatus(selectedSession) === 'in-progress' : false;
13765
14235
  const selectedSessionTitle = selectedSession?.title || chatTitle();
13766
14236
  const messages = selectedSession ? selectedSession.messages : [];
13767
14237
  const showCreatePullRequestButton = canCreatePullRequest(selectedSession);
@@ -13778,7 +14248,7 @@ const getChatModeDetailVirtualDom = ({
13778
14248
  onDragEnter: HandleDragEnterChatView,
13779
14249
  onDragOver: HandleDragOverChatView,
13780
14250
  type: Div
13781
- }, ...getChatHeaderDomDetailMode(selectedSessionTitle, authEnabled, userState, userName, authErrorMessage), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, messagesScrollTop, useChatMathWorker), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, false, false, '', [], '', hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton, voiceDictationEnabled, scrollDownButtonEnabled, messagesAutoScrollEnabled), ...getChatOverlaysVirtualDom({
14251
+ }, ...getChatHeaderDomDetailMode(selectedSessionTitle, authEnabled, userState, userName, authErrorMessage), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, messagesScrollTop, useChatMathWorker), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, false, false, '', [], '', hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton, voiceDictationEnabled, isSelectedSessionInProgress, scrollDownButtonEnabled, messagesAutoScrollEnabled), ...getChatOverlaysVirtualDom({
13782
14252
  agentMode,
13783
14253
  agentModePickerVisible: isAgentModePickerVisible,
13784
14254
  composerAttachmentPreviewOverlayAttachmentId,
@@ -13814,6 +14284,7 @@ const getChatSearchDom = (hasSearchField, searchValue) => {
13814
14284
  value: searchValue
13815
14285
  }];
13816
14286
  };
14287
+
13817
14288
  const getChatHeaderListModeDom = (authEnabled = false, userState = 'loggedOut', userName = '', authErrorMessage = '', searchEnabled = false, searchFieldVisible = false, searchValue = '') => {
13818
14289
  const hasAuthError = authEnabled && !!authErrorMessage;
13819
14290
  const hasSearchField = searchEnabled && searchFieldVisible;
@@ -13849,12 +14320,11 @@ const getEmptyChatSessionsDom = () => {
13849
14320
  };
13850
14321
 
13851
14322
  const getSessionStatusClassName = session => {
13852
- const hasInProgressAssistantMessage = session.messages.some(message => message.role === 'assistant' && message.inProgress);
13853
- if (hasInProgressAssistantMessage) {
14323
+ const status = getChatSessionStatus(session);
14324
+ if (status === 'in-progress') {
13854
14325
  return ChatListItemStatusInProgress;
13855
14326
  }
13856
- const hasAssistantMessage = session.messages.some(message => message.role === 'assistant');
13857
- if (hasAssistantMessage) {
14327
+ if (status === 'finished') {
13858
14328
  return ChatListItemStatusFinished;
13859
14329
  }
13860
14330
  return ChatListItemStatusStopped;
@@ -13981,7 +14451,7 @@ const getChatModeListVirtualDom = ({
13981
14451
  onDragEnter: HandleDragEnterChatView,
13982
14452
  onDragOver: HandleDragOverChatView,
13983
14453
  type: Div
13984
- }, ...getChatHeaderListModeDom(authEnabled, userState, userName, authErrorMessage, searchEnabled, searchFieldVisible, searchValue), ...getChatListDom(visibleSessions, selectedSessionId, listFocusedIndex, chatListScrollTop), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, false, false, '', [], '', hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, false, voiceDictationEnabled), ...getChatOverlaysVirtualDom({
14454
+ }, ...getChatHeaderListModeDom(authEnabled, userState, userName, authErrorMessage, searchEnabled, searchFieldVisible, searchValue), ...getChatListDom(visibleSessions, selectedSessionId, listFocusedIndex, chatListScrollTop), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, false, false, '', [], '', hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, false, voiceDictationEnabled, false), ...getChatOverlaysVirtualDom({
13985
14455
  agentMode,
13986
14456
  agentModePickerVisible: isAgentModePickerVisible,
13987
14457
  composerAttachmentPreviewOverlayAttachmentId,
@@ -14714,6 +15184,17 @@ const renderEventListeners = () => {
14714
15184
  name: HandlePointerDownModelPickerList,
14715
15185
  params: ['handlePointerDownModelPickerList'],
14716
15186
  preventDefault: true
15187
+ }, {
15188
+ name: HandlePointerDownProjectSidebarSash,
15189
+ params: ['handlePointerDownProjectSidebarSash'],
15190
+ preventDefault: true
15191
+ }, {
15192
+ name: HandlePointerMoveProjectSidebarSash,
15193
+ params: ['handlePointerMoveProjectSidebarSash', ClientX]
15194
+ }, {
15195
+ name: HandlePointerUpProjectSidebarSash,
15196
+ params: ['handlePointerUpProjectSidebarSash', ClientX],
15197
+ preventDefault: true
14717
15198
  }, {
14718
15199
  name: HandlePointerUpModelPickerList,
14719
15200
  params: ['handlePointerUpModelPickerList', ClientY],
@@ -14827,6 +15308,7 @@ const saveState = state => {
14827
15308
  projectExpandedIds,
14828
15309
  projectListScrollTop,
14829
15310
  projects,
15311
+ projectSidebarWidth,
14830
15312
  reasoningEffort,
14831
15313
  renamingSessionId,
14832
15314
  searchFieldVisible,
@@ -14849,6 +15331,7 @@ const saveState = state => {
14849
15331
  projectExpandedIds,
14850
15332
  projectListScrollTop,
14851
15333
  projects,
15334
+ projectSidebarWidth,
14852
15335
  reasoningEffort,
14853
15336
  renamingSessionId,
14854
15337
  searchFieldVisible,
@@ -15119,7 +15602,10 @@ const commandMap = {
15119
15602
  'Chat.handleMouseOut': wrapCommand(handleMouseOut),
15120
15603
  'Chat.handleMouseOver': wrapCommand(handleMouseOver),
15121
15604
  'Chat.handlePointerDownModelPickerList': wrapCommand(handlePointerDownModelPickerList),
15605
+ 'Chat.handlePointerDownProjectSidebarSash': wrapCommand(handlePointerDownProjectSidebarSash),
15606
+ 'Chat.handlePointerMoveProjectSidebarSash': wrapCommand(handlePointerMoveProjectSidebarSash),
15122
15607
  'Chat.handlePointerUpModelPickerList': wrapCommand(handlePointerUpModelPickerList),
15608
+ 'Chat.handlePointerUpProjectSidebarSash': wrapCommand(handlePointerUpProjectSidebarSash),
15123
15609
  'Chat.handleProjectAddButtonContextMenu': wrapCommand(handleProjectAddButtonContextMenu),
15124
15610
  'Chat.handleProjectListContextMenu': wrapCommand(handleProjectListContextMenu),
15125
15611
  'Chat.handleProjectListScroll': wrapCommand(handleProjectListScroll),