@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
@@ -0,0 +1,580 @@
1
+ import { ReactWidget } from '@jupyterlab/ui-components';
2
+ import Add from '@mui/icons-material/Add';
3
+ import Cable from '@mui/icons-material/Cable';
4
+ import CheckCircle from '@mui/icons-material/CheckCircle';
5
+ import CheckCircleOutline from '@mui/icons-material/CheckCircleOutline';
6
+ import Delete from '@mui/icons-material/Delete';
7
+ import Edit from '@mui/icons-material/Edit';
8
+ import Error from '@mui/icons-material/Error';
9
+ import ErrorOutline from '@mui/icons-material/ErrorOutline';
10
+ import MoreVert from '@mui/icons-material/MoreVert';
11
+ import Psychology from '@mui/icons-material/Psychology';
12
+ import Settings from '@mui/icons-material/Settings';
13
+ import { Alert, Box, Button, Card, CardContent, Dialog, DialogActions, DialogContent, DialogTitle, Divider, FormControl, FormControlLabel, IconButton, InputLabel, List, ListItem, ListItemSecondaryAction, ListItemText, Menu, MenuItem, Select, Slider, Switch, Tab, Tabs, TextField, ThemeProvider, Typography, createTheme } from '@mui/material';
14
+ import React, { useEffect, useState } from 'react';
15
+ import { SECRETS_NAMESPACE, SECRETS_REPLACEMENT } from '../tokens';
16
+ import { ProviderConfigDialog } from './provider-config-dialog';
17
+ /**
18
+ * Create a theme that uses IThemeManager to detect theme
19
+ * @param themeManager - Optional theme manager to detect theme
20
+ * @returns A Material-UI theme configured for the current JupyterLab theme
21
+ */
22
+ const createJupyterLabTheme = (themeManager) => {
23
+ // Use IThemeManager if available, otherwise default to light theme
24
+ const isDark = themeManager?.theme
25
+ ? !themeManager.isLight(themeManager.theme)
26
+ : false;
27
+ return createTheme({
28
+ palette: {
29
+ mode: isDark ? 'dark' : 'light'
30
+ }
31
+ });
32
+ };
33
+ /**
34
+ * A JupyterLab widget for AI settings configuration
35
+ */
36
+ export class AISettingsWidget extends ReactWidget {
37
+ /**
38
+ * Construct a new AI settings widget
39
+ * @param options - The options for initializing the widget
40
+ */
41
+ constructor(options) {
42
+ super();
43
+ Private.setToken(options.token);
44
+ this._settingsModel = options.settingsModel;
45
+ this._agentManager = options.agentManager;
46
+ this._themeManager = options.themeManager;
47
+ this._chatProviderRegistry = options.chatProviderRegistry;
48
+ this._secretsManager = options.secretsManager;
49
+ this.id = 'jupyterlite-ai-settings';
50
+ this.title.label = 'AI Settings';
51
+ this.title.caption = 'Configure AI providers and behavior';
52
+ this.title.closable = true;
53
+ }
54
+ /**
55
+ * Render the AI settings component
56
+ * @returns A React element containing the AI settings interface
57
+ */
58
+ render() {
59
+ return (React.createElement(AISettingsComponent, { model: this._settingsModel, agentManager: this._agentManager, themeManager: this._themeManager, chatProviderRegistry: this._chatProviderRegistry, secretsManager: this._secretsManager }));
60
+ }
61
+ _settingsModel;
62
+ _agentManager;
63
+ _themeManager;
64
+ _chatProviderRegistry;
65
+ _secretsManager;
66
+ }
67
+ /**
68
+ * The main AI settings component that provides configuration UI
69
+ * @param props - Component props containing models and theme manager
70
+ * @returns A React component for AI settings configuration
71
+ */
72
+ const AISettingsComponent = ({ model, agentManager, themeManager, chatProviderRegistry, secretsManager }) => {
73
+ if (!model) {
74
+ return React.createElement("div", null, "Settings model not available");
75
+ }
76
+ const [config, setConfig] = useState(model.config || {});
77
+ const [theme, setTheme] = useState(() => createJupyterLabTheme(themeManager));
78
+ const [activeTab, setActiveTab] = useState(0);
79
+ const [dialogOpen, setDialogOpen] = useState(false);
80
+ const [editingProvider, setEditingProvider] = useState();
81
+ const [menuAnchor, setMenuAnchor] = useState(null);
82
+ const [menuProviderId, setMenuProviderId] = useState('');
83
+ const [mcpDialogOpen, setMcpDialogOpen] = useState(false);
84
+ const [editingMCPServer, setEditingMCPServer] = useState();
85
+ const [mcpMenuAnchor, setMcpMenuAnchor] = useState(null);
86
+ const [mcpMenuServerId, setMcpMenuServerId] = useState('');
87
+ /**
88
+ * Effect to listen for model state changes and update config
89
+ */
90
+ useEffect(() => {
91
+ if (!model || !model.stateChanged) {
92
+ return;
93
+ }
94
+ const onStateChanged = () => {
95
+ setConfig(model.config || {});
96
+ };
97
+ model.stateChanged.connect(onStateChanged);
98
+ return () => {
99
+ model.stateChanged.disconnect(onStateChanged);
100
+ };
101
+ }, [model]);
102
+ /**
103
+ * Effect to listen for theme changes and update the Material-UI theme
104
+ */
105
+ useEffect(() => {
106
+ if (!themeManager) {
107
+ return;
108
+ }
109
+ const updateTheme = () => {
110
+ setTheme(createJupyterLabTheme(themeManager));
111
+ };
112
+ themeManager.themeChanged.connect(updateTheme);
113
+ return () => {
114
+ themeManager.themeChanged.disconnect(updateTheme);
115
+ };
116
+ }, [themeManager]);
117
+ /**
118
+ * Effect to listen for MCP connection changes to re-render connection status
119
+ */
120
+ useEffect(() => {
121
+ if (!agentManager) {
122
+ return;
123
+ }
124
+ const onMCPConnectionChanged = () => {
125
+ // Force a re-render by updating the config state
126
+ setConfig(prevConfig => ({ ...prevConfig }));
127
+ };
128
+ agentManager.mcpConnectionChanged.connect(onMCPConnectionChanged);
129
+ return () => {
130
+ agentManager.mcpConnectionChanged.disconnect(onMCPConnectionChanged);
131
+ };
132
+ }, [agentManager]);
133
+ const getSecretFromManager = async (provider, fieldName) => {
134
+ const secret = await secretsManager?.get(Private.getToken(), SECRETS_NAMESPACE, `${provider}:${fieldName}`);
135
+ return secret?.value;
136
+ };
137
+ /**
138
+ * Attach a secrets field to the secrets manager.
139
+ * @param input - the DOm element to attach.
140
+ * @param provider - the name of the provider.
141
+ * @param fieldName - the name of the field.
142
+ */
143
+ const handleSecretField = async (input, provider, fieldName) => {
144
+ if (!(model.config.useSecretsManager && secretsManager)) {
145
+ return;
146
+ }
147
+ await secretsManager?.attach(Private.getToken(), SECRETS_NAMESPACE, `${provider}:${fieldName}`, input);
148
+ };
149
+ /**
150
+ * Handle adding a new AI provider
151
+ * @param providerConfig - The provider configuration to add
152
+ */
153
+ const handleAddProvider = async (providerConfig) => {
154
+ if (model.config.useSecretsManager &&
155
+ secretsManager &&
156
+ providerConfig.apiKey) {
157
+ providerConfig.apiKey = SECRETS_REPLACEMENT;
158
+ }
159
+ await model.addProvider(providerConfig);
160
+ };
161
+ /**
162
+ * Handle editing an existing AI provider
163
+ * @param providerConfig - The updated provider configuration
164
+ */
165
+ const handleEditProvider = async (providerConfig) => {
166
+ if (editingProvider) {
167
+ if (model.config.useSecretsManager &&
168
+ secretsManager &&
169
+ providerConfig.apiKey) {
170
+ providerConfig.apiKey = SECRETS_REPLACEMENT;
171
+ }
172
+ await model.updateProvider(editingProvider.id, providerConfig);
173
+ setEditingProvider(undefined);
174
+ }
175
+ };
176
+ /**
177
+ * Handle deleting an AI provider
178
+ * @param id - The ID of the provider to delete
179
+ */
180
+ const handleDeleteProvider = async (id) => {
181
+ await model.removeProvider(id);
182
+ setMenuAnchor(null);
183
+ };
184
+ /**
185
+ * Open the provider edit dialog
186
+ * @param provider - The provider to edit
187
+ */
188
+ const openEditDialog = async (provider) => {
189
+ // Retrieve the API key from the secrets manager if necessary.
190
+ if (model.config.useSecretsManager && secretsManager) {
191
+ provider.apiKey =
192
+ (await getSecretFromManager(provider.provider, 'apiKey')) ??
193
+ provider.apiKey ??
194
+ '';
195
+ }
196
+ setEditingProvider(provider);
197
+ setDialogOpen(true);
198
+ setMenuAnchor(null);
199
+ };
200
+ /**
201
+ * Open the provider add dialog
202
+ */
203
+ const openAddDialog = () => {
204
+ setEditingProvider(undefined);
205
+ setDialogOpen(true);
206
+ };
207
+ /**
208
+ * Handle provider menu click
209
+ * @param event - The click event
210
+ * @param providerId - The ID of the provider
211
+ */
212
+ const handleMenuClick = (event, providerId) => {
213
+ setMenuAnchor(event.currentTarget);
214
+ setMenuProviderId(providerId);
215
+ };
216
+ /**
217
+ * Handle provider menu close
218
+ */
219
+ const handleMenuClose = () => {
220
+ setMenuAnchor(null);
221
+ setMenuProviderId('');
222
+ };
223
+ /**
224
+ * Handle updating AI configuration
225
+ * @param updates - Partial configuration updates to apply
226
+ */
227
+ const handleConfigUpdate = async (updates) => {
228
+ if (updates.useSecretsManager !== undefined) {
229
+ if (updates.useSecretsManager) {
230
+ for (const provider of model.config.providers) {
231
+ provider.apiKey = SECRETS_REPLACEMENT;
232
+ await model.updateProvider(provider.id, provider);
233
+ }
234
+ }
235
+ else {
236
+ for (const provider of model.config.providers) {
237
+ const apiKey = await getSecretFromManager(provider.provider, 'apiKey');
238
+ if (apiKey) {
239
+ provider.apiKey = apiKey;
240
+ await model.updateProvider(provider.id, provider);
241
+ }
242
+ }
243
+ }
244
+ }
245
+ await model.updateConfig(updates);
246
+ };
247
+ /**
248
+ * Handle adding a new MCP server
249
+ * @param serverConfig - The MCP server configuration to add
250
+ */
251
+ const handleAddMCPServer = async (serverConfig) => {
252
+ await model.addMCPServer(serverConfig);
253
+ };
254
+ /**
255
+ * Handle editing an existing MCP server
256
+ * @param serverConfig - The updated MCP server configuration
257
+ */
258
+ const handleEditMCPServer = async (serverConfig) => {
259
+ if (editingMCPServer) {
260
+ await model.updateMCPServer(editingMCPServer.id, serverConfig);
261
+ setEditingMCPServer(undefined);
262
+ }
263
+ };
264
+ /**
265
+ * Handle deleting an MCP server
266
+ * @param id - The ID of the MCP server to delete
267
+ */
268
+ const handleDeleteMCPServer = async (id) => {
269
+ await model.removeMCPServer(id);
270
+ setMcpMenuAnchor(null);
271
+ };
272
+ /**
273
+ * Open the MCP server edit dialog
274
+ * @param server - The MCP server to edit
275
+ */
276
+ const openEditMCPDialog = (server) => {
277
+ setEditingMCPServer(server);
278
+ setMcpDialogOpen(true);
279
+ setMcpMenuAnchor(null);
280
+ };
281
+ /**
282
+ * Open the MCP server add dialog
283
+ */
284
+ const openAddMCPDialog = () => {
285
+ setEditingMCPServer(undefined);
286
+ setMcpDialogOpen(true);
287
+ };
288
+ /**
289
+ * Handle MCP server menu click
290
+ * @param event - The click event
291
+ * @param serverId - The ID of the MCP server
292
+ */
293
+ const handleMCPMenuClick = (event, serverId) => {
294
+ setMcpMenuAnchor(event.currentTarget);
295
+ setMcpMenuServerId(serverId);
296
+ };
297
+ /**
298
+ * Handle MCP server menu close
299
+ */
300
+ const handleMCPMenuClose = () => {
301
+ setMcpMenuAnchor(null);
302
+ setMcpMenuServerId('');
303
+ };
304
+ const isValidConfig = agentManager?.hasValidConfig() ?? false;
305
+ return (React.createElement(ThemeProvider, { theme: theme },
306
+ React.createElement(Box, { sx: {
307
+ height: '100%',
308
+ maxHeight: '100vh',
309
+ overflow: 'auto',
310
+ p: 2,
311
+ pb: 4,
312
+ fontSize: '0.9rem'
313
+ } },
314
+ React.createElement(Box, { sx: { mb: 2, display: 'flex', alignItems: 'center', gap: 2 } },
315
+ React.createElement(Settings, { color: "primary", sx: { fontSize: 24 } }),
316
+ React.createElement(Typography, { variant: "h5", component: "h1", sx: { fontWeight: 600 } }, "AI Settings")),
317
+ React.createElement(Alert, { severity: isValidConfig ? 'success' : 'warning', icon: isValidConfig ? React.createElement(CheckCircle, null) : React.createElement(Error, null), sx: { mb: 2 } }, isValidConfig
318
+ ? 'Configuration is valid and ready to use'
319
+ : 'Please add and configure a provider to get started'),
320
+ React.createElement(Box, { sx: { borderBottom: 1, borderColor: 'divider', mb: 2 } },
321
+ React.createElement(Tabs, { value: activeTab, onChange: (_, newValue) => setActiveTab(newValue) },
322
+ React.createElement(Tab, { label: "Providers" }),
323
+ React.createElement(Tab, { label: "Model Parameters" }),
324
+ React.createElement(Tab, { label: "Behavior" }),
325
+ React.createElement(Tab, { label: "MCP Servers" }))),
326
+ activeTab === 0 && (React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
327
+ secretsManager !== undefined && (React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.useSecretsManager, onChange: e => handleConfigUpdate({
328
+ useSecretsManager: e.target.checked
329
+ }), color: "primary", sx: { alignSelf: 'flex-start' } }), label: React.createElement("div", null,
330
+ React.createElement("span", null, "Use the secrets manager to manage API keys"),
331
+ !config.useSecretsManager && (React.createElement(Alert, { severity: "warning", icon: React.createElement(Error, null), sx: { mb: 2 } }, "The secrets will be stored in plain text in settings"))) })),
332
+ config.providers.length > 0 && (React.createElement(Card, { elevation: 2 },
333
+ React.createElement(CardContent, null,
334
+ React.createElement(Typography, { variant: "h6", component: "h2", gutterBottom: true }, "Active Providers"),
335
+ React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
336
+ React.createElement(FormControl, { fullWidth: true },
337
+ React.createElement(InputLabel, null, "Chat Provider"),
338
+ React.createElement(Select, { value: config.activeProvider, label: "Chat Provider", onChange: e => model.setActiveProvider(e.target.value) }, config.providers.map(provider => (React.createElement(MenuItem, { key: provider.id, value: provider.id }, provider.name))))),
339
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.useSameProviderForChatAndCompleter, onChange: e => handleConfigUpdate({
340
+ useSameProviderForChatAndCompleter: e.target.checked
341
+ }), color: "primary" }), label: "Use same provider for chat and completions" }),
342
+ !config.useSameProviderForChatAndCompleter && (React.createElement(FormControl, { fullWidth: true },
343
+ React.createElement(InputLabel, null, "Completion Provider"),
344
+ React.createElement(Select, { value: config.activeCompleterProvider || '', label: "Completion Provider", onChange: e => model.setActiveCompleterProvider(e.target.value || undefined) },
345
+ React.createElement(MenuItem, { value: "" },
346
+ React.createElement("em", null, "Use chat provider")),
347
+ config.providers.map(provider => (React.createElement(MenuItem, { key: provider.id, value: provider.id }, provider.name)))))))))),
348
+ React.createElement(Card, { elevation: 2 },
349
+ React.createElement(CardContent, null,
350
+ React.createElement(Box, { sx: {
351
+ display: 'flex',
352
+ alignItems: 'center',
353
+ justifyContent: 'space-between',
354
+ mb: 2
355
+ } },
356
+ React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
357
+ React.createElement(Typography, { variant: "h6", component: "h2" }, "Configured Providers")),
358
+ React.createElement(Button, { variant: "contained", startIcon: React.createElement(Add, null), onClick: openAddDialog, size: "small" }, "Add Provider")),
359
+ config.providers.length === 0 ? (React.createElement(Alert, { severity: "info" }, "No providers configured yet. Click \"Add Provider\" to get started.")) : (React.createElement(List, null, config.providers.map(provider => (React.createElement(ListItem, { key: provider.id, divider: true },
360
+ React.createElement(ListItemText, { primary: provider.name, secondary: React.createElement(Typography, { variant: "body2", color: "text.secondary" },
361
+ provider.provider,
362
+ " \u2022 ",
363
+ provider.model) }),
364
+ React.createElement(ListItemSecondaryAction, null,
365
+ React.createElement(IconButton, { onClick: e => handleMenuClick(e, provider.id), size: "small" },
366
+ React.createElement(MoreVert, null)))))))))))),
367
+ activeTab === 1 && (React.createElement(Card, { elevation: 2 },
368
+ React.createElement(CardContent, null,
369
+ React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', mb: 1.5, gap: 1 } },
370
+ React.createElement(Psychology, { color: "primary" }),
371
+ React.createElement(Typography, { variant: "h6", component: "h2" }, "Model Parameters")),
372
+ React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
373
+ React.createElement(Box, null,
374
+ React.createElement(Typography, { variant: "body1", gutterBottom: true },
375
+ "Temperature: ",
376
+ config.temperature),
377
+ React.createElement(Slider, { value: config.temperature, onChange: (_, value) => handleConfigUpdate({ temperature: value }), min: 0, max: 2, step: 0.1, valueLabelDisplay: "auto" }),
378
+ React.createElement(Typography, { variant: "caption", color: "text.secondary" }, "Controls response creativity: low value = deterministic and focused, middle value = balanced, high value = creative and varied")),
379
+ React.createElement(TextField, { fullWidth: true, label: "Max Tokens", type: "number", value: config.maxTokens || '', onChange: e => handleConfigUpdate({
380
+ maxTokens: e.target.value
381
+ ? parseInt(e.target.value)
382
+ : undefined
383
+ }), inputProps: { min: 1, max: 8192 }, helperText: "Maximum length of AI responses (leave empty for provider default)" }),
384
+ React.createElement(TextField, { fullWidth: true, label: "Max Turns", type: "number", value: config.maxTurns, onChange: e => handleConfigUpdate({
385
+ maxTurns: parseInt(e.target.value)
386
+ }), inputProps: { min: 1, max: 100 }, helperText: "Maximum number of tool execution turns (when using tools)" }))))),
387
+ activeTab === 2 && (React.createElement(Card, { elevation: 2 },
388
+ React.createElement(CardContent, null,
389
+ React.createElement(Typography, { variant: "h6", component: "h2", gutterBottom: true }, "Behavior Settings"),
390
+ React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
391
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.toolsEnabled, onChange: e => handleConfigUpdate({
392
+ toolsEnabled: e.target.checked
393
+ }), color: "primary" }), label: React.createElement(Box, null,
394
+ React.createElement(Typography, { variant: "body1" }, "Enable Tools"),
395
+ React.createElement(Typography, { variant: "caption", color: "text.secondary" }, "Allow the AI to use tools like notebook operations, code execution, and file management")) }),
396
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.sendWithShiftEnter, onChange: e => handleConfigUpdate({
397
+ sendWithShiftEnter: e.target.checked
398
+ }), color: "primary" }), label: React.createElement(Box, null,
399
+ React.createElement(Typography, { variant: "body1" }, "Send with Shift+Enter"),
400
+ React.createElement(Typography, { variant: "caption", color: "text.secondary" }, "Use Shift+Enter to send messages (Enter creates new line)")) }),
401
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.showTokenUsage, onChange: e => handleConfigUpdate({
402
+ showTokenUsage: e.target.checked
403
+ }), color: "primary" }), label: React.createElement(Box, null,
404
+ React.createElement(Typography, { variant: "body1" }, "Show Token Usage"),
405
+ React.createElement(Typography, { variant: "caption", color: "text.secondary" }, "Display token usage information in the chat toolbar")) }),
406
+ React.createElement(Divider, { sx: { my: 1 } }),
407
+ React.createElement(TextField, { fullWidth: true, multiline: true, rows: 3, label: "System Prompt", value: config.systemPrompt, onChange: e => handleConfigUpdate({ systemPrompt: e.target.value }), placeholder: "Define the AI's behavior and personality...", helperText: "Instructions that define how the AI should behave and respond" }),
408
+ React.createElement(Divider, { sx: { my: 2 } }),
409
+ React.createElement(Box, null,
410
+ React.createElement(Typography, { variant: "body1", gutterBottom: true }, "Commands Requiring Approval"),
411
+ React.createElement(Typography, { variant: "caption", color: "text.secondary", gutterBottom: true, sx: { display: 'block' } }, "Commands that require user approval before AI can execute them"),
412
+ React.createElement(List, { sx: { mb: 2, maxHeight: 200, overflow: 'auto' } }, config.commandsRequiringApproval.map((command, index) => (React.createElement(ListItem, { key: index, divider: true },
413
+ React.createElement(ListItemText, { primary: command }),
414
+ React.createElement(ListItemSecondaryAction, null,
415
+ React.createElement(IconButton, { onClick: () => {
416
+ const newCommands = [
417
+ ...config.commandsRequiringApproval
418
+ ];
419
+ newCommands.splice(index, 1);
420
+ handleConfigUpdate({
421
+ commandsRequiringApproval: newCommands
422
+ });
423
+ }, size: "small" },
424
+ React.createElement(Delete, null))))))),
425
+ React.createElement(TextField, { fullWidth: true, label: "Add New Command", placeholder: "e.g., notebook:run-cell", onKeyDown: e => {
426
+ if (e.key === 'Enter') {
427
+ const value = e.target.value.trim();
428
+ if (value &&
429
+ !config.commandsRequiringApproval.includes(value)) {
430
+ const newCommands = [
431
+ ...config.commandsRequiringApproval,
432
+ value
433
+ ];
434
+ handleConfigUpdate({
435
+ commandsRequiringApproval: newCommands
436
+ });
437
+ e.target.value = '';
438
+ }
439
+ }
440
+ }, helperText: "Press Enter to add a command. Common commands: notebook:run-cell, console:execute, fileeditor:run-code" })))))),
441
+ activeTab === 3 && (React.createElement(Card, { elevation: 2 },
442
+ React.createElement(CardContent, null,
443
+ React.createElement(Box, { sx: {
444
+ display: 'flex',
445
+ alignItems: 'center',
446
+ justifyContent: 'space-between',
447
+ mb: 2
448
+ } },
449
+ React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
450
+ React.createElement(Cable, { color: "primary" }),
451
+ React.createElement(Typography, { variant: "h6", component: "h2" }, "Remote MCP Servers")),
452
+ React.createElement(Button, { variant: "contained", startIcon: React.createElement(Add, null), onClick: openAddMCPDialog, size: "small" }, "Add Server")),
453
+ React.createElement(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 } }, "Configure remote Model Context Protocol (MCP) servers to extend the AI's capabilities with external tools and data sources."),
454
+ config.mcpServers.length === 0 ? (React.createElement(Alert, { severity: "info" }, "No MCP servers configured yet. Click \"Add Server\" to connect to remote MCP services.")) : (React.createElement(List, null, config.mcpServers.map(server => (React.createElement(ListItem, { key: server.id, divider: true },
455
+ React.createElement(ListItemText, { primary: React.createElement(Box, { sx: {
456
+ display: 'flex',
457
+ alignItems: 'center',
458
+ gap: 1
459
+ } },
460
+ React.createElement(Typography, { variant: "body1" }, server.name),
461
+ server.enabled &&
462
+ agentManager?.isMCPServerConnected(server.name) && (React.createElement(CheckCircleOutline, { sx: { color: 'success.main', fontSize: 16 } })),
463
+ server.enabled &&
464
+ !agentManager?.isMCPServerConnected(server.name) && (React.createElement(ErrorOutline, { sx: { color: 'error.main', fontSize: 16 } })),
465
+ React.createElement(Switch, { checked: server.enabled, onChange: e => model.updateMCPServer(server.id, {
466
+ enabled: e.target.checked
467
+ }), size: "small", color: "primary" })), secondary: React.createElement(Box, null,
468
+ React.createElement(Typography, { variant: "body2", color: "text.secondary" }, server.url),
469
+ server.enabled &&
470
+ server.connected !== undefined && (React.createElement(Typography, { variant: "caption", color: "text.secondary" },
471
+ "Status:",
472
+ ' ',
473
+ server.connected
474
+ ? 'Connected'
475
+ : 'Connection failed'))) }),
476
+ React.createElement(ListItemSecondaryAction, null,
477
+ React.createElement(IconButton, { onClick: e => handleMCPMenuClick(e, server.id), size: "small" },
478
+ React.createElement(MoreVert, null))))))))))),
479
+ React.createElement(ProviderConfigDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSave: editingProvider ? handleEditProvider : handleAddProvider, initialConfig: editingProvider, mode: editingProvider ? 'edit' : 'add', chatProviderRegistry: chatProviderRegistry, handleSecretField: handleSecretField }),
480
+ React.createElement(Menu, { anchorEl: menuAnchor, open: Boolean(menuAnchor), onClose: handleMenuClose },
481
+ React.createElement(MenuItem, { onClick: () => {
482
+ const provider = config.providers.find(p => p.id === menuProviderId);
483
+ if (provider) {
484
+ openEditDialog(provider);
485
+ }
486
+ } },
487
+ React.createElement(Edit, { sx: { mr: 1 } }),
488
+ "Edit"),
489
+ React.createElement(MenuItem, { onClick: () => handleDeleteProvider(menuProviderId), sx: { color: 'error.main' } },
490
+ React.createElement(Delete, { sx: { mr: 1 } }),
491
+ "Delete")),
492
+ React.createElement(MCPServerDialog, { open: mcpDialogOpen, onClose: () => setMcpDialogOpen(false), onSave: editingMCPServer ? handleEditMCPServer : handleAddMCPServer, initialConfig: editingMCPServer, mode: editingMCPServer ? 'edit' : 'add' }),
493
+ React.createElement(Menu, { anchorEl: mcpMenuAnchor, open: Boolean(mcpMenuAnchor), onClose: handleMCPMenuClose },
494
+ React.createElement(MenuItem, { onClick: () => {
495
+ const server = config.mcpServers.find(s => s.id === mcpMenuServerId);
496
+ if (server) {
497
+ openEditMCPDialog(server);
498
+ }
499
+ } },
500
+ React.createElement(Edit, { sx: { mr: 1 } }),
501
+ "Edit"),
502
+ React.createElement(MenuItem, { onClick: () => handleDeleteMCPServer(mcpMenuServerId), sx: { color: 'error.main' } },
503
+ React.createElement(Delete, { sx: { mr: 1 } }),
504
+ "Delete")))));
505
+ };
506
+ /**
507
+ * Dialog component for adding/editing MCP server configurations
508
+ * @param props - Component props for the MCP server dialog
509
+ * @returns A React component for MCP server configuration
510
+ */
511
+ const MCPServerDialog = ({ open, onClose, onSave, initialConfig, mode }) => {
512
+ const [name, setName] = useState(initialConfig?.name || '');
513
+ const [url, setUrl] = useState(initialConfig?.url || '');
514
+ const [enabled, setEnabled] = useState(initialConfig?.enabled ?? true);
515
+ /**
516
+ * Effect to reset dialog state when opened with new config
517
+ */
518
+ useEffect(() => {
519
+ if (open) {
520
+ setName(initialConfig?.name || '');
521
+ setUrl(initialConfig?.url || '');
522
+ setEnabled(initialConfig?.enabled ?? true);
523
+ }
524
+ }, [open, initialConfig]);
525
+ /**
526
+ * Handle saving the MCP server configuration
527
+ */
528
+ const handleSave = () => {
529
+ if (!name.trim() || !url.trim()) {
530
+ return;
531
+ }
532
+ onSave({
533
+ name: name.trim(),
534
+ url: url.trim(),
535
+ enabled,
536
+ connected: false
537
+ });
538
+ onClose();
539
+ };
540
+ /**
541
+ * Check if a URL is valid
542
+ * @param url - The URL to validate
543
+ * @returns true if the URL is valid
544
+ */
545
+ const _isValidUrl = (url) => {
546
+ try {
547
+ new URL(url);
548
+ return true;
549
+ }
550
+ catch {
551
+ return false;
552
+ }
553
+ };
554
+ const canSave = name.trim() && url.trim() && _isValidUrl(url.trim());
555
+ return (React.createElement(Dialog, { open: open, onClose: onClose, maxWidth: "sm", fullWidth: true },
556
+ React.createElement(DialogTitle, null, mode === 'add' ? 'Add MCP Server' : 'Edit MCP Server'),
557
+ React.createElement(DialogContent, null,
558
+ React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2, pt: 1 } },
559
+ React.createElement(TextField, { autoFocus: true, fullWidth: true, label: "Server Name", value: name, onChange: e => setName(e.target.value), placeholder: "My MCP Server", helperText: "A friendly name to identify this MCP server" }),
560
+ React.createElement(TextField, { fullWidth: true, label: "Server URL", value: url, onChange: e => setUrl(e.target.value), placeholder: "https://example.com/mcp", helperText: "The HTTP/HTTPS URL of the MCP server", error: Boolean(url.trim() && !_isValidUrl(url.trim())) }),
561
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: enabled, onChange: e => setEnabled(e.target.checked), color: "primary" }), label: "Enable this server" }))),
562
+ React.createElement(DialogActions, null,
563
+ React.createElement(Button, { onClick: onClose }, "Cancel"),
564
+ React.createElement(Button, { onClick: handleSave, variant: "contained", disabled: !canSave }, mode === 'add' ? 'Add' : 'Save'))));
565
+ };
566
+ var Private;
567
+ (function (Private) {
568
+ /**
569
+ * The token to use with the secrets manager, setter and getter.
570
+ */
571
+ let secretsToken;
572
+ function setToken(value) {
573
+ secretsToken = value;
574
+ }
575
+ Private.setToken = setToken;
576
+ function getToken() {
577
+ return secretsToken;
578
+ }
579
+ Private.getToken = getToken;
580
+ })(Private || (Private = {}));