@notebook-intelligence/notebook-intelligence 1.3.5 → 2.1.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.
package/README.md CHANGED
@@ -157,7 +157,7 @@ Below is an example of a server configuration with SSE transport. For SSE transp
157
157
  }
158
158
  ```
159
159
 
160
- If you have multiple servers configured but you would like to disable some for a while, you can do so by using the `disabled` key. `servername2` will be diabled and not available in `@mcp` chat participant.
160
+ If you have multiple servers configured but you would like to disable some for a while, you can do so by using the `disabled` key. `servername2` will be disabled and not available in `@mcp` chat participant.
161
161
 
162
162
  ```json
163
163
  "mcpServers": {
package/lib/api.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Signal } from '@lumino/signaling';
2
- import { IChatCompletionResponseEmitter, IChatParticipant, IContextItem, ITelemetryEvent, RequestDataType } from './tokens';
2
+ import { IChatCompletionResponseEmitter, IChatParticipant, IContextItem, ITelemetryEvent, IToolSelections, RequestDataType } from './tokens';
3
3
  export declare enum GitHubCopilotLoginStatus {
4
4
  NotLoggedIn = "NOT_LOGGED_IN",
5
5
  ActivatingDevice = "ACTIVATING_DEVICE",
@@ -18,6 +18,7 @@ export declare class NBIConfig {
18
18
  get inlineCompletionModel(): any;
19
19
  get usingGitHubCopilotModel(): boolean;
20
20
  get storeGitHubAccessToken(): boolean;
21
+ get toolConfig(): any;
21
22
  capabilities: any;
22
23
  chatParticipants: IChatParticipant[];
23
24
  changed: Signal<this, void>;
@@ -39,7 +40,7 @@ export declare class NBIAPI {
39
40
  static fetchCapabilities(): Promise<void>;
40
41
  static setConfig(config: any): Promise<void>;
41
42
  static updateOllamaModelList(): Promise<void>;
42
- static chatRequest(messageId: string, chatId: string, prompt: string, language: string, filename: string, additionalContext: IContextItem[], responseEmitter: IChatCompletionResponseEmitter): Promise<void>;
43
+ static chatRequest(messageId: string, chatId: string, prompt: string, language: string, filename: string, additionalContext: IContextItem[], chatMode: string, toolSelections: IToolSelections, responseEmitter: IChatCompletionResponseEmitter): Promise<void>;
43
44
  static generateCode(chatId: string, prompt: string, prefix: string, suffix: string, existingCode: string, language: string, filename: string, responseEmitter: IChatCompletionResponseEmitter): Promise<void>;
44
45
  static sendChatUserInput(messageId: string, data: any): Promise<void>;
45
46
  static sendWebSocketMessage(messageId: string, messageType: RequestDataType, data: any): Promise<void>;
package/lib/api.js CHANGED
@@ -42,6 +42,9 @@ export class NBIConfig {
42
42
  get storeGitHubAccessToken() {
43
43
  return this.capabilities.store_github_access_token === true;
44
44
  }
45
+ get toolConfig() {
46
+ return this.capabilities.tool_config;
47
+ }
45
48
  }
46
49
  class NBIAPI {
47
50
  static async initialize() {
@@ -167,7 +170,7 @@ class NBIAPI {
167
170
  });
168
171
  });
169
172
  }
170
- static async chatRequest(messageId, chatId, prompt, language, filename, additionalContext, responseEmitter) {
173
+ static async chatRequest(messageId, chatId, prompt, language, filename, additionalContext, chatMode, toolSelections, responseEmitter) {
171
174
  this._messageReceived.connect((_, msg) => {
172
175
  msg = JSON.parse(msg);
173
176
  if (msg.id === messageId) {
@@ -177,7 +180,15 @@ class NBIAPI {
177
180
  this._webSocket.send(JSON.stringify({
178
181
  id: messageId,
179
182
  type: RequestDataType.ChatRequest,
180
- data: { chatId, prompt, language, filename, additionalContext }
183
+ data: {
184
+ chatId,
185
+ prompt,
186
+ language,
187
+ filename,
188
+ additionalContext,
189
+ chatMode,
190
+ toolSelections
191
+ }
181
192
  }));
182
193
  }
183
194
  static async generateCode(chatId, prompt, prefix, suffix, existingCode, language, filename, responseEmitter) {
@@ -1,6 +1,6 @@
1
1
  /// <reference types="react" />
2
2
  import { ReactWidget } from '@jupyterlab/apputils';
3
- import { IActiveDocumentInfo, ICellContents, IContextItem, ITelemetryEmitter } from './tokens';
3
+ import { IActiveDocumentInfo, ICellContents, IContextItem, ITelemetryEmitter, IToolSelections } from './tokens';
4
4
  import { JupyterFrontEnd } from '@jupyterlab/application';
5
5
  export declare enum RunChatCompletionType {
6
6
  Chat = 0,
@@ -21,6 +21,8 @@ export interface IRunChatCompletionRequest {
21
21
  suffix?: string;
22
22
  existingCode?: string;
23
23
  additionalContext?: IContextItem[];
24
+ chatMode: string;
25
+ toolSelections?: IToolSelections;
24
26
  }
25
27
  export interface IChatSidebarOptions {
26
28
  getActiveDocumentInfo: () => IActiveDocumentInfo;
@@ -4,12 +4,13 @@ import { ReactWidget } from '@jupyterlab/apputils';
4
4
  import { UUID } from '@lumino/coreutils';
5
5
  import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';
6
6
  import { NBIAPI, GitHubCopilotLoginStatus } from './api';
7
- import { BackendMessageType, ContextType, GITHUB_COPILOT_PROVIDER_ID, RequestDataType, ResponseStreamDataType, TelemetryEventType } from './tokens';
7
+ import { BackendMessageType, BuiltinToolsetType, ContextType, GITHUB_COPILOT_PROVIDER_ID, RequestDataType, ResponseStreamDataType, TelemetryEventType } from './tokens';
8
8
  import { MarkdownRenderer } from './markdown-renderer';
9
9
  import copySvgstr from '../style/icons/copy.svg';
10
10
  import copilotSvgstr from '../style/icons/copilot.svg';
11
11
  import copilotWarningSvgstr from '../style/icons/copilot-warning.svg';
12
- import { VscSend, VscStopCircle, VscEye, VscEyeClosed, VscTriangleRight, VscTriangleDown, VscWarning } from 'react-icons/vsc';
12
+ import { VscSend, VscStopCircle, VscEye, VscEyeClosed, VscTriangleRight, VscTriangleDown, VscWarning, VscSettingsGear, VscPassFilled, VscTools, VscTrash } from 'react-icons/vsc';
13
+ import { MdOutlineCheckBoxOutlineBlank, MdCheckBox } from 'react-icons/md';
13
14
  import { extractLLMGeneratedCode, isDarkTheme } from './utils';
14
15
  const OPENAI_COMPATIBLE_CHAT_MODEL_ID = 'openai-compatible-chat-model';
15
16
  const LITELLM_COMPATIBLE_CHAT_MODEL_ID = 'litellm-compatible-chat-model';
@@ -239,7 +240,9 @@ function ChatResponse(props) {
239
240
  React.createElement("div", { className: "chat-message-from" },
240
241
  ((_a = msg.participant) === null || _a === void 0 ? void 0 : _a.iconPath) && (React.createElement("div", { className: `chat-message-from-icon ${((_b = msg.participant) === null || _b === void 0 ? void 0 : _b.id) === 'default' ? 'chat-message-from-icon-default' : ''} ${isDarkTheme() ? 'dark' : ''}` },
241
242
  React.createElement("img", { src: msg.participant.iconPath }))),
242
- React.createElement("div", { className: "chat-message-from-title" }, msg.from === 'user' ? 'User' : ((_c = msg.participant) === null || _c === void 0 ? void 0 : _c.name) || 'Copilot'),
243
+ React.createElement("div", { className: "chat-message-from-title" }, msg.from === 'user'
244
+ ? 'User'
245
+ : ((_c = msg.participant) === null || _c === void 0 ? void 0 : _c.name) || 'AI Assistant'),
243
246
  React.createElement("div", { className: "chat-message-from-progress", style: { display: `${props.showGenerating ? 'visible' : 'none'}` } },
244
247
  React.createElement("div", { className: "loading-ellipsis" }, "Generating"))),
245
248
  React.createElement("div", { className: "chat-message-timestamp" }, timestamp)),
@@ -302,17 +305,25 @@ function ChatResponse(props) {
302
305
  async function submitCompletionRequest(request, responseEmitter) {
303
306
  switch (request.type) {
304
307
  case RunChatCompletionType.Chat:
305
- return NBIAPI.chatRequest(request.messageId, request.chatId, request.content, request.language || 'python', request.filename || 'Untitled.ipynb', request.additionalContext || [], responseEmitter);
308
+ return NBIAPI.chatRequest(request.messageId, request.chatId, request.content, request.language || 'python', request.filename || 'Untitled.ipynb', request.additionalContext || [], request.chatMode, request.toolSelections || {}, responseEmitter);
306
309
  case RunChatCompletionType.ExplainThis:
307
310
  case RunChatCompletionType.FixThis:
308
311
  case RunChatCompletionType.ExplainThisOutput:
309
312
  case RunChatCompletionType.TroubleshootThisOutput: {
310
- return NBIAPI.chatRequest(request.messageId, request.chatId, request.content, request.language || 'python', request.filename || 'Untitled.ipynb', [], responseEmitter);
313
+ return NBIAPI.chatRequest(request.messageId, request.chatId, request.content, request.language || 'python', request.filename || 'Untitled.ipynb', [], 'ask', {}, responseEmitter);
311
314
  }
312
315
  case RunChatCompletionType.GenerateCode:
313
316
  return NBIAPI.generateCode(request.chatId, request.content, request.prefix || '', request.suffix || '', request.existingCode || '', request.language || 'python', request.filename || 'Untitled.ipynb', responseEmitter);
314
317
  }
315
318
  }
319
+ function CheckBoxItem(props) {
320
+ const indent = props.indent || 0;
321
+ return (React.createElement("div", { className: `checkbox-item checkbox-item-indent-${indent} ${props.header ? 'checkbox-item-header' : ''}`, title: props.title, onClick: event => props.onClick(event) },
322
+ React.createElement("div", { className: "checkbox-item-toggle" },
323
+ props.checked ? (React.createElement(MdCheckBox, { className: "checkbox-icon" })) : (React.createElement(MdOutlineCheckBoxOutlineBlank, { className: "checkbox-icon" })),
324
+ props.label),
325
+ props.title && (React.createElement("div", { className: "checkbox-item-description" }, props.title))));
326
+ }
316
327
  function SidebarComponent(props) {
317
328
  const [chatMessages, setChatMessages] = useState([]);
318
329
  const [prompt, setPrompt] = useState('');
@@ -336,8 +347,274 @@ function SidebarComponent(props) {
336
347
  const [activeDocumentInfo, setActiveDocumentInfo] = useState(null);
337
348
  const [currentFileContextTitle, setCurrentFileContextTitle] = useState('');
338
349
  const telemetryEmitter = props.getTelemetryEmitter();
350
+ const [chatMode, setChatMode] = useState('ask');
351
+ const [toolSelectionTitle, setToolSelectionTitle] = useState('Tool selection');
352
+ const [selectedToolCount, setSelectedToolCount] = useState(0);
353
+ const [notebookExecuteToolSelected, setNotebookExecuteToolSelected] = useState(false);
354
+ const [toolConfig, setToolConfig] = useState({
355
+ builtinToolsets: [
356
+ { id: BuiltinToolsetType.NotebookEdit, name: 'Notebook edit' },
357
+ { id: BuiltinToolsetType.NotebookExecute, name: 'Notebook execute' }
358
+ ],
359
+ mcpServers: [],
360
+ extensions: []
361
+ });
362
+ const [showModeTools, setShowModeTools] = useState(false);
363
+ const toolSelectionsInitial = {
364
+ builtinToolsets: [BuiltinToolsetType.NotebookEdit],
365
+ mcpServers: {},
366
+ extensions: {}
367
+ };
368
+ const toolSelectionsEmpty = {
369
+ builtinToolsets: [],
370
+ mcpServers: {},
371
+ extensions: {}
372
+ };
373
+ const [toolSelections, setToolSelections] = useState(toolSelectionsInitial);
374
+ const [hasExtensionTools, setHasExtensionTools] = useState(false);
375
+ NBIAPI.configChanged.connect(() => {
376
+ setToolConfig(NBIAPI.config.toolConfig);
377
+ });
378
+ useEffect(() => {
379
+ let hasTools = false;
380
+ for (const extension of toolConfig.extensions) {
381
+ if (extension.toolsets.length > 0) {
382
+ hasTools = true;
383
+ break;
384
+ }
385
+ }
386
+ setHasExtensionTools(hasTools);
387
+ }, [toolConfig]);
388
+ useEffect(() => {
389
+ const builtinToolSelCount = toolSelections.builtinToolsets.length;
390
+ let mcpServerToolSelCount = 0;
391
+ let extensionToolSelCount = 0;
392
+ for (const serverId in toolSelections.mcpServers) {
393
+ const mcpServerTools = toolSelections.mcpServers[serverId];
394
+ mcpServerToolSelCount += mcpServerTools.length;
395
+ }
396
+ for (const extensionId in toolSelections.extensions) {
397
+ const extensionToolsets = toolSelections.extensions[extensionId];
398
+ for (const toolsetId in extensionToolsets) {
399
+ const toolsetTools = extensionToolsets[toolsetId];
400
+ extensionToolSelCount += toolsetTools.length;
401
+ }
402
+ }
403
+ const typeCounts = [];
404
+ if (builtinToolSelCount > 0) {
405
+ typeCounts.push(`${builtinToolSelCount} built-in`);
406
+ }
407
+ if (mcpServerToolSelCount > 0) {
408
+ typeCounts.push(`${mcpServerToolSelCount} mcp`);
409
+ }
410
+ if (extensionToolSelCount > 0) {
411
+ typeCounts.push(`${extensionToolSelCount} ext`);
412
+ }
413
+ setSelectedToolCount(builtinToolSelCount + mcpServerToolSelCount + extensionToolSelCount);
414
+ setNotebookExecuteToolSelected(toolSelections.builtinToolsets.includes(BuiltinToolsetType.NotebookExecute));
415
+ setToolSelectionTitle(typeCounts.length === 0
416
+ ? 'Tool selection'
417
+ : `Tool selection (${typeCounts.join(', ')})`);
418
+ }, [toolSelections]);
419
+ const onClearToolsButtonClicked = () => {
420
+ setToolSelections(toolSelectionsEmpty);
421
+ };
422
+ const getBuiltinToolsetState = (toolsetName) => {
423
+ return toolSelections.builtinToolsets.includes(toolsetName);
424
+ };
425
+ const setBuiltinToolsetState = (toolsetName, enabled) => {
426
+ const newConfig = { ...toolSelections };
427
+ if (enabled) {
428
+ if (!toolSelections.builtinToolsets.includes(toolsetName)) {
429
+ newConfig.builtinToolsets.push(toolsetName);
430
+ }
431
+ }
432
+ else {
433
+ const index = newConfig.builtinToolsets.indexOf(toolsetName);
434
+ if (index !== -1) {
435
+ newConfig.builtinToolsets.splice(index, 1);
436
+ }
437
+ }
438
+ setToolSelections(newConfig);
439
+ };
440
+ const anyMCPServerToolSelected = (id) => {
441
+ if (!(id in toolSelections.mcpServers)) {
442
+ return false;
443
+ }
444
+ return toolSelections.mcpServers[id].length > 0;
445
+ };
446
+ const getMCPServerState = (id) => {
447
+ if (!(id in toolSelections.mcpServers)) {
448
+ return false;
449
+ }
450
+ const mcpServer = toolConfig.mcpServers.find(server => server.id === id);
451
+ const selectedServerTools = toolSelections.mcpServers[id];
452
+ for (const tool of mcpServer.tools) {
453
+ if (!selectedServerTools.includes(tool.name)) {
454
+ return false;
455
+ }
456
+ }
457
+ return true;
458
+ };
459
+ const onMCPServerClicked = (id) => {
460
+ if (anyMCPServerToolSelected(id)) {
461
+ const newConfig = { ...toolSelections };
462
+ delete newConfig.mcpServers[id];
463
+ setToolSelections(newConfig);
464
+ }
465
+ else {
466
+ const mcpServer = toolConfig.mcpServers.find(server => server.id === id);
467
+ const newConfig = { ...toolSelections };
468
+ newConfig.mcpServers[id] = structuredClone(mcpServer.tools.map((tool) => tool.name));
469
+ setToolSelections(newConfig);
470
+ }
471
+ };
472
+ const getMCPServerToolState = (serverId, toolId) => {
473
+ if (!(serverId in toolSelections.mcpServers)) {
474
+ return false;
475
+ }
476
+ const selectedServerTools = toolSelections.mcpServers[serverId];
477
+ return selectedServerTools.includes(toolId);
478
+ };
479
+ const setMCPServerToolState = (serverId, toolId, checked) => {
480
+ const newConfig = { ...toolSelections };
481
+ if (checked && !(serverId in newConfig.mcpServers)) {
482
+ newConfig.mcpServers[serverId] = [];
483
+ }
484
+ const selectedServerTools = newConfig.mcpServers[serverId];
485
+ if (checked) {
486
+ selectedServerTools.push(toolId);
487
+ }
488
+ else {
489
+ const index = selectedServerTools.indexOf(toolId);
490
+ if (index !== -1) {
491
+ selectedServerTools.splice(index, 1);
492
+ }
493
+ }
494
+ setToolSelections(newConfig);
495
+ };
496
+ // all toolsets and tools of the extension are selected
497
+ const getExtensionState = (extensionId) => {
498
+ if (!(extensionId in toolSelections.extensions)) {
499
+ return false;
500
+ }
501
+ const extension = toolConfig.extensions.find(extension => extension.id === extensionId);
502
+ for (const toolset of extension.toolsets) {
503
+ if (!getExtensionToolsetState(extensionId, toolset.id)) {
504
+ return false;
505
+ }
506
+ }
507
+ return true;
508
+ };
509
+ const getExtensionToolsetState = (extensionId, toolsetId) => {
510
+ if (!(extensionId in toolSelections.extensions)) {
511
+ return false;
512
+ }
513
+ if (!(toolsetId in toolSelections.extensions[extensionId])) {
514
+ return false;
515
+ }
516
+ const extension = toolConfig.extensions.find(ext => ext.id === extensionId);
517
+ const extensionToolset = extension.toolsets.find((toolset) => toolset.id === toolsetId);
518
+ const selectedToolsetTools = toolSelections.extensions[extensionId][toolsetId];
519
+ for (const tool of extensionToolset.tools) {
520
+ if (!selectedToolsetTools.includes(tool)) {
521
+ return false;
522
+ }
523
+ }
524
+ return true;
525
+ };
526
+ const anyExtensionToolsetSelected = (extensionId) => {
527
+ if (!(extensionId in toolSelections.extensions)) {
528
+ return false;
529
+ }
530
+ return Object.keys(toolSelections.extensions[extensionId]).length > 0;
531
+ };
532
+ const onExtensionClicked = (extensionId) => {
533
+ if (anyExtensionToolsetSelected(extensionId)) {
534
+ const newConfig = { ...toolSelections };
535
+ delete newConfig.extensions[extensionId];
536
+ setToolSelections(newConfig);
537
+ }
538
+ else {
539
+ const newConfig = { ...toolSelections };
540
+ const extension = toolConfig.extensions.find(ext => ext.id === extensionId);
541
+ if (extensionId in newConfig.extensions) {
542
+ delete newConfig.extensions[extensionId];
543
+ }
544
+ newConfig.extensions[extensionId] = {};
545
+ for (const toolset of extension.toolsets) {
546
+ newConfig.extensions[extensionId][toolset.id] = structuredClone(toolset.tools);
547
+ }
548
+ setToolSelections(newConfig);
549
+ }
550
+ };
551
+ const anyExtensionToolsetToolSelected = (extensionId, toolsetId) => {
552
+ if (!(extensionId in toolSelections.extensions)) {
553
+ return false;
554
+ }
555
+ if (!(toolsetId in toolSelections.extensions[extensionId])) {
556
+ return false;
557
+ }
558
+ return toolSelections.extensions[extensionId][toolsetId].length > 0;
559
+ };
560
+ const onExtensionToolsetClicked = (extensionId, toolsetId) => {
561
+ if (anyExtensionToolsetToolSelected(extensionId, toolsetId)) {
562
+ const newConfig = { ...toolSelections };
563
+ if (toolsetId in newConfig.extensions[extensionId]) {
564
+ delete newConfig.extensions[extensionId][toolsetId];
565
+ }
566
+ setToolSelections(newConfig);
567
+ }
568
+ else {
569
+ const extension = toolConfig.extensions.find(ext => ext.id === extensionId);
570
+ const extensionToolset = extension.toolsets.find((toolset) => toolset.id === toolsetId);
571
+ const newConfig = { ...toolSelections };
572
+ if (!(extensionId in newConfig.extensions)) {
573
+ newConfig.extensions[extensionId] = {};
574
+ }
575
+ newConfig.extensions[extensionId][toolsetId] = structuredClone(extensionToolset.tools);
576
+ setToolSelections(newConfig);
577
+ }
578
+ };
579
+ const getExtensionToolsetToolState = (extensionId, toolsetId, toolId) => {
580
+ if (!(extensionId in toolSelections.extensions)) {
581
+ return false;
582
+ }
583
+ const selectedExtensionToolsets = toolSelections.extensions[extensionId];
584
+ if (!(toolsetId in selectedExtensionToolsets)) {
585
+ return false;
586
+ }
587
+ const selectedServerTools = selectedExtensionToolsets[toolsetId];
588
+ return selectedServerTools.includes(toolId);
589
+ };
590
+ const setExtensionToolsetToolState = (extensionId, toolsetId, toolId, checked) => {
591
+ const newConfig = { ...toolSelections };
592
+ if (checked && !(extensionId in newConfig.extensions)) {
593
+ newConfig.extensions[extensionId] = {};
594
+ }
595
+ if (checked && !(toolsetId in newConfig.extensions[extensionId])) {
596
+ newConfig.extensions[extensionId][toolsetId] = [];
597
+ }
598
+ const selectedTools = newConfig.extensions[extensionId][toolsetId];
599
+ if (checked) {
600
+ selectedTools.push(toolId);
601
+ }
602
+ else {
603
+ const index = selectedTools.indexOf(toolId);
604
+ if (index !== -1) {
605
+ selectedTools.splice(index, 1);
606
+ }
607
+ }
608
+ setToolSelections(newConfig);
609
+ };
339
610
  useEffect(() => {
340
611
  const prefixes = [];
612
+ if (chatMode !== 'ask') {
613
+ prefixes.push('/clear');
614
+ setOriginalPrefixes(prefixes);
615
+ setPrefixSuggestions(prefixes);
616
+ return;
617
+ }
341
618
  const chatParticipants = NBIAPI.config.chatParticipants;
342
619
  for (const participant of chatParticipants) {
343
620
  const id = participant.id;
@@ -353,7 +630,7 @@ function SidebarComponent(props) {
353
630
  }
354
631
  setOriginalPrefixes(prefixes);
355
632
  setPrefixSuggestions(prefixes);
356
- }, []);
633
+ }, [chatMode]);
357
634
  useEffect(() => {
358
635
  const fetchData = () => {
359
636
  setGHLoginStatus(NBIAPI.getLoginStatus());
@@ -399,6 +676,7 @@ function SidebarComponent(props) {
399
676
  applyPrefixSuggestion(prefix);
400
677
  };
401
678
  const handleSubmitStopChatButtonClick = async () => {
679
+ setShowModeTools(false);
402
680
  if (!copilotRequestInProgress) {
403
681
  handleUserInputSubmit();
404
682
  }
@@ -406,6 +684,18 @@ function SidebarComponent(props) {
406
684
  handleUserInputCancel();
407
685
  }
408
686
  };
687
+ const handleSettingsButtonClick = async () => {
688
+ setShowModeTools(false);
689
+ props
690
+ .getApp()
691
+ .commands.execute('notebook-intelligence:open-configuration-dialog');
692
+ };
693
+ const handleChatToolsButtonClick = async () => {
694
+ if (!showModeTools) {
695
+ NBIAPI.fetchCapabilities();
696
+ }
697
+ setShowModeTools(!showModeTools);
698
+ };
409
699
  const handleUserInputSubmit = async () => {
410
700
  setPromptHistoryIndex(promptHistory.length + 1);
411
701
  setPromptHistory([...promptHistory, prompt]);
@@ -480,7 +770,9 @@ function SidebarComponent(props) {
480
770
  content: extractedPrompt,
481
771
  language: activeDocInfo.language,
482
772
  filename: activeDocInfo.filename,
483
- additionalContext
773
+ additionalContext,
774
+ chatMode,
775
+ toolSelections: toolSelections
484
776
  }, {
485
777
  emit: async (response) => {
486
778
  var _a, _b, _c, _d, _e;
@@ -617,6 +909,7 @@ function SidebarComponent(props) {
617
909
  event.stopPropagation();
618
910
  event.preventDefault();
619
911
  setShowPopover(false);
912
+ setShowModeTools(false);
620
913
  setSelectedPrefixSuggestionIndex(0);
621
914
  }
622
915
  else if (event.key === 'ArrowUp') {
@@ -831,7 +1124,9 @@ function SidebarComponent(props) {
831
1124
  }, [ghLoginStatus]);
832
1125
  return (React.createElement("div", { className: "sidebar" },
833
1126
  React.createElement("div", { className: "sidebar-header" },
834
- React.createElement("div", { className: "sidebar-title" }, "Notebook Intelligence")),
1127
+ React.createElement("div", { className: "sidebar-title" }, "Notebook Intelligence"),
1128
+ React.createElement("div", { className: "user-input-footer-button", onClick: () => handleSettingsButtonClick() },
1129
+ React.createElement(VscSettingsGear, null))),
835
1130
  !chatEnabled && !ghLoginRequired && (React.createElement("div", { className: "sidebar-login-info" },
836
1131
  "Chat is disabled as you don't have a model configured.",
837
1132
  React.createElement("button", { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", onClick: handleConfigurationClick },
@@ -851,7 +1146,7 @@ function SidebarComponent(props) {
851
1146
  copilotRequestInProgress }))),
852
1147
  React.createElement("div", { ref: messagesEndRef })))),
853
1148
  chatEnabled && (React.createElement("div", { className: `sidebar-user-input ${copilotRequestInProgress ? 'generating' : ''}` },
854
- React.createElement("textarea", { ref: promptInputRef, rows: 3, onChange: onPromptChange, onKeyDown: onPromptKeyDown, placeholder: "Ask Copilot...", spellCheck: false, value: prompt }),
1149
+ React.createElement("textarea", { ref: promptInputRef, rows: 3, onChange: onPromptChange, onKeyDown: onPromptKeyDown, placeholder: "Ask Notebook Intelligence...", spellCheck: false, value: prompt }),
855
1150
  (activeDocumentInfo === null || activeDocumentInfo === void 0 ? void 0 : activeDocumentInfo.filename) && (React.createElement("div", { className: "user-input-context-row" },
856
1151
  React.createElement("div", { className: `user-input-context user-input-context-active-file ${contextOn ? 'on' : 'off'}` },
857
1152
  React.createElement("div", null, currentFileContextTitle),
@@ -859,16 +1154,72 @@ function SidebarComponent(props) {
859
1154
  React.createElement(VscEye, { title: "Use as context" }))) : (React.createElement("div", { className: "user-input-context-toggle", onClick: () => setContextOn(!contextOn) },
860
1155
  React.createElement(VscEyeClosed, { title: "Don't use as context" })))))),
861
1156
  React.createElement("div", { className: "user-input-footer" },
862
- React.createElement("div", null,
1157
+ chatMode === 'ask' && (React.createElement("div", null,
863
1158
  React.createElement("a", { href: "javascript:void(0)", onClick: () => {
864
1159
  var _a;
865
1160
  setShowPopover(true);
866
1161
  (_a = promptInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
867
- }, title: "Select chat participant" }, "@")),
1162
+ }, title: "Select chat participant" }, "@"))),
868
1163
  React.createElement("div", { style: { flexGrow: 1 } }),
1164
+ React.createElement("div", { className: "chat-mode-widgets-container" },
1165
+ React.createElement("div", null,
1166
+ React.createElement("select", { className: "chat-mode-select", title: "Chat mode", value: chatMode, onChange: event => {
1167
+ if (event.target.value === 'ask') {
1168
+ setToolSelections(toolSelectionsEmpty);
1169
+ }
1170
+ else if (event.target.value === 'agent') {
1171
+ setToolSelections(toolSelectionsInitial);
1172
+ }
1173
+ setShowModeTools(false);
1174
+ setChatMode(event.target.value);
1175
+ } },
1176
+ React.createElement("option", { value: "ask" }, "Ask"),
1177
+ React.createElement("option", { value: "agent" }, "Agent"))),
1178
+ chatMode !== 'ask' && (React.createElement("div", { className: `user-input-footer-button tools-button ${notebookExecuteToolSelected ? 'tools-button-warning' : selectedToolCount > 0 ? 'tools-button-active' : ''}`, onClick: () => handleChatToolsButtonClick(), title: notebookExecuteToolSelected
1179
+ ? `Notebook execute tool selected!\n${toolSelectionTitle}`
1180
+ : toolSelectionTitle },
1181
+ React.createElement(VscTools, null),
1182
+ selectedToolCount > 0 && React.createElement(React.Fragment, null, selectedToolCount)))),
869
1183
  React.createElement("div", null,
870
1184
  React.createElement("button", { className: "jp-Dialog-button jp-mod-accept jp-mod-styled send-button", onClick: () => handleSubmitStopChatButtonClick(), disabled: prompt.length === 0 && !copilotRequestInProgress }, copilotRequestInProgress ? React.createElement(VscStopCircle, null) : React.createElement(VscSend, null)))),
871
- showPopover && prefixSuggestions.length > 0 && (React.createElement("div", { className: "user-input-autocomplete" }, prefixSuggestions.map((prefix, index) => (React.createElement("div", { key: `key-${index}`, className: `user-input-autocomplete-item ${index === selectedPrefixSuggestionIndex ? 'selected' : ''}`, "data-value": prefix, onClick: event => prefixSuggestionSelected(event) }, prefix)))))))));
1185
+ showPopover && prefixSuggestions.length > 0 && (React.createElement("div", { className: "user-input-autocomplete" }, prefixSuggestions.map((prefix, index) => (React.createElement("div", { key: `key-${index}`, className: `user-input-autocomplete-item ${index === selectedPrefixSuggestionIndex ? 'selected' : ''}`, "data-value": prefix, onClick: event => prefixSuggestionSelected(event) }, prefix))))),
1186
+ showModeTools && (React.createElement("div", { className: "mode-tools-popover", tabIndex: 1, autoFocus: true, onKeyDown: (event) => {
1187
+ if (event.key === 'Escape' || event.key === 'Enter') {
1188
+ event.stopPropagation();
1189
+ event.preventDefault();
1190
+ setShowModeTools(false);
1191
+ }
1192
+ } },
1193
+ React.createElement("div", { className: "mode-tools-popover-header" },
1194
+ React.createElement("div", { className: "mode-tools-popover-header-icon" },
1195
+ React.createElement(VscTools, null)),
1196
+ React.createElement("div", { className: "mode-tools-popover-title" }, toolSelectionTitle),
1197
+ React.createElement("div", { className: "mode-tools-popover-clear-tools-button", style: {
1198
+ visibility: selectedToolCount > 0 ? 'visible' : 'hidden'
1199
+ } },
1200
+ React.createElement("div", null,
1201
+ React.createElement(VscTrash, null)),
1202
+ React.createElement("div", null,
1203
+ React.createElement("a", { href: "javascript:void(0);", onClick: onClearToolsButtonClicked }, "clear"))),
1204
+ React.createElement("div", { className: "mode-tools-popover-close-button", onClick: () => setShowModeTools(false) },
1205
+ React.createElement("div", null,
1206
+ React.createElement(VscPassFilled, null)),
1207
+ React.createElement("div", null, "Done"))),
1208
+ React.createElement("div", { className: "mode-tools-popover-tool-list" },
1209
+ React.createElement("div", { className: "mode-tools-group-header" }, "Built-in"),
1210
+ React.createElement("div", { className: "mode-tools-group mode-tools-group-built-in" }, toolConfig.builtinToolsets.map((toolset) => (React.createElement(CheckBoxItem, { key: toolset.id, label: toolset.name, checked: getBuiltinToolsetState(toolset.id), header: true, onClick: () => {
1211
+ setBuiltinToolsetState(toolset.id, !getBuiltinToolsetState(toolset.id));
1212
+ } })))),
1213
+ toolConfig.mcpServers.length > 0 && (React.createElement("div", { className: "mode-tools-group-header" }, "MCP Servers")),
1214
+ toolConfig.mcpServers.map((mcpServer, index) => (React.createElement("div", { className: "mode-tools-group" },
1215
+ React.createElement(CheckBoxItem, { label: mcpServer.id, header: true, checked: getMCPServerState(mcpServer.id), onClick: () => onMCPServerClicked(mcpServer.id) }),
1216
+ mcpServer.tools.map((tool, index) => (React.createElement(CheckBoxItem, { label: tool.name, title: tool.description, indent: 1, checked: getMCPServerToolState(mcpServer.id, tool.name), onClick: () => setMCPServerToolState(mcpServer.id, tool.name, !getMCPServerToolState(mcpServer.id, tool.name)) })))))),
1217
+ hasExtensionTools && (React.createElement("div", { className: "mode-tools-group-header" }, "Extension tools")),
1218
+ toolConfig.extensions.map((extension, index) => (React.createElement("div", { className: "mode-tools-group" },
1219
+ React.createElement(CheckBoxItem, { label: `${extension.name} (${extension.id})`, header: true, checked: getExtensionState(extension.id), onClick: () => onExtensionClicked(extension.id) }),
1220
+ extension.toolsets.map((toolset, index) => (React.createElement(React.Fragment, null,
1221
+ React.createElement(CheckBoxItem, { label: `${toolset.name} (${toolset.id})`, indent: 1, checked: getExtensionToolsetState(extension.id, toolset.id), onClick: () => onExtensionToolsetClicked(extension.id, toolset.id) }),
1222
+ toolset.tools.map((tool, index) => (React.createElement(CheckBoxItem, { label: tool, indent: 2, checked: getExtensionToolsetToolState(extension.id, toolset.id, tool), onClick: () => setExtensionToolsetToolState(extension.id, toolset.id, tool, !getExtensionToolsetToolState(extension.id, toolset.id, tool)) }))))))))))))))));
872
1223
  }
873
1224
  function InlinePopoverComponent(props) {
874
1225
  const [modifiedCode, setModifiedCode] = useState('');
@@ -964,7 +1315,8 @@ function InlinePromptComponent(props) {
964
1315
  filename: undefined,
965
1316
  prefix: props.prefix,
966
1317
  suffix: props.suffix,
967
- existingCode: props.existingCode
1318
+ existingCode: props.existingCode,
1319
+ chatMode: 'ask'
968
1320
  }, {
969
1321
  emit: async (response) => {
970
1322
  props.onResponseEmit(response);
@@ -999,7 +1351,7 @@ function InlinePromptComponent(props) {
999
1351
  }
1000
1352
  }, []);
1001
1353
  return (React.createElement("div", { className: "inline-prompt-container", style: { height: props.limitHeight ? '40px' : '100%' } },
1002
- React.createElement("textarea", { ref: promptInputRef, rows: 3, onChange: onPromptChange, onKeyDown: onPromptKeyDown, placeholder: "Ask Copilot to generate Python code...", spellCheck: false, value: prompt })));
1354
+ React.createElement("textarea", { ref: promptInputRef, rows: 3, onChange: onPromptChange, onKeyDown: onPromptKeyDown, placeholder: "Ask Notebook Intelligence to generate Python code...", spellCheck: false, value: prompt })));
1003
1355
  }
1004
1356
  function GitHubCopilotStatusComponent(props) {
1005
1357
  const [ghLoginStatus, setGHLoginStatus] = useState(GitHubCopilotLoginStatus.NotLoggedIn);