@jupyterlite/ai 0.8.1 → 0.9.0-a1

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.
Files changed (162) hide show
  1. package/lib/agent.d.ts +243 -0
  2. package/lib/agent.js +627 -0
  3. package/lib/chat-model.d.ts +195 -0
  4. package/lib/chat-model.js +591 -0
  5. package/lib/completion/completion-provider.d.ts +93 -0
  6. package/lib/completion/completion-provider.js +235 -0
  7. package/lib/completion/index.d.ts +1 -0
  8. package/lib/completion/index.js +1 -0
  9. package/lib/components/clear-button.d.ts +18 -0
  10. package/lib/components/clear-button.js +31 -0
  11. package/lib/components/index.d.ts +3 -0
  12. package/lib/components/index.js +3 -0
  13. package/lib/components/model-select.d.ts +19 -0
  14. package/lib/components/model-select.js +154 -0
  15. package/lib/components/stop-button.d.ts +3 -3
  16. package/lib/components/stop-button.js +8 -9
  17. package/lib/components/token-usage-display.d.ts +45 -0
  18. package/lib/components/token-usage-display.js +74 -0
  19. package/lib/components/tool-select.d.ts +27 -0
  20. package/lib/components/tool-select.js +130 -0
  21. package/lib/icons.d.ts +3 -1
  22. package/lib/icons.js +10 -13
  23. package/lib/index.d.ts +5 -5
  24. package/lib/index.js +341 -169
  25. package/lib/mcp/browser.d.ts +68 -0
  26. package/lib/mcp/browser.js +132 -0
  27. package/lib/models/settings-model.d.ts +70 -0
  28. package/lib/models/settings-model.js +296 -0
  29. package/lib/providers/built-in-providers.d.ts +9 -0
  30. package/lib/providers/built-in-providers.js +266 -0
  31. package/lib/providers/models.d.ts +37 -0
  32. package/lib/providers/models.js +28 -0
  33. package/lib/providers/provider-registry.d.ts +94 -0
  34. package/lib/providers/provider-registry.js +155 -0
  35. package/lib/tokens.d.ts +167 -86
  36. package/lib/tokens.js +25 -12
  37. package/lib/tools/commands.d.ts +11 -0
  38. package/lib/tools/commands.js +126 -0
  39. package/lib/tools/file.d.ts +27 -0
  40. package/lib/tools/file.js +262 -0
  41. package/lib/tools/notebook.d.ts +41 -0
  42. package/lib/tools/notebook.js +779 -0
  43. package/lib/tools/tool-registry.d.ts +35 -0
  44. package/lib/tools/tool-registry.js +55 -0
  45. package/lib/widgets/ai-settings.d.ts +49 -0
  46. package/lib/widgets/ai-settings.js +580 -0
  47. package/lib/widgets/chat-wrapper.d.ts +144 -0
  48. package/lib/widgets/chat-wrapper.js +390 -0
  49. package/lib/widgets/provider-config-dialog.d.ts +14 -0
  50. package/lib/widgets/provider-config-dialog.js +112 -0
  51. package/package.json +151 -40
  52. package/schema/settings-model.json +159 -0
  53. package/src/agent.ts +836 -0
  54. package/src/chat-model.ts +771 -0
  55. package/src/completion/completion-provider.ts +346 -0
  56. package/src/completion/index.ts +1 -0
  57. package/src/components/clear-button.tsx +56 -0
  58. package/src/components/index.ts +3 -0
  59. package/src/components/model-select.tsx +245 -0
  60. package/src/components/stop-button.tsx +11 -11
  61. package/src/components/token-usage-display.tsx +130 -0
  62. package/src/components/tool-select.tsx +218 -0
  63. package/src/icons.ts +12 -14
  64. package/src/index.ts +485 -232
  65. package/src/mcp/browser.ts +213 -0
  66. package/src/models/settings-model.ts +413 -0
  67. package/src/providers/built-in-providers.ts +294 -0
  68. package/src/providers/models.ts +79 -0
  69. package/src/providers/provider-registry.ts +189 -0
  70. package/src/tokens.ts +217 -90
  71. package/src/tools/commands.ts +151 -0
  72. package/src/tools/file.ts +307 -0
  73. package/src/tools/notebook.ts +987 -0
  74. package/src/tools/tool-registry.ts +63 -0
  75. package/src/types.d.ts +4 -0
  76. package/src/widgets/ai-settings.tsx +1233 -0
  77. package/src/widgets/chat-wrapper.tsx +543 -0
  78. package/src/widgets/provider-config-dialog.tsx +272 -0
  79. package/style/base.css +335 -14
  80. package/style/icons/jupyternaut-lite.svg +1 -1
  81. package/lib/base-completer.d.ts +0 -49
  82. package/lib/base-completer.js +0 -14
  83. package/lib/chat-handler.d.ts +0 -56
  84. package/lib/chat-handler.js +0 -201
  85. package/lib/completion-provider.d.ts +0 -34
  86. package/lib/completion-provider.js +0 -32
  87. package/lib/default-prompts.d.ts +0 -2
  88. package/lib/default-prompts.js +0 -31
  89. package/lib/default-providers/Anthropic/completer.d.ts +0 -12
  90. package/lib/default-providers/Anthropic/completer.js +0 -46
  91. package/lib/default-providers/Anthropic/settings-schema.json +0 -70
  92. package/lib/default-providers/ChromeAI/completer.d.ts +0 -12
  93. package/lib/default-providers/ChromeAI/completer.js +0 -56
  94. package/lib/default-providers/ChromeAI/instructions.d.ts +0 -6
  95. package/lib/default-providers/ChromeAI/instructions.js +0 -42
  96. package/lib/default-providers/ChromeAI/settings-schema.json +0 -18
  97. package/lib/default-providers/Gemini/completer.d.ts +0 -12
  98. package/lib/default-providers/Gemini/completer.js +0 -48
  99. package/lib/default-providers/Gemini/instructions.d.ts +0 -2
  100. package/lib/default-providers/Gemini/instructions.js +0 -9
  101. package/lib/default-providers/Gemini/settings-schema.json +0 -64
  102. package/lib/default-providers/MistralAI/completer.d.ts +0 -13
  103. package/lib/default-providers/MistralAI/completer.js +0 -52
  104. package/lib/default-providers/MistralAI/instructions.d.ts +0 -2
  105. package/lib/default-providers/MistralAI/instructions.js +0 -18
  106. package/lib/default-providers/MistralAI/settings-schema.json +0 -75
  107. package/lib/default-providers/Ollama/completer.d.ts +0 -12
  108. package/lib/default-providers/Ollama/completer.js +0 -43
  109. package/lib/default-providers/Ollama/instructions.d.ts +0 -2
  110. package/lib/default-providers/Ollama/instructions.js +0 -70
  111. package/lib/default-providers/Ollama/settings-schema.json +0 -143
  112. package/lib/default-providers/OpenAI/completer.d.ts +0 -12
  113. package/lib/default-providers/OpenAI/completer.js +0 -43
  114. package/lib/default-providers/OpenAI/settings-schema.json +0 -628
  115. package/lib/default-providers/WebLLM/completer.d.ts +0 -21
  116. package/lib/default-providers/WebLLM/completer.js +0 -127
  117. package/lib/default-providers/WebLLM/instructions.d.ts +0 -6
  118. package/lib/default-providers/WebLLM/instructions.js +0 -32
  119. package/lib/default-providers/WebLLM/settings-schema.json +0 -19
  120. package/lib/default-providers/index.d.ts +0 -2
  121. package/lib/default-providers/index.js +0 -179
  122. package/lib/provider.d.ts +0 -144
  123. package/lib/provider.js +0 -412
  124. package/lib/settings/base.json +0 -7
  125. package/lib/settings/index.d.ts +0 -3
  126. package/lib/settings/index.js +0 -3
  127. package/lib/settings/panel.d.ts +0 -226
  128. package/lib/settings/panel.js +0 -510
  129. package/lib/settings/textarea.d.ts +0 -2
  130. package/lib/settings/textarea.js +0 -18
  131. package/lib/settings/utils.d.ts +0 -2
  132. package/lib/settings/utils.js +0 -4
  133. package/lib/types/ai-model.d.ts +0 -24
  134. package/lib/types/ai-model.js +0 -5
  135. package/schema/chat.json +0 -28
  136. package/schema/provider-registry.json +0 -29
  137. package/schema/system-prompts.json +0 -22
  138. package/src/base-completer.ts +0 -75
  139. package/src/chat-handler.ts +0 -262
  140. package/src/completion-provider.ts +0 -64
  141. package/src/default-prompts.ts +0 -33
  142. package/src/default-providers/Anthropic/completer.ts +0 -59
  143. package/src/default-providers/ChromeAI/completer.ts +0 -73
  144. package/src/default-providers/ChromeAI/instructions.ts +0 -45
  145. package/src/default-providers/Gemini/completer.ts +0 -61
  146. package/src/default-providers/Gemini/instructions.ts +0 -9
  147. package/src/default-providers/MistralAI/completer.ts +0 -69
  148. package/src/default-providers/MistralAI/instructions.ts +0 -18
  149. package/src/default-providers/Ollama/completer.ts +0 -54
  150. package/src/default-providers/Ollama/instructions.ts +0 -70
  151. package/src/default-providers/OpenAI/completer.ts +0 -54
  152. package/src/default-providers/WebLLM/completer.ts +0 -151
  153. package/src/default-providers/WebLLM/instructions.ts +0 -33
  154. package/src/default-providers/index.ts +0 -211
  155. package/src/global.d.ts +0 -9
  156. package/src/provider.ts +0 -514
  157. package/src/settings/index.ts +0 -3
  158. package/src/settings/panel.tsx +0 -773
  159. package/src/settings/textarea.tsx +0 -33
  160. package/src/settings/utils.ts +0 -5
  161. package/src/types/ai-model.ts +0 -37
  162. package/src/types/service-worker.d.ts +0 -6
package/src/index.ts CHANGED
@@ -1,68 +1,194 @@
1
+ import {
2
+ ILabShell,
3
+ ILayoutRestorer,
4
+ JupyterFrontEnd,
5
+ JupyterFrontEndPlugin
6
+ } from '@jupyterlab/application';
7
+
1
8
  import {
2
9
  ActiveCellManager,
3
- buildChatSidebar,
4
- buildErrorWidget,
5
- ChatCommandRegistry,
6
- IActiveCellManager,
7
- IChatCommandRegistry,
10
+ AttachmentOpenerRegistry,
11
+ ChatWidget,
8
12
  InputToolbarRegistry
9
13
  } from '@jupyter/chat';
10
- import {
11
- JupyterFrontEnd,
12
- JupyterFrontEndPlugin,
13
- ILayoutRestorer
14
- } from '@jupyterlab/application';
15
- import { ReactWidget, IThemeManager } from '@jupyterlab/apputils';
14
+
15
+ import { ICommandPalette, IThemeManager } from '@jupyterlab/apputils';
16
+
16
17
  import { ICompletionProviderManager } from '@jupyterlab/completer';
18
+
19
+ import { IDocumentManager } from '@jupyterlab/docmanager';
20
+
17
21
  import { INotebookTracker } from '@jupyterlab/notebook';
22
+
18
23
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
24
+
25
+ import { IKernelSpecManager, KernelSpec } from '@jupyterlab/services';
26
+
19
27
  import { ISettingRegistry } from '@jupyterlab/settingregistry';
20
- import { IFormRendererRegistry } from '@jupyterlab/ui-components';
21
- import { ReadonlyPartialJSONObject } from '@lumino/coreutils';
28
+
29
+ import { settingsIcon } from '@jupyterlab/ui-components';
22
30
  import { ISecretsManager, SecretsManager } from 'jupyter-secrets-manager';
23
31
 
24
- import { ChatHandler, welcomeMessage } from './chat-handler';
25
- import { CompletionProvider } from './completion-provider';
26
- import { defaultProviderPlugins } from './default-providers';
27
- import { AIProviderRegistry } from './provider';
28
- import { aiSettingsRenderer, textArea } from './settings';
29
- import { IAIProviderRegistry, PLUGIN_IDS } from './tokens';
32
+ import { AgentManager } from './agent';
33
+ import { AIChatModel } from './chat-model';
34
+
35
+ import {
36
+ ChatProviderRegistry,
37
+ CompletionProviderRegistry
38
+ } from './providers/provider-registry';
39
+
40
+ import {
41
+ IAgentManager,
42
+ IChatProviderRegistry,
43
+ ICompletionProviderRegistry,
44
+ IToolRegistry,
45
+ SECRETS_NAMESPACE,
46
+ IAISettingsModel
47
+ } from './tokens';
48
+
49
+ import {
50
+ registerBuiltInChatProviders,
51
+ registerBuiltInCompletionProviders
52
+ } from './providers/built-in-providers';
53
+
54
+ import { AICompletionProvider } from './completion';
55
+
56
+ import { clearItem } from './components/clear-button';
57
+
58
+ import { createModelSelectItem } from './components/model-select';
59
+
30
60
  import { stopItem } from './components/stop-button';
31
61
 
32
- const chatCommandRegistryPlugin: JupyterFrontEndPlugin<IChatCommandRegistry> = {
33
- id: PLUGIN_IDS.chatCommandRegistry,
34
- description: 'Autocompletion registry',
62
+ import { createToolSelectItem } from './components/tool-select';
63
+
64
+ import { AISettingsModel } from './models/settings-model';
65
+
66
+ import { ToolRegistry } from './tools/tool-registry';
67
+
68
+ import {
69
+ createAddCellTool,
70
+ createDeleteCellTool,
71
+ createExecuteActiveCellTool,
72
+ createGetCellInfoTool,
73
+ createGetNotebookInfoTool,
74
+ createNotebookCreationTool,
75
+ createRunCellTool,
76
+ createSaveNotebookTool,
77
+ createSetCellContentTool
78
+ } from './tools/notebook';
79
+
80
+ import {
81
+ createCopyFileTool,
82
+ createDeleteFileTool,
83
+ createNavigateToDirectoryTool,
84
+ createNewFileTool,
85
+ createOpenFileTool,
86
+ createRenameFileTool
87
+ } from './tools/file';
88
+
89
+ import {
90
+ createDiscoverCommandsTool,
91
+ createExecuteCommandTool
92
+ } from './tools/commands';
93
+
94
+ import { AISettingsWidget } from './widgets/ai-settings';
95
+
96
+ import { ChatWrapperWidget } from './widgets/chat-wrapper';
97
+
98
+ /**
99
+ * Command IDs namespace
100
+ */
101
+ namespace CommandIds {
102
+ export const openSettings = '@jupyterlite/ai:open-settings';
103
+ export const reposition = '@jupyterlite/ai:reposition';
104
+ }
105
+
106
+ /**
107
+ * Chat provider registry plugin
108
+ */
109
+ const chatProviderRegistryPlugin: JupyterFrontEndPlugin<IChatProviderRegistry> =
110
+ {
111
+ id: '@jupyterlite/ai:chat-provider-registry',
112
+ description: 'Chat AI provider registry',
113
+ autoStart: true,
114
+ provides: IChatProviderRegistry,
115
+ activate: () => {
116
+ return new ChatProviderRegistry();
117
+ }
118
+ };
119
+
120
+ /**
121
+ * Completion provider registry plugin
122
+ */
123
+ const completionProviderRegistryPlugin: JupyterFrontEndPlugin<ICompletionProviderRegistry> =
124
+ {
125
+ id: '@jupyterlite/ai:completion-provider-registry',
126
+ description: 'Completion provider registry',
127
+ autoStart: true,
128
+ provides: ICompletionProviderRegistry,
129
+ activate: () => {
130
+ return new CompletionProviderRegistry();
131
+ }
132
+ };
133
+
134
+ /**
135
+ * Built-in chat providers plugin
136
+ */
137
+ const builtInChatProvidersPlugin: JupyterFrontEndPlugin<void> = {
138
+ id: '@jupyterlite/ai:built-in-chat-providers',
139
+ description: 'Register built-in chat AI providers',
35
140
  autoStart: true,
36
- provides: IChatCommandRegistry,
37
- activate: () => {
38
- const registry = new ChatCommandRegistry();
39
- registry.addProvider(new ChatHandler.ClearCommandProvider());
40
- return registry;
141
+ requires: [IChatProviderRegistry],
142
+ activate: (app: JupyterFrontEnd, chatRegistry: IChatProviderRegistry) => {
143
+ registerBuiltInChatProviders(chatRegistry);
41
144
  }
42
145
  };
43
146
 
44
- const chatPlugin: JupyterFrontEndPlugin<void> = {
45
- id: PLUGIN_IDS.chat,
46
- description: 'LLM chat extension',
147
+ /**
148
+ * Built-in completion providers plugin
149
+ */
150
+ const builtInCompletionProvidersPlugin: JupyterFrontEndPlugin<void> = {
151
+ id: '@jupyterlite/ai:built-in-completion-providers',
152
+ description: 'Register built-in completion providers',
47
153
  autoStart: true,
48
- requires: [IAIProviderRegistry, IRenderMimeRegistry, IChatCommandRegistry],
49
- optional: [
50
- INotebookTracker,
51
- ISettingRegistry,
52
- IThemeManager,
53
- ILayoutRestorer
154
+ requires: [ICompletionProviderRegistry],
155
+ activate: (
156
+ app: JupyterFrontEnd,
157
+ completionRegistry: ICompletionProviderRegistry
158
+ ) => {
159
+ registerBuiltInCompletionProviders(completionRegistry);
160
+ }
161
+ };
162
+
163
+ /**
164
+ * Initialization data for the extension.
165
+ */
166
+ const plugin: JupyterFrontEndPlugin<void> = {
167
+ id: '@jupyterlite/ai:plugin',
168
+ description: 'AI in JupyterLab',
169
+ autoStart: true,
170
+ requires: [
171
+ IAISettingsModel,
172
+ IAgentManager,
173
+ IToolRegistry,
174
+ IRenderMimeRegistry,
175
+ IDocumentManager
54
176
  ],
55
- activate: async (
177
+ optional: [IThemeManager, INotebookTracker, ILayoutRestorer, ILabShell],
178
+ activate: (
56
179
  app: JupyterFrontEnd,
57
- providerRegistry: IAIProviderRegistry,
180
+ settingsModel: AISettingsModel,
181
+ agentManager: AgentManager,
182
+ toolRegistry: IToolRegistry,
58
183
  rmRegistry: IRenderMimeRegistry,
59
- chatCommandRegistry: IChatCommandRegistry,
60
- notebookTracker: INotebookTracker | null,
61
- settingsRegistry: ISettingRegistry | null,
62
- themeManager: IThemeManager | null,
63
- restorer: ILayoutRestorer | null
64
- ) => {
65
- let activeCellManager: IActiveCellManager | null = null;
184
+ docManager: IDocumentManager,
185
+ themeManager?: IThemeManager,
186
+ notebookTracker?: INotebookTracker,
187
+ restorer?: ILayoutRestorer,
188
+ labShell?: ILabShell
189
+ ): void => {
190
+ // Create ActiveCellManager if notebook tracker is available
191
+ let activeCellManager: ActiveCellManager | undefined;
66
192
  if (notebookTracker) {
67
193
  activeCellManager = new ActiveCellManager({
68
194
  tracker: notebookTracker,
@@ -70,55 +196,38 @@ const chatPlugin: JupyterFrontEndPlugin<void> = {
70
196
  });
71
197
  }
72
198
 
73
- const chatHandler = new ChatHandler({
74
- providerRegistry,
75
- activeCellManager
199
+ // Create AI chat model
200
+ const chatModel = new AIChatModel({
201
+ user: { username: 'user', display_name: 'User' },
202
+ settingsModel,
203
+ agentManager,
204
+ activeCellManager,
205
+ documentManager: docManager
76
206
  });
77
207
 
78
- let sendWithShiftEnter = false;
79
- let enableCodeToolbar = true;
80
- let personaName = 'AI';
81
-
82
- function loadSetting(setting: ISettingRegistry.ISettings): void {
83
- sendWithShiftEnter = setting.get('sendWithShiftEnter')
84
- .composite as boolean;
85
- enableCodeToolbar = setting.get('enableCodeToolbar').composite as boolean;
86
- personaName = setting.get('personaName').composite as string;
87
-
88
- // set the properties
89
- chatHandler.config = { sendWithShiftEnter, enableCodeToolbar };
90
- chatHandler.personaName = personaName;
91
- }
92
-
93
- Promise.all([app.restored, settingsRegistry?.load(chatPlugin.id)])
94
- .then(([, settings]) => {
95
- if (!settings) {
96
- console.warn(
97
- 'The SettingsRegistry is not loaded for the chat extension'
98
- );
99
- return;
100
- }
101
- loadSetting(settings);
102
- settings.changed.connect(loadSetting);
103
- })
104
- .catch(reason => {
105
- console.error(
106
- `Something went wrong when reading the settings.\n${reason}`
107
- );
108
- });
109
-
110
- let chatWidget: ReactWidget | null = null;
111
-
208
+ // Create input toolbar registry with all buttons
112
209
  const inputToolbarRegistry = InputToolbarRegistry.defaultToolbarRegistry();
113
- const stopButton = stopItem(() => chatHandler.stopStreaming());
210
+ const stopButton = stopItem();
211
+ const clearButton = clearItem();
212
+ const toolSelectButton = createToolSelectItem(
213
+ toolRegistry,
214
+ settingsModel.config.toolsEnabled
215
+ );
216
+ const modelSelectButton = createModelSelectItem(settingsModel);
217
+
114
218
  inputToolbarRegistry.addItem('stop', stopButton);
219
+ inputToolbarRegistry.addItem('clear', clearButton);
220
+ inputToolbarRegistry.addItem('model', modelSelectButton);
221
+ inputToolbarRegistry.addItem('tools', toolSelectButton);
222
+
223
+ // Listen to writers changes to show/hide stop button
224
+ chatModel.writersChanged.connect((_, writers) => {
225
+ // Check if AI is currently writing (streaming)
226
+ const aiWriting = writers.some(
227
+ writer => writer.user.username === 'ai-assistant'
228
+ );
115
229
 
116
- chatHandler.writersChanged.connect((_, writers) => {
117
- if (
118
- writers.filter(
119
- writer => writer.user.username === chatHandler.personaName
120
- ).length
121
- ) {
230
+ if (aiWriting) {
122
231
  inputToolbarRegistry.hide('send');
123
232
  inputToolbarRegistry.show('stop');
124
233
  } else {
@@ -127,182 +236,326 @@ const chatPlugin: JupyterFrontEndPlugin<void> = {
127
236
  }
128
237
  });
129
238
 
130
- try {
131
- chatWidget = buildChatSidebar({
132
- model: chatHandler,
133
- themeManager,
134
- rmRegistry,
135
- chatCommandRegistry,
136
- inputToolbarRegistry,
137
- welcomeMessage: welcomeMessage(providerRegistry.providers)
138
- });
139
- } catch (e) {
140
- chatWidget = buildErrorWidget(themeManager);
141
- }
239
+ // Listen for settings changes to update tool availability
240
+ settingsModel.stateChanged.connect(() => {
241
+ const config = settingsModel.config;
242
+ if (!config.toolsEnabled) {
243
+ inputToolbarRegistry.hide('tools');
244
+ } else {
245
+ inputToolbarRegistry.show('tools');
246
+ }
247
+ });
142
248
 
143
- chatWidget.title.caption = 'Jupyterlite AI Chat';
144
- chatWidget.id = '@jupyterlite/ai:chat-widget';
249
+ // Create attachment opener registry to handle file attachments
250
+ const attachmentOpenerRegistry = new AttachmentOpenerRegistry();
251
+ attachmentOpenerRegistry.set('file', attachment => {
252
+ app.commands.execute('docmanager:open', { path: attachment.value });
253
+ });
254
+
255
+ attachmentOpenerRegistry.set('notebook', attachment => {
256
+ app.commands.execute('docmanager:open', { path: attachment.value });
257
+ });
145
258
 
146
- app.shell.add(chatWidget as ReactWidget, 'left', { rank: 2000 });
259
+ // Create chat panel with drag/drop functionality
260
+ const chatPanel = new ChatWidget({
261
+ model: chatModel,
262
+ rmRegistry,
263
+ themeManager,
264
+ inputToolbarRegistry,
265
+ attachmentOpenerRegistry
266
+ });
267
+
268
+ // Create wrapper widget with a toolbar
269
+ const chatWrapper = new ChatWrapperWidget({
270
+ chatPanel,
271
+ commands: app.commands,
272
+ chatModel,
273
+ settingsModel
274
+ });
275
+
276
+ app.shell.add(chatWrapper, 'left', { rank: 1000 });
147
277
 
148
278
  if (restorer) {
149
- restorer.add(chatWidget, chatWidget.id);
279
+ restorer.add(chatWrapper, chatWrapper.id);
150
280
  }
281
+
282
+ registerCommands(app, labShell);
151
283
  }
152
284
  };
153
285
 
154
- const completerPlugin: JupyterFrontEndPlugin<void> = {
155
- id: PLUGIN_IDS.completer,
156
- autoStart: true,
157
- requires: [IAIProviderRegistry, ICompletionProviderManager],
158
- activate: (
159
- app: JupyterFrontEnd,
160
- providerRegistry: IAIProviderRegistry,
161
- manager: ICompletionProviderManager
162
- ): void => {
163
- const completer = new CompletionProvider({
164
- providerRegistry,
165
- requestCompletion: () => app.commands.execute('inline-completer:invoke')
286
+ function registerCommands(app: JupyterFrontEnd, labShell?: ILabShell) {
287
+ const { commands } = app;
288
+
289
+ if (labShell) {
290
+ commands.addCommand(CommandIds.reposition, {
291
+ label: 'Reposition Widget',
292
+ execute: (args: any) => {
293
+ const { widgetId, area, mode } = args;
294
+ const widget = widgetId
295
+ ? Array.from(labShell.widgets('main')).find(w => w.id === widgetId) ||
296
+ labShell.currentWidget
297
+ : labShell.currentWidget;
298
+
299
+ if (!widget) {
300
+ return;
301
+ }
302
+
303
+ if (area && area !== 'main') {
304
+ // Move to different area
305
+ labShell.move(widget, area);
306
+ labShell.activateById(widget.id);
307
+ } else if (mode) {
308
+ // Reposition within main area using split mode
309
+ labShell.add(widget, 'main', { mode, activate: true });
310
+ }
311
+ },
312
+ describedBy: {
313
+ args: {
314
+ type: 'object',
315
+ properties: {
316
+ widgetId: {
317
+ type: 'string',
318
+ description:
319
+ 'The widget ID to reposition in the application shell'
320
+ },
321
+ area: {
322
+ type: 'string',
323
+ description: 'The name of the area to reposition the widget to'
324
+ },
325
+ mode: {
326
+ type: 'string',
327
+ enum: ['split-left', 'split-right', 'split-top', 'split-bottom'],
328
+ description: 'The mode to use when repositioning the widget'
329
+ }
330
+ }
331
+ }
332
+ }
166
333
  });
167
- manager.registerInlineProvider(completer);
168
334
  }
169
- };
335
+ }
170
336
 
171
- const providerRegistryPlugin: JupyterFrontEndPlugin<IAIProviderRegistry> =
172
- SecretsManager.sign(PLUGIN_IDS.providerRegistry, token => ({
173
- id: PLUGIN_IDS.providerRegistry,
337
+ /**
338
+ * A plugin to provide the settings model.
339
+ */
340
+ const agentManager: JupyterFrontEndPlugin<AgentManager> = SecretsManager.sign(
341
+ SECRETS_NAMESPACE,
342
+ token => ({
343
+ id: SECRETS_NAMESPACE,
344
+ description: 'Provide the AI agent manager',
174
345
  autoStart: true,
175
- requires: [IFormRendererRegistry, ISettingRegistry],
176
- optional: [IRenderMimeRegistry, ISecretsManager],
177
- provides: IAIProviderRegistry,
346
+ provides: IAgentManager,
347
+ requires: [IAISettingsModel, IChatProviderRegistry],
348
+ optional: [
349
+ ICommandPalette,
350
+ ICompletionProviderManager,
351
+ ICompletionProviderRegistry,
352
+ ILayoutRestorer,
353
+ ISecretsManager,
354
+ IThemeManager,
355
+ IToolRegistry
356
+ ],
178
357
  activate: (
179
358
  app: JupyterFrontEnd,
180
- editorRegistry: IFormRendererRegistry,
181
- settingRegistry: ISettingRegistry,
182
- rmRegistry?: IRenderMimeRegistry,
183
- secretsManager?: ISecretsManager
184
- ): IAIProviderRegistry => {
185
- const providerRegistry = new AIProviderRegistry({
186
- token,
187
- secretsManager
359
+ settingsModel: AISettingsModel,
360
+ chatProviderRegistry: IChatProviderRegistry,
361
+ palette: ICommandPalette,
362
+ completionManager?: ICompletionProviderManager,
363
+ completionProviderRegistry?: ICompletionProviderRegistry,
364
+ restorer?: ILayoutRestorer,
365
+ secretsManager?: ISecretsManager,
366
+ themeManager?: IThemeManager,
367
+ toolRegistry?: IToolRegistry
368
+ ): AgentManager => {
369
+ // Build the agent manager
370
+ const agentManager = new AgentManager({
371
+ settingsModel,
372
+ toolRegistry,
373
+ chatProviderRegistry,
374
+ secretsManager,
375
+ token
188
376
  });
189
377
 
190
- editorRegistry.addRenderer(
191
- `${PLUGIN_IDS.providerRegistry}.AIproviders`,
192
- aiSettingsRenderer({
193
- providerRegistry,
194
- secretsToken: token,
195
- rmRegistry,
196
- secretsManager
197
- })
198
- );
378
+ // Build the settings panel
379
+ const settingsWidget = new AISettingsWidget({
380
+ settingsModel,
381
+ agentManager,
382
+ themeManager,
383
+ chatProviderRegistry,
384
+ secretsManager,
385
+ token
386
+ });
387
+ settingsWidget.id = 'jupyterlite-ai-settings';
388
+ settingsWidget.title.icon = settingsIcon;
389
+
390
+ // Build the completion provider
391
+ if (completionManager && completionProviderRegistry) {
392
+ const completionProvider = new AICompletionProvider({
393
+ settingsModel,
394
+ completionProviderRegistry: completionProviderRegistry,
395
+ secretsManager,
396
+ token
397
+ });
398
+
399
+ completionManager.registerInlineProvider(completionProvider);
400
+ } else {
401
+ console.info(
402
+ 'Completion provider manager not available, skipping AI completion setup'
403
+ );
404
+ }
405
+
406
+ if (restorer) {
407
+ restorer.add(settingsWidget, settingsWidget.id);
408
+ }
199
409
 
200
- settingRegistry
201
- .load(providerRegistryPlugin.id)
202
- .then(settings => {
203
- if (!secretsManager) {
204
- delete settings.schema.properties?.['UseSecretsManager'];
410
+ app.commands.addCommand(CommandIds.openSettings, {
411
+ label: 'AI Settings',
412
+ caption: 'Configure AI providers and behavior',
413
+ icon: settingsIcon,
414
+ execute: () => {
415
+ // Check if the widget already exists in shell
416
+ let widget = Array.from(app.shell.widgets('main')).find(
417
+ w => w.id === 'jupyterlite-ai-settings'
418
+ ) as AISettingsWidget;
419
+
420
+ if (!widget && settingsWidget) {
421
+ // Use the pre-created widget
422
+ widget = settingsWidget;
423
+ app.shell.add(widget, 'main');
205
424
  }
206
- const updateProvider = () => {
207
- // Get the Ai provider settings.
208
- const providerSettings = settings.get('AIproviders')
209
- .composite as ReadonlyPartialJSONObject;
210
-
211
- // Update completer provider.
212
- if (Object.keys(providerSettings).includes('completer')) {
213
- providerRegistry.setCompleterProvider(
214
- providerSettings['completer'] as ReadonlyPartialJSONObject
215
- );
216
- } else {
217
- providerRegistry.setCompleterProvider({});
218
- }
219
425
 
220
- // Update chat provider.
221
- if (Object.keys(providerSettings).includes('chat')) {
222
- providerRegistry.setChatProvider(
223
- providerSettings['chat'] as ReadonlyPartialJSONObject
224
- );
225
- } else {
226
- providerRegistry.setChatProvider({});
227
- }
228
- };
229
-
230
- settings.changed.connect(() => updateProvider());
231
- updateProvider();
232
- })
233
- .catch(reason => {
234
- console.error(
235
- `Failed to load settings for ${providerRegistryPlugin.id}`,
236
- reason
237
- );
426
+ if (widget) {
427
+ app.shell.activateById(widget.id);
428
+ }
429
+ },
430
+ describedBy: {
431
+ args: {}
432
+ }
433
+ });
434
+
435
+ // Add to command palette if available
436
+ if (palette) {
437
+ palette.addItem({
438
+ command: CommandIds.openSettings,
439
+ category: 'AI Assistant'
238
440
  });
441
+ }
239
442
 
240
- return providerRegistry;
443
+ return agentManager;
241
444
  }
242
- }));
445
+ })
446
+ );
447
+
448
+ /**
449
+ * A plugin to provide the settings model.
450
+ */
451
+ const settingsModel: JupyterFrontEndPlugin<AISettingsModel> = {
452
+ id: '@jupyterlite/ai:settings-model',
453
+ description: 'Provide the AI settings model',
454
+ autoStart: true,
455
+ provides: IAISettingsModel,
456
+ requires: [ISettingRegistry],
457
+ activate: (app: JupyterFrontEnd, settingRegistry: ISettingRegistry) => {
458
+ return new AISettingsModel({ settingRegistry });
459
+ }
460
+ };
243
461
 
244
- const systemPromptsPlugin: JupyterFrontEndPlugin<void> = {
245
- id: PLUGIN_IDS.systemPrompts,
462
+ /**
463
+ * A plugin to provide the tool registry.
464
+ */
465
+ const toolRegistry: JupyterFrontEndPlugin<IToolRegistry> = {
466
+ id: '@jupyterlite/ai:tool-registry',
467
+ description: 'Provide the AI tool registry',
246
468
  autoStart: true,
247
- requires: [IAIProviderRegistry, ISettingRegistry],
248
- optional: [IFormRendererRegistry],
469
+ requires: [IAISettingsModel, IDocumentManager, IKernelSpecManager],
470
+ optional: [INotebookTracker],
471
+ provides: IToolRegistry,
249
472
  activate: (
250
473
  app: JupyterFrontEnd,
251
- providerRegistry: IAIProviderRegistry,
252
- settingsRegistry: ISettingRegistry,
253
- editorRegistry: IFormRendererRegistry | null
254
- ): void => {
255
- // Set textarea renderer for the prompt setting.
256
- editorRegistry?.addRenderer(
257
- `${PLUGIN_IDS.systemPrompts}.chatSystemPrompt`,
258
- textArea
474
+ settingsModel: AISettingsModel,
475
+ docManager: IDocumentManager,
476
+ kernelSpecManager: KernelSpec.IManager,
477
+ notebookTracker?: INotebookTracker
478
+ ) => {
479
+ const { commands } = app;
480
+ const toolRegistry = new ToolRegistry();
481
+
482
+ const notebookCreationTool = createNotebookCreationTool(
483
+ docManager,
484
+ kernelSpecManager
485
+ );
486
+ toolRegistry.add('create_notebook', notebookCreationTool);
487
+
488
+ // Add high-level notebook operation tools
489
+ const addCellTool = createAddCellTool(docManager, notebookTracker);
490
+ const getNotebookInfoTool = createGetNotebookInfoTool(
491
+ docManager,
492
+ notebookTracker
259
493
  );
260
- editorRegistry?.addRenderer(
261
- `${PLUGIN_IDS.systemPrompts}.completionSystemPrompt`,
262
- textArea
494
+ const getCellInfoTool = createGetCellInfoTool(docManager, notebookTracker);
495
+ const setCellContentTool = createSetCellContentTool(
496
+ docManager,
497
+ commands,
498
+ notebookTracker
499
+ );
500
+ const runCellTool = createRunCellTool(docManager, notebookTracker);
501
+ const deleteCellTool = createDeleteCellTool(docManager, notebookTracker);
502
+ const saveNotebookTool = createSaveNotebookTool(
503
+ docManager,
504
+ notebookTracker
505
+ );
506
+ const executeActiveCellTool = createExecuteActiveCellTool(
507
+ docManager,
508
+ notebookTracker
263
509
  );
264
510
 
265
- /**
266
- * Update the prompts in the provider registry.
267
- */
268
- function loadSetting(setting: ISettingRegistry.ISettings): void {
269
- providerRegistry.chatSystemPrompt = setting.get('chatSystemPrompt')
270
- .composite as string;
271
- providerRegistry.completerSystemPrompt = setting.get(
272
- 'completionSystemPrompt'
273
- ).composite as string;
274
- }
511
+ toolRegistry.add('add_cell', addCellTool);
512
+ toolRegistry.add('get_notebook_info', getNotebookInfoTool);
513
+ toolRegistry.add('get_cell_info', getCellInfoTool);
514
+ toolRegistry.add('set_cell_content', setCellContentTool);
515
+ toolRegistry.add('run_cell', runCellTool);
516
+ toolRegistry.add('delete_cell', deleteCellTool);
517
+ toolRegistry.add('save_notebook', saveNotebookTool);
518
+ toolRegistry.add('execute_active_cell', executeActiveCellTool);
275
519
 
276
- Promise.all([
277
- app.restored,
278
- settingsRegistry?.load(PLUGIN_IDS.systemPrompts)
279
- ])
280
- .then(([, settings]) => {
281
- if (!settings) {
282
- console.warn(
283
- 'The SettingsRegistry is not loaded for the chat extension'
284
- );
285
- return;
286
- }
287
- loadSetting(settings);
288
- settings.changed.connect(loadSetting);
289
- })
290
- .catch(reason => {
291
- console.error(
292
- `Something went wrong when reading the settings.\n${reason}`
293
- );
294
- });
520
+ // Add file operation tools
521
+ const newFileTool = createNewFileTool(docManager);
522
+ const openFileTool = createOpenFileTool(docManager);
523
+ const deleteFileTool = createDeleteFileTool(docManager);
524
+ const renameFileTool = createRenameFileTool(docManager);
525
+ const copyFileTool = createCopyFileTool(docManager);
526
+ const navigateToDirectoryTool = createNavigateToDirectoryTool(app.commands);
527
+
528
+ toolRegistry.add('create_file', newFileTool);
529
+ toolRegistry.add('open_file', openFileTool);
530
+ toolRegistry.add('delete_file', deleteFileTool);
531
+ toolRegistry.add('rename_file', renameFileTool);
532
+ toolRegistry.add('copy_file', copyFileTool);
533
+ toolRegistry.add('navigate_to_directory', navigateToDirectoryTool);
534
+
535
+ // Add command operation tools
536
+ const discoverCommandsTool = createDiscoverCommandsTool(app.commands);
537
+ const executeCommandTool = createExecuteCommandTool(
538
+ app.commands,
539
+ settingsModel
540
+ );
541
+
542
+ toolRegistry.add('discover_commands', discoverCommandsTool);
543
+ toolRegistry.add('execute_command', executeCommandTool);
544
+
545
+ return toolRegistry;
295
546
  }
296
547
  };
297
548
 
298
549
  export default [
299
- providerRegistryPlugin,
300
- chatCommandRegistryPlugin,
301
- chatPlugin,
302
- completerPlugin,
303
- systemPromptsPlugin,
304
- ...defaultProviderPlugins
550
+ chatProviderRegistryPlugin,
551
+ completionProviderRegistryPlugin,
552
+ builtInChatProvidersPlugin,
553
+ builtInCompletionProvidersPlugin,
554
+ plugin,
555
+ settingsModel,
556
+ agentManager,
557
+ toolRegistry
305
558
  ];
306
559
 
307
- export { IAIProviderRegistry } from './tokens';
308
- export * from './base-completer';
560
+ // Export extension points for other extensions to use
561
+ export * from './tokens';