@jupyterlite/ai 0.18.0 → 0.19.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.
Files changed (108) hide show
  1. package/lib/chat-commands/clear.d.ts +1 -0
  2. package/lib/chat-commands/index.d.ts +1 -0
  3. package/lib/chat-commands/skills.d.ts +2 -1
  4. package/lib/chat-model-handler.d.ts +3 -1
  5. package/lib/chat-model.d.ts +46 -8
  6. package/lib/chat-model.js +51 -21
  7. package/lib/completion/completion-provider.d.ts +3 -1
  8. package/lib/completion/completion-provider.js +1 -2
  9. package/lib/completion/index.d.ts +1 -0
  10. package/lib/components/clear-button.d.ts +1 -0
  11. package/lib/components/clear-button.js +3 -4
  12. package/lib/components/completion-status.d.ts +1 -0
  13. package/lib/components/completion-status.js +5 -4
  14. package/lib/components/index.d.ts +1 -0
  15. package/lib/components/model-select.d.ts +1 -0
  16. package/lib/components/model-select.js +62 -67
  17. package/lib/components/save-button.d.ts +1 -0
  18. package/lib/components/save-button.js +4 -5
  19. package/lib/components/stop-button.d.ts +1 -0
  20. package/lib/components/stop-button.js +3 -4
  21. package/lib/components/tool-select.d.ts +3 -1
  22. package/lib/components/tool-select.js +47 -60
  23. package/lib/components/usage-display.d.ts +4 -2
  24. package/lib/components/usage-display.js +50 -61
  25. package/lib/diff-manager.d.ts +3 -1
  26. package/lib/index.d.ts +3 -2
  27. package/lib/index.js +28 -17
  28. package/lib/models/settings-model.d.ts +3 -1
  29. package/lib/rendered-message-outputarea.d.ts +1 -0
  30. package/lib/tokens.d.ts +18 -640
  31. package/lib/tokens.js +2 -31
  32. package/lib/widgets/ai-settings.d.ts +3 -1
  33. package/lib/widgets/ai-settings.js +185 -349
  34. package/lib/widgets/main-area-chat.d.ts +1 -0
  35. package/lib/widgets/provider-config-dialog.d.ts +2 -1
  36. package/lib/widgets/provider-config-dialog.js +102 -167
  37. package/package.json +111 -258
  38. package/src/chat-commands/skills.ts +2 -2
  39. package/src/chat-model-handler.ts +6 -4
  40. package/src/chat-model.ts +66 -19
  41. package/src/completion/completion-provider.ts +6 -6
  42. package/src/components/clear-button.tsx +0 -2
  43. package/src/components/completion-status.tsx +2 -2
  44. package/src/components/model-select.tsx +1 -1
  45. package/src/components/stop-button.tsx +0 -2
  46. package/src/components/tool-select.tsx +10 -9
  47. package/src/components/usage-display.tsx +4 -2
  48. package/src/diff-manager.ts +4 -3
  49. package/src/index.ts +62 -44
  50. package/src/models/settings-model.ts +6 -6
  51. package/src/tokens.ts +23 -788
  52. package/src/widgets/ai-settings.tsx +14 -11
  53. package/src/widgets/provider-config-dialog.tsx +8 -8
  54. package/LICENSE +0 -30
  55. package/README.md +0 -49
  56. package/lib/agent.d.ts +0 -280
  57. package/lib/agent.js +0 -1103
  58. package/lib/icons.d.ts +0 -3
  59. package/lib/icons.js +0 -8
  60. package/lib/providers/built-in-providers.d.ts +0 -21
  61. package/lib/providers/built-in-providers.js +0 -233
  62. package/lib/providers/generated-model-info.d.ts +0 -8
  63. package/lib/providers/generated-model-info.js +0 -502
  64. package/lib/providers/model-info.d.ts +0 -6
  65. package/lib/providers/model-info.js +0 -91
  66. package/lib/providers/models.d.ts +0 -37
  67. package/lib/providers/models.js +0 -28
  68. package/lib/providers/provider-registry.d.ts +0 -49
  69. package/lib/providers/provider-registry.js +0 -72
  70. package/lib/providers/provider-tools.d.ts +0 -36
  71. package/lib/providers/provider-tools.js +0 -93
  72. package/lib/skills/index.d.ts +0 -4
  73. package/lib/skills/index.js +0 -7
  74. package/lib/skills/parse-skill.d.ts +0 -25
  75. package/lib/skills/parse-skill.js +0 -69
  76. package/lib/skills/skill-loader.d.ts +0 -25
  77. package/lib/skills/skill-loader.js +0 -133
  78. package/lib/skills/skill-registry.d.ts +0 -31
  79. package/lib/skills/skill-registry.js +0 -100
  80. package/lib/skills/types.d.ts +0 -29
  81. package/lib/skills/types.js +0 -5
  82. package/lib/tools/commands.d.ts +0 -11
  83. package/lib/tools/commands.js +0 -154
  84. package/lib/tools/skills.d.ts +0 -9
  85. package/lib/tools/skills.js +0 -73
  86. package/lib/tools/tool-registry.d.ts +0 -35
  87. package/lib/tools/tool-registry.js +0 -55
  88. package/lib/tools/web.d.ts +0 -8
  89. package/lib/tools/web.js +0 -196
  90. package/src/agent.ts +0 -1431
  91. package/src/icons.ts +0 -11
  92. package/src/providers/built-in-providers.ts +0 -241
  93. package/src/providers/generated-model-info.ts +0 -508
  94. package/src/providers/model-info.ts +0 -145
  95. package/src/providers/models.ts +0 -76
  96. package/src/providers/provider-registry.ts +0 -88
  97. package/src/providers/provider-tools.ts +0 -179
  98. package/src/skills/index.ts +0 -14
  99. package/src/skills/parse-skill.ts +0 -91
  100. package/src/skills/skill-loader.ts +0 -175
  101. package/src/skills/skill-registry.ts +0 -137
  102. package/src/skills/types.ts +0 -37
  103. package/src/tools/commands.ts +0 -210
  104. package/src/tools/skills.ts +0 -84
  105. package/src/tools/tool-registry.ts +0 -63
  106. package/src/tools/web.ts +0 -238
  107. package/src/types.d.ts +0 -4
  108. package/style/icons/jupyternaut-lite.svg +0 -7
@@ -1,3 +1,5 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { getEffectiveContextWindow, SECRETS_REPLACEMENT } from '@jupyternaut/agent';
1
3
  import { ReactWidget } from '@jupyterlab/ui-components';
2
4
  import { Debouncer } from '@lumino/polling';
3
5
  import Add from '@mui/icons-material/Add';
@@ -13,8 +15,6 @@ import MoreVert from '@mui/icons-material/MoreVert';
13
15
  import Settings from '@mui/icons-material/Settings';
14
16
  import { Alert, Box, Button, Card, CardContent, Chip, Dialog, DialogActions, DialogContent, DialogTitle, Divider, FormControl, FormControlLabel, IconButton, InputLabel, List, ListItem, ListItemText, Menu, MenuItem, Select, Switch, Tab, Tabs, TextField, ThemeProvider, Tooltip, Typography, createTheme } from '@mui/material';
15
17
  import React, { useEffect, useMemo, useState } from 'react';
16
- import { getEffectiveContextWindow } from '../providers/model-info';
17
- import { SECRETS_REPLACEMENT } from '../tokens';
18
18
  import { ProviderConfigDialog } from './provider-config-dialog';
19
19
  /**
20
20
  * Create a theme that uses IThemeManager to detect theme
@@ -63,7 +63,7 @@ export class AISettingsWidget extends ReactWidget {
63
63
  * @returns A React element containing the AI settings interface
64
64
  */
65
65
  render() {
66
- return (React.createElement(AISettingsComponent, { model: this._settingsModel, agentManagerFactory: this._agentManagerFactory, themeManager: this._themeManager, providerRegistry: this._providerRegistry, secretsAccess: this._secretsAccess, trans: this._trans }));
66
+ return (_jsx(AISettingsComponent, { model: this._settingsModel, agentManagerFactory: this._agentManagerFactory, themeManager: this._themeManager, providerRegistry: this._providerRegistry, secretsAccess: this._secretsAccess, trans: this._trans }));
67
67
  }
68
68
  _settingsModel;
69
69
  _agentManagerFactory;
@@ -79,7 +79,7 @@ export class AISettingsWidget extends ReactWidget {
79
79
  */
80
80
  const AISettingsComponent = ({ model, agentManagerFactory, themeManager, providerRegistry, secretsAccess, trans }) => {
81
81
  if (!model) {
82
- return React.createElement("div", null, trans.__('Settings model not available'));
82
+ return _jsx("div", { children: trans.__('Settings model not available') });
83
83
  }
84
84
  const [config, setConfig] = useState(model.config || {});
85
85
  const [theme, setTheme] = useState(() => createJupyterLabTheme(themeManager));
@@ -356,8 +356,7 @@ const AISettingsComponent = ({ model, agentManagerFactory, themeManager, provide
356
356
  setMcpMenuAnchor(null);
357
357
  setMcpMenuServerId('');
358
358
  };
359
- return (React.createElement(ThemeProvider, { theme: theme },
360
- React.createElement(Box, { sx: {
359
+ return (_jsx(ThemeProvider, { theme: theme, children: _jsxs(Box, { sx: {
361
360
  height: '100%',
362
361
  maxHeight: '100vh',
363
362
  overflow: 'auto',
@@ -365,339 +364,185 @@ const AISettingsComponent = ({ model, agentManagerFactory, themeManager, provide
365
364
  pb: 4,
366
365
  boxSizing: 'border-box',
367
366
  fontSize: '0.9rem'
368
- } },
369
- React.createElement(Box, { sx: { mb: 2, display: 'flex', alignItems: 'center', gap: 2 } },
370
- React.createElement(Settings, { color: "primary", sx: { fontSize: 24 } }),
371
- React.createElement(Typography, { variant: "h5", component: "h1", sx: { fontWeight: 600 } }, trans.__('AI Settings'))),
372
- React.createElement(Box, { sx: { borderBottom: 1, borderColor: 'divider', mb: 2 } },
373
- React.createElement(Tabs, { value: activeTab, onChange: (_, newValue) => setActiveTab(newValue) },
374
- React.createElement(Tab, { label: trans.__('Providers') }),
375
- React.createElement(Tab, { label: trans.__('Behavior') }),
376
- React.createElement(Tab, { label: trans.__('MCP Servers') }))),
377
- activeTab === 0 && (React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
378
- config.providers.length > 0 && (React.createElement(Card, { elevation: 2 },
379
- React.createElement(CardContent, null,
380
- React.createElement(Typography, { variant: "h6", component: "h2", gutterBottom: true }, trans.__('Default Providers')),
381
- React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
382
- React.createElement(FormControl, { fullWidth: true },
383
- React.createElement(InputLabel, null, trans.__('Chat Provider')),
384
- React.createElement(Select, { value: config.defaultProvider, label: trans.__('Chat Provider'), onChange: e => model.setActiveProvider(e.target.value) }, config.providers.map(provider => (React.createElement(MenuItem, { key: provider.id, value: provider.id }, provider.name))))),
385
- React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.useSameProviderForChatAndCompleter, onChange: e => handleConfigUpdate({
386
- useSameProviderForChatAndCompleter: e.target.checked
387
- }), color: "primary" }), label: trans.__('Use same provider for chat and completions') }),
388
- !config.useSameProviderForChatAndCompleter && (React.createElement(FormControl, { fullWidth: true },
389
- React.createElement(InputLabel, null, trans.__('Completion Provider')),
390
- React.createElement(Select, { value: config.activeCompleterProvider || '', label: trans.__('Completion Provider'), className: "jp-ai-completion-provider-select", onChange: e => model.setActiveCompleterProvider(e.target.value || undefined) },
391
- React.createElement(MenuItem, { value: "" },
392
- React.createElement("em", null, trans.__('No completion'))),
393
- config.providers.map(provider => (React.createElement(MenuItem, { key: provider.id, value: provider.id }, provider.name)))))))))),
394
- React.createElement(Card, { elevation: 2 },
395
- React.createElement(CardContent, null,
396
- React.createElement(Box, { sx: {
397
- display: 'flex',
398
- alignItems: 'center',
399
- justifyContent: 'space-between',
400
- mb: 2
401
- } },
402
- React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
403
- React.createElement(Typography, { variant: "h6", component: "h2" }, trans.__('Configured Providers'))),
404
- React.createElement(Button, { variant: "contained", startIcon: React.createElement(Add, null), onClick: openAddDialog, size: "small" }, trans.__('Add Provider'))),
405
- config.providers.length === 0 ? (React.createElement(Alert, { severity: "info" }, trans.__('No providers configured yet. Click "Add Provider" to get started.'))) : (React.createElement(List, null, config.providers.map(provider => {
406
- const isActive = config.defaultProvider === provider.id;
407
- const isActiveCompleter = config.useSameProviderForChatAndCompleter
408
- ? isActive
409
- : config.activeCompleterProvider === provider.id;
410
- const providerInfo = providerRegistry.getProviderInfo(provider.provider);
411
- const providerToolCapabilities = providerInfo?.providerToolCapabilities;
412
- const params = provider.parameters;
413
- const effectiveContextWindow = getEffectiveContextWindow(provider, providerRegistry);
414
- const webSearchEnabled = !!providerToolCapabilities?.webSearch &&
415
- provider.customSettings?.webSearch?.enabled === true;
416
- const webFetchEnabled = !!providerToolCapabilities?.webFetch &&
417
- provider.customSettings?.webFetch?.enabled === true;
418
- return (React.createElement(ListItem, { key: provider.id, sx: {
419
- flexDirection: 'column',
420
- alignItems: 'stretch',
421
- py: 2
422
- } },
423
- React.createElement(Box, { sx: {
424
- display: 'flex',
425
- justifyContent: 'space-between',
426
- alignItems: 'flex-start',
427
- width: '100%',
428
- mb: 1
429
- } },
430
- React.createElement(Box, { sx: { flex: 1 } },
431
- React.createElement(Box, { sx: {
432
- display: 'flex',
433
- alignItems: 'center',
434
- gap: 1,
435
- mb: 0.5
436
- } },
437
- React.createElement(Typography, { variant: "subtitle1", fontWeight: "medium" }, provider.name),
438
- isActive && (React.createElement(Chip, { label: trans.__('Chat'), size: "small", color: "primary", icon: React.createElement(CheckCircle, null) })),
439
- isActiveCompleter && (React.createElement(Chip, { label: trans.__('Completion'), size: "small", color: "secondary", icon: React.createElement(CheckCircle, null) }))),
440
- React.createElement(Typography, { variant: "body2", color: "text.secondary", gutterBottom: true },
441
- provider.provider,
442
- " \u2022 ",
443
- provider.model,
444
- provider.description &&
445
- ` ${provider.description}`),
446
- (params?.temperature !== undefined ||
447
- params?.maxOutputTokens !== undefined ||
448
- params?.maxTurns !== undefined ||
449
- effectiveContextWindow !== undefined ||
450
- webSearchEnabled ||
451
- webFetchEnabled) && (React.createElement(Box, { sx: {
452
- display: 'flex',
453
- flexWrap: 'wrap',
454
- gap: 1,
455
- mt: 1
456
- } },
457
- params?.temperature !== undefined && (React.createElement(Chip, { label: trans.__('Temp: %1', params.temperature), size: "small", variant: "outlined" })),
458
- params?.maxOutputTokens !== undefined && (React.createElement(Chip, { label: trans.__('Tokens: %1', params.maxOutputTokens), size: "small", variant: "outlined" })),
459
- params?.maxTurns !== undefined && (React.createElement(Chip, { label: trans.__('Turns: %1', params.maxTurns), size: "small", variant: "outlined" })),
460
- effectiveContextWindow !== undefined && (React.createElement(Chip, { label: trans.__('Context: %1', effectiveContextWindow), size: "small", variant: "outlined" })),
461
- webSearchEnabled && (React.createElement(Chip, { label: trans.__('Web Search'), size: "small", variant: "outlined", color: "info" })),
462
- webFetchEnabled && (React.createElement(Chip, { label: trans.__('Web Fetch'), size: "small", variant: "outlined", color: "info" }))))),
463
- React.createElement(IconButton, { onClick: e => handleMenuClick(e, provider.id), size: "small" },
464
- React.createElement(MoreVert, null)))));
465
- }))))),
466
- secretsAccess?.isAvailable && (React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.useSecretsManager, onChange: e => handleConfigUpdate({
467
- useSecretsManager: e.target.checked
468
- }), color: "primary", sx: { alignSelf: 'flex-start' } }), label: React.createElement("div", null,
469
- React.createElement("span", null, trans.__('Use the secrets manager to manage API keys')),
470
- !config.useSecretsManager && (React.createElement(Alert, { severity: "warning", icon: React.createElement(Error, null), sx: { mb: 2 } }, trans.__('The secrets are stored in plain text in settings')))) })))),
471
- activeTab === 1 && (React.createElement(Card, { elevation: 2 },
472
- React.createElement(CardContent, null,
473
- React.createElement(Typography, { variant: "h6", component: "h2", gutterBottom: true }, trans.__('Behavior Settings')),
474
- React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
475
- React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.toolsEnabled, onChange: e => handleConfigUpdate({
476
- toolsEnabled: e.target.checked
477
- }), color: "primary" }), label: React.createElement(Box, null,
478
- React.createElement(Typography, { variant: "body1" }, trans.__('Enable Tools')),
479
- React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Allow the AI to use tools like notebook operations, code execution, and file management'))) }),
480
- React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.sendWithShiftEnter, onChange: e => handleConfigUpdate({
481
- sendWithShiftEnter: e.target.checked
482
- }), color: "primary" }), label: React.createElement(Box, null,
483
- React.createElement(Typography, { variant: "body1" }, trans.__('Send with Shift+Enter')),
484
- React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Use Shift+Enter to send messages (Enter creates new line)'))) }),
485
- React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.autoTitle, onChange: e => handleConfigUpdate({
486
- autoTitle: e.target.checked
487
- }), color: "primary" }), label: React.createElement(Box, null,
488
- React.createElement(Typography, { variant: "body1" }, trans.__('Auto Title')),
489
- React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Automatically generate a chat title from the model for every message until there are 5 messages'))) }),
490
- React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.showTokenUsage, onChange: e => handleConfigUpdate({
491
- showTokenUsage: e.target.checked
492
- }), color: "primary" }), label: React.createElement(Box, null,
493
- React.createElement(Typography, { variant: "body1" }, trans.__('Show Token Usage')),
494
- React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Display token usage information in the chat toolbar'))) }),
495
- React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.showContextUsage, onChange: e => handleConfigUpdate({
496
- showContextUsage: e.target.checked
497
- }), color: "primary" }), label: React.createElement(Box, null,
498
- React.createElement(Typography, { variant: "body1" }, trans.__('Show Context Usage')),
499
- React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Display estimated context usage in the chat toolbar'))) }),
500
- React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.showCellDiff, onChange: e => handleConfigUpdate({
501
- showCellDiff: e.target.checked
502
- }), color: "primary" }), label: React.createElement(Box, null,
503
- React.createElement(Typography, { variant: "body1" }, trans.__('Show Cell Diff')),
504
- React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Show diff view when AI modifies cell content'))) }),
505
- config.showCellDiff && (React.createElement(FormControl, { sx: { ml: 4 } },
506
- React.createElement(InputLabel, null, trans.__('Diff Display Mode')),
507
- React.createElement(Select, { value: config.diffDisplayMode, label: trans.__('Diff Display Mode'), onChange: e => handleConfigUpdate({
508
- diffDisplayMode: e.target.value
509
- }) },
510
- React.createElement(MenuItem, { value: "split" }, trans.__('Split View')),
511
- React.createElement(MenuItem, { value: "unified" }, trans.__('Unified View'))))),
512
- React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.showFileDiff, onChange: e => handleConfigUpdate({
513
- showFileDiff: e.target.checked
514
- }), color: "primary" }), label: React.createElement(Box, null,
515
- React.createElement(Typography, { variant: "body1" }, trans.__('Show File Diff')),
516
- React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Show diff view when AI modifies file content'))) }),
517
- React.createElement(TextField, { fullWidth: true, label: trans.__('Chat Backup Directory'), value: config.chatBackupDirectory, onChange: e => handleConfigUpdate({
518
- chatBackupDirectory: e.target.value
519
- }), helperText: trans.__('Directory where chat history backups are saved (relative to the server root)') }),
520
- React.createElement(Divider, { sx: { my: 1 } }),
521
- React.createElement(TextField, { fullWidth: true, multiline: true, rows: 3, label: trans.__('System Prompt'), value: systemPromptValue, onChange: e => handleSystemPromptChange(e.target.value), placeholder: trans.__("Define the AI's behavior and personality..."), helperText: trans.__('Instructions that define how the AI should behave and respond') }),
522
- React.createElement(TextField, { fullWidth: true, multiline: true, rows: 3, label: trans.__('Completion System Prompt'), value: completionPromptValue, onChange: e => handleCompletionPromptChange(e.target.value), placeholder: trans.__('Define how the AI should generate code completions...'), helperText: trans.__('Instructions that define how the AI should generate code completions') }),
523
- React.createElement(Divider, { sx: { my: 2 } }),
524
- React.createElement(Box, null,
525
- React.createElement(Typography, { variant: "body1", gutterBottom: true, sx: {
526
- display: 'inline-flex',
527
- alignItems: 'center',
528
- gap: 1
529
- } },
530
- trans.__('Skills Paths'),
531
- React.createElement(Tooltip, { title: trans.__('Directories containing agent skills, relative to the server root. Skills are loaded from all paths; the first occurrence of a skill name takes priority.') },
532
- React.createElement(InfoOutlined, { sx: { fontSize: 16 } }))),
533
- React.createElement(List, { sx: { mb: 2, maxHeight: 200, overflow: 'auto' } }, (config.skillsPaths ?? []).map((skillPath, index) => (React.createElement(ListItem, { key: index, divider: true, secondaryAction: React.createElement(IconButton, { onClick: () => {
534
- const newPaths = [...config.skillsPaths];
535
- newPaths.splice(index, 1);
536
- handleConfigUpdate({ skillsPaths: newPaths });
537
- }, size: "small" },
538
- React.createElement(Delete, null)) },
539
- React.createElement(ListItemText, { primary: skillPath }))))),
540
- React.createElement(TextField, { fullWidth: true, label: trans.__('Add Skills Path'), placeholder: trans.__('e.g., .claude/skills'), onKeyDown: e => {
541
- if (e.key === 'Enter') {
542
- const value = e.target.value.trim();
543
- if (value &&
544
- !(config.skillsPaths ?? []).includes(value)) {
545
- const newPaths = [
546
- ...(config.skillsPaths ?? []),
547
- value
548
- ];
549
- handleConfigUpdate({ skillsPaths: newPaths });
550
- e.target.value = '';
551
- }
552
- }
553
- }, helperText: trans.__('Press Enter to add a path. Defaults: .agents/skills, _agents/skills') })),
554
- React.createElement(Divider, { sx: { my: 2 } }),
555
- React.createElement(Box, null,
556
- React.createElement(Typography, { variant: "body1", gutterBottom: true }, trans.__('Commands Requiring Approval')),
557
- React.createElement(Typography, { variant: "caption", color: "text.secondary", gutterBottom: true, sx: { display: 'block' } }, trans.__('Commands that require user approval before AI can execute them')),
558
- React.createElement(List, { sx: { mb: 2, maxHeight: 200, overflow: 'auto' } }, config.commandsRequiringApproval.map((command, index) => (React.createElement(ListItem, { key: index, divider: true, secondaryAction: React.createElement(IconButton, { onClick: () => {
559
- const newCommands = [
560
- ...config.commandsRequiringApproval
561
- ];
562
- newCommands.splice(index, 1);
563
- handleConfigUpdate({
564
- commandsRequiringApproval: newCommands
565
- });
566
- }, size: "small" },
567
- React.createElement(Delete, null)) },
568
- React.createElement(ListItemText, { primary: command }))))),
569
- React.createElement(TextField, { fullWidth: true, label: trans.__('Add New Command'), placeholder: trans.__('e.g., notebook:run-cell'), onKeyDown: e => {
570
- if (e.key === 'Enter') {
571
- const value = e.target.value.trim();
572
- if (value &&
573
- !config.commandsRequiringApproval.includes(value)) {
574
- const newCommands = [
575
- ...config.commandsRequiringApproval,
576
- value
577
- ];
578
- handleConfigUpdate({
579
- commandsRequiringApproval: newCommands
580
- });
581
- e.target.value = '';
582
- }
583
- }
584
- }, helperText: trans.__('Press Enter to add a command. Common commands: notebook:run-cell, console:execute, fileeditor:run-code') })),
585
- React.createElement(Divider, { sx: { my: 2 } }),
586
- React.createElement(Box, null,
587
- React.createElement(Typography, { variant: "body1", gutterBottom: true }, trans.__('Commands Auto-Rendering MIME Bundles')),
588
- React.createElement(Typography, { variant: "caption", color: "text.secondary", gutterBottom: true, sx: { display: 'block' } }, trans.__('Only these execute_command command IDs can auto-render MIME bundle outputs in chat')),
589
- React.createElement(List, { sx: { mb: 2, maxHeight: 200, overflow: 'auto' } }, (config.commandsAutoRenderMimeBundles ?? []).map((command, index) => (React.createElement(ListItem, { key: index, divider: true, secondaryAction: React.createElement(IconButton, { onClick: () => {
590
- const newCommands = [
591
- ...(config.commandsAutoRenderMimeBundles ??
592
- [])
593
- ];
594
- newCommands.splice(index, 1);
595
- handleConfigUpdate({
596
- commandsAutoRenderMimeBundles: newCommands
597
- });
598
- }, size: "small" },
599
- React.createElement(Delete, null)) },
600
- React.createElement(ListItemText, { primary: command }))))),
601
- React.createElement(TextField, { fullWidth: true, label: trans.__('Add Auto-Render Command'), placeholder: trans.__('e.g., jupyterlab-ai-commands:execute-in-kernel'), onKeyDown: e => {
602
- if (e.key === 'Enter') {
603
- const value = e.target.value.trim();
604
- const existingCommands = config.commandsAutoRenderMimeBundles ?? [];
605
- if (value && !existingCommands.includes(value)) {
606
- const newCommands = [...existingCommands, value];
607
- handleConfigUpdate({
608
- commandsAutoRenderMimeBundles: newCommands
609
- });
610
- e.target.value = '';
611
- }
612
- }
613
- }, helperText: trans.__('Press Enter to add a command. Default: jupyterlab-ai-commands:execute-in-kernel') })),
614
- React.createElement(Divider, { sx: { my: 2 } }),
615
- React.createElement(Box, null,
616
- React.createElement(Typography, { variant: "body1", gutterBottom: true }, trans.__('Trusted MIME Types for Auto-Render')),
617
- React.createElement(Typography, { variant: "caption", color: "text.secondary", gutterBottom: true, sx: { display: 'block' } }, trans.__('When auto-rendering command outputs, these MIME types are marked trusted in chat')),
618
- React.createElement(List, { sx: { mb: 2, maxHeight: 200, overflow: 'auto' } }, (config.trustedMimeTypesForAutoRender ?? []).map((mimeType, index) => (React.createElement(ListItem, { key: index, divider: true, secondaryAction: React.createElement(IconButton, { onClick: () => {
619
- const newMimeTypes = [
620
- ...(config.trustedMimeTypesForAutoRender ??
621
- [])
622
- ];
623
- newMimeTypes.splice(index, 1);
624
- handleConfigUpdate({
625
- trustedMimeTypesForAutoRender: newMimeTypes
626
- });
627
- }, size: "small" },
628
- React.createElement(Delete, null)) },
629
- React.createElement(ListItemText, { primary: mimeType }))))),
630
- React.createElement(TextField, { fullWidth: true, label: trans.__('Add Trusted MIME Type'), placeholder: trans.__('e.g., text/html'), onKeyDown: e => {
631
- if (e.key === 'Enter') {
632
- const value = e.target.value.trim();
633
- const existingMimeTypes = config.trustedMimeTypesForAutoRender ?? [];
634
- if (value && !existingMimeTypes.includes(value)) {
635
- const newMimeTypes = [...existingMimeTypes, value];
636
- handleConfigUpdate({
637
- trustedMimeTypesForAutoRender: newMimeTypes
638
- });
639
- e.target.value = '';
640
- }
641
- }
642
- }, helperText: trans.__('Press Enter to add a MIME type. Default: text/html') })))))),
643
- activeTab === 2 && (React.createElement(Card, { elevation: 2 },
644
- React.createElement(CardContent, null,
645
- React.createElement(Box, { sx: {
646
- display: 'flex',
647
- alignItems: 'center',
648
- justifyContent: 'space-between',
649
- mb: 2
650
- } },
651
- React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
652
- React.createElement(Cable, { color: "primary" }),
653
- React.createElement(Typography, { variant: "h6", component: "h2" }, trans.__('Remote MCP Servers'))),
654
- React.createElement(Button, { variant: "contained", startIcon: React.createElement(Add, null), onClick: openAddMCPDialog, size: "small" }, trans.__('Add Server'))),
655
- React.createElement(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 } }, trans.__("Configure remote Model Context Protocol (MCP) servers to extend the AI's capabilities with external tools and data sources.")),
656
- config.mcpServers.length === 0 ? (React.createElement(Alert, { severity: "info" }, trans.__('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, secondaryAction: React.createElement(IconButton, { onClick: e => handleMCPMenuClick(e, server.id), size: "small" },
657
- React.createElement(MoreVert, null)) },
658
- React.createElement(ListItemText, { primary: React.createElement(Box, { sx: {
367
+ }, children: [_jsxs(Box, { sx: { mb: 2, display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Settings, { color: "primary", sx: { fontSize: 24 } }), _jsx(Typography, { variant: "h5", component: "h1", sx: { fontWeight: 600 }, children: trans.__('AI Settings') })] }), _jsx(Box, { sx: { borderBottom: 1, borderColor: 'divider', mb: 2 }, children: _jsxs(Tabs, { value: activeTab, onChange: (_, newValue) => setActiveTab(newValue), children: [_jsx(Tab, { label: trans.__('Providers') }), _jsx(Tab, { label: trans.__('Behavior') }), _jsx(Tab, { label: trans.__('MCP Servers') })] }) }), activeTab === 0 && (_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [config.providers.length > 0 && (_jsx(Card, { elevation: 2, children: _jsxs(CardContent, { children: [_jsx(Typography, { variant: "h6", component: "h2", gutterBottom: true, children: trans.__('Default Providers') }), _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsxs(FormControl, { fullWidth: true, children: [_jsx(InputLabel, { children: trans.__('Chat Provider') }), _jsx(Select, { value: config.defaultProvider, label: trans.__('Chat Provider'), onChange: e => model.setActiveProvider(e.target.value), children: config.providers.map(provider => (_jsx(MenuItem, { value: provider.id, children: provider.name }, provider.id))) })] }), _jsx(FormControlLabel, { control: _jsx(Switch, { checked: config.useSameProviderForChatAndCompleter, onChange: e => handleConfigUpdate({
368
+ useSameProviderForChatAndCompleter: e.target.checked
369
+ }), color: "primary" }), label: trans.__('Use same provider for chat and completions') }), !config.useSameProviderForChatAndCompleter && (_jsxs(FormControl, { fullWidth: true, children: [_jsx(InputLabel, { children: trans.__('Completion Provider') }), _jsxs(Select, { value: config.activeCompleterProvider || '', label: trans.__('Completion Provider'), className: "jp-ai-completion-provider-select", onChange: e => model.setActiveCompleterProvider(e.target.value || undefined), children: [_jsx(MenuItem, { value: "", children: _jsx("em", { children: trans.__('No completion') }) }), config.providers.map(provider => (_jsx(MenuItem, { value: provider.id, children: provider.name }, provider.id)))] })] }))] })] }) })), _jsx(Card, { elevation: 2, children: _jsxs(CardContent, { children: [_jsxs(Box, { sx: {
370
+ display: 'flex',
371
+ alignItems: 'center',
372
+ justifyContent: 'space-between',
373
+ mb: 2
374
+ }, children: [_jsx(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: _jsx(Typography, { variant: "h6", component: "h2", children: trans.__('Configured Providers') }) }), _jsx(Button, { variant: "contained", startIcon: _jsx(Add, {}), onClick: openAddDialog, size: "small", children: trans.__('Add Provider') })] }), config.providers.length === 0 ? (_jsx(Alert, { severity: "info", children: trans.__('No providers configured yet. Click "Add Provider" to get started.') })) : (_jsx(List, { children: config.providers.map(provider => {
375
+ const isActive = config.defaultProvider === provider.id;
376
+ const isActiveCompleter = config.useSameProviderForChatAndCompleter
377
+ ? isActive
378
+ : config.activeCompleterProvider === provider.id;
379
+ const providerInfo = providerRegistry.getProviderInfo(provider.provider);
380
+ const providerToolCapabilities = providerInfo?.providerToolCapabilities;
381
+ const params = provider.parameters;
382
+ const effectiveContextWindow = getEffectiveContextWindow(provider, providerRegistry);
383
+ const webSearchEnabled = !!providerToolCapabilities?.webSearch &&
384
+ provider.customSettings?.webSearch?.enabled === true;
385
+ const webFetchEnabled = !!providerToolCapabilities?.webFetch &&
386
+ provider.customSettings?.webFetch?.enabled === true;
387
+ return (_jsx(ListItem, { sx: {
388
+ flexDirection: 'column',
389
+ alignItems: 'stretch',
390
+ py: 2
391
+ }, children: _jsxs(Box, { sx: {
392
+ display: 'flex',
393
+ justifyContent: 'space-between',
394
+ alignItems: 'flex-start',
395
+ width: '100%',
396
+ mb: 1
397
+ }, children: [_jsxs(Box, { sx: { flex: 1 }, children: [_jsxs(Box, { sx: {
398
+ display: 'flex',
399
+ alignItems: 'center',
400
+ gap: 1,
401
+ mb: 0.5
402
+ }, children: [_jsx(Typography, { variant: "subtitle1", fontWeight: "medium", children: provider.name }), isActive && (_jsx(Chip, { label: trans.__('Chat'), size: "small", color: "primary", icon: _jsx(CheckCircle, {}) })), isActiveCompleter && (_jsx(Chip, { label: trans.__('Completion'), size: "small", color: "secondary", icon: _jsx(CheckCircle, {}) }))] }), _jsxs(Typography, { variant: "body2", color: "text.secondary", gutterBottom: true, children: [provider.provider, " \u2022 ", provider.model, provider.description &&
403
+ ` • ${provider.description}`] }), (params?.temperature !== undefined ||
404
+ params?.maxOutputTokens !== undefined ||
405
+ params?.maxTurns !== undefined ||
406
+ effectiveContextWindow !== undefined ||
407
+ webSearchEnabled ||
408
+ webFetchEnabled) && (_jsxs(Box, { sx: {
409
+ display: 'flex',
410
+ flexWrap: 'wrap',
411
+ gap: 1,
412
+ mt: 1
413
+ }, children: [params?.temperature !== undefined && (_jsx(Chip, { label: trans.__('Temp: %1', params.temperature), size: "small", variant: "outlined" })), params?.maxOutputTokens !== undefined && (_jsx(Chip, { label: trans.__('Tokens: %1', params.maxOutputTokens), size: "small", variant: "outlined" })), params?.maxTurns !== undefined && (_jsx(Chip, { label: trans.__('Turns: %1', params.maxTurns), size: "small", variant: "outlined" })), effectiveContextWindow !== undefined && (_jsx(Chip, { label: trans.__('Context: %1', effectiveContextWindow), size: "small", variant: "outlined" })), webSearchEnabled && (_jsx(Chip, { label: trans.__('Web Search'), size: "small", variant: "outlined", color: "info" })), webFetchEnabled && (_jsx(Chip, { label: trans.__('Web Fetch'), size: "small", variant: "outlined", color: "info" }))] }))] }), _jsx(IconButton, { onClick: e => handleMenuClick(e, provider.id), size: "small", children: _jsx(MoreVert, {}) })] }) }, provider.id));
414
+ }) }))] }) }), secretsAccess?.isAvailable && (_jsx(FormControlLabel, { control: _jsx(Switch, { checked: config.useSecretsManager, onChange: e => handleConfigUpdate({
415
+ useSecretsManager: e.target.checked
416
+ }), color: "primary", sx: { alignSelf: 'flex-start' } }), label: _jsxs("div", { children: [_jsx("span", { children: trans.__('Use the secrets manager to manage API keys') }), !config.useSecretsManager && (_jsx(Alert, { severity: "warning", icon: _jsx(Error, {}), sx: { mb: 2 }, children: trans.__('The secrets are stored in plain text in settings') }))] }) }))] })), activeTab === 1 && (_jsx(Card, { elevation: 2, children: _jsxs(CardContent, { children: [_jsx(Typography, { variant: "h6", component: "h2", gutterBottom: true, children: trans.__('Behavior Settings') }), _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsx(FormControlLabel, { control: _jsx(Switch, { checked: config.toolsEnabled, onChange: e => handleConfigUpdate({
417
+ toolsEnabled: e.target.checked
418
+ }), color: "primary" }), label: _jsxs(Box, { children: [_jsx(Typography, { variant: "body1", children: trans.__('Enable Tools') }), _jsx(Typography, { variant: "caption", color: "text.secondary", children: trans.__('Allow the AI to use tools like notebook operations, code execution, and file management') })] }) }), _jsx(FormControlLabel, { control: _jsx(Switch, { checked: config.sendWithShiftEnter, onChange: e => handleConfigUpdate({
419
+ sendWithShiftEnter: e.target.checked
420
+ }), color: "primary" }), label: _jsxs(Box, { children: [_jsx(Typography, { variant: "body1", children: trans.__('Send with Shift+Enter') }), _jsx(Typography, { variant: "caption", color: "text.secondary", children: trans.__('Use Shift+Enter to send messages (Enter creates new line)') })] }) }), _jsx(FormControlLabel, { control: _jsx(Switch, { checked: config.autoTitle, onChange: e => handleConfigUpdate({
421
+ autoTitle: e.target.checked
422
+ }), color: "primary" }), label: _jsxs(Box, { children: [_jsx(Typography, { variant: "body1", children: trans.__('Auto Title') }), _jsx(Typography, { variant: "caption", color: "text.secondary", children: trans.__('Automatically generate a chat title from the model for every message until there are 5 messages') })] }) }), _jsx(FormControlLabel, { control: _jsx(Switch, { checked: config.showTokenUsage, onChange: e => handleConfigUpdate({
423
+ showTokenUsage: e.target.checked
424
+ }), color: "primary" }), label: _jsxs(Box, { children: [_jsx(Typography, { variant: "body1", children: trans.__('Show Token Usage') }), _jsx(Typography, { variant: "caption", color: "text.secondary", children: trans.__('Display token usage information in the chat toolbar') })] }) }), _jsx(FormControlLabel, { control: _jsx(Switch, { checked: config.showContextUsage, onChange: e => handleConfigUpdate({
425
+ showContextUsage: e.target.checked
426
+ }), color: "primary" }), label: _jsxs(Box, { children: [_jsx(Typography, { variant: "body1", children: trans.__('Show Context Usage') }), _jsx(Typography, { variant: "caption", color: "text.secondary", children: trans.__('Display estimated context usage in the chat toolbar') })] }) }), _jsx(FormControlLabel, { control: _jsx(Switch, { checked: config.showCellDiff, onChange: e => handleConfigUpdate({
427
+ showCellDiff: e.target.checked
428
+ }), color: "primary" }), label: _jsxs(Box, { children: [_jsx(Typography, { variant: "body1", children: trans.__('Show Cell Diff') }), _jsx(Typography, { variant: "caption", color: "text.secondary", children: trans.__('Show diff view when AI modifies cell content') })] }) }), config.showCellDiff && (_jsxs(FormControl, { sx: { ml: 4 }, children: [_jsx(InputLabel, { children: trans.__('Diff Display Mode') }), _jsxs(Select, { value: config.diffDisplayMode, label: trans.__('Diff Display Mode'), onChange: e => handleConfigUpdate({
429
+ diffDisplayMode: e.target.value
430
+ }), children: [_jsx(MenuItem, { value: "split", children: trans.__('Split View') }), _jsx(MenuItem, { value: "unified", children: trans.__('Unified View') })] })] })), _jsx(FormControlLabel, { control: _jsx(Switch, { checked: config.showFileDiff, onChange: e => handleConfigUpdate({
431
+ showFileDiff: e.target.checked
432
+ }), color: "primary" }), label: _jsxs(Box, { children: [_jsx(Typography, { variant: "body1", children: trans.__('Show File Diff') }), _jsx(Typography, { variant: "caption", color: "text.secondary", children: trans.__('Show diff view when AI modifies file content') })] }) }), _jsx(TextField, { fullWidth: true, label: trans.__('Chat Backup Directory'), value: config.chatBackupDirectory, onChange: e => handleConfigUpdate({
433
+ chatBackupDirectory: e.target.value
434
+ }), helperText: trans.__('Directory where chat history backups are saved (relative to the server root)') }), _jsx(Divider, { sx: { my: 1 } }), _jsx(TextField, { fullWidth: true, multiline: true, rows: 3, label: trans.__('System Prompt'), value: systemPromptValue, onChange: e => handleSystemPromptChange(e.target.value), placeholder: trans.__("Define the AI's behavior and personality..."), helperText: trans.__('Instructions that define how the AI should behave and respond') }), _jsx(TextField, { fullWidth: true, multiline: true, rows: 3, label: trans.__('Completion System Prompt'), value: completionPromptValue, onChange: e => handleCompletionPromptChange(e.target.value), placeholder: trans.__('Define how the AI should generate code completions...'), helperText: trans.__('Instructions that define how the AI should generate code completions') }), _jsx(Divider, { sx: { my: 2 } }), _jsxs(Box, { children: [_jsxs(Typography, { variant: "body1", gutterBottom: true, sx: {
435
+ display: 'inline-flex',
436
+ alignItems: 'center',
437
+ gap: 1
438
+ }, children: [trans.__('Skills Paths'), _jsx(Tooltip, { title: trans.__('Directories containing agent skills, relative to the server root. Skills are loaded from all paths; the first occurrence of a skill name takes priority.'), children: _jsx(InfoOutlined, { sx: { fontSize: 16 } }) })] }), _jsx(List, { sx: { mb: 2, maxHeight: 200, overflow: 'auto' }, children: (config.skillsPaths ?? []).map((skillPath, index) => (_jsx(ListItem, { divider: true, secondaryAction: _jsx(IconButton, { onClick: () => {
439
+ const newPaths = [...config.skillsPaths];
440
+ newPaths.splice(index, 1);
441
+ handleConfigUpdate({ skillsPaths: newPaths });
442
+ }, size: "small", children: _jsx(Delete, {}) }), children: _jsx(ListItemText, { primary: skillPath }) }, index))) }), _jsx(TextField, { fullWidth: true, label: trans.__('Add Skills Path'), placeholder: trans.__('e.g., .claude/skills'), onKeyDown: e => {
443
+ if (e.key === 'Enter') {
444
+ const value = e.target.value.trim();
445
+ if (value &&
446
+ !(config.skillsPaths ?? []).includes(value)) {
447
+ const newPaths = [
448
+ ...(config.skillsPaths ?? []),
449
+ value
450
+ ];
451
+ handleConfigUpdate({ skillsPaths: newPaths });
452
+ e.target.value = '';
453
+ }
454
+ }
455
+ }, helperText: trans.__('Press Enter to add a path. Defaults: .agents/skills, _agents/skills') })] }), _jsx(Divider, { sx: { my: 2 } }), _jsxs(Box, { children: [_jsx(Typography, { variant: "body1", gutterBottom: true, children: trans.__('Commands Requiring Approval') }), _jsx(Typography, { variant: "caption", color: "text.secondary", gutterBottom: true, sx: { display: 'block' }, children: trans.__('Commands that require user approval before AI can execute them') }), _jsx(List, { sx: { mb: 2, maxHeight: 200, overflow: 'auto' }, children: config.commandsRequiringApproval.map((command, index) => (_jsx(ListItem, { divider: true, secondaryAction: _jsx(IconButton, { onClick: () => {
456
+ const newCommands = [
457
+ ...config.commandsRequiringApproval
458
+ ];
459
+ newCommands.splice(index, 1);
460
+ handleConfigUpdate({
461
+ commandsRequiringApproval: newCommands
462
+ });
463
+ }, size: "small", children: _jsx(Delete, {}) }), children: _jsx(ListItemText, { primary: command }) }, index))) }), _jsx(TextField, { fullWidth: true, label: trans.__('Add New Command'), placeholder: trans.__('e.g., notebook:run-cell'), onKeyDown: e => {
464
+ if (e.key === 'Enter') {
465
+ const value = e.target.value.trim();
466
+ if (value &&
467
+ !config.commandsRequiringApproval.includes(value)) {
468
+ const newCommands = [
469
+ ...config.commandsRequiringApproval,
470
+ value
471
+ ];
472
+ handleConfigUpdate({
473
+ commandsRequiringApproval: newCommands
474
+ });
475
+ e.target.value = '';
476
+ }
477
+ }
478
+ }, helperText: trans.__('Press Enter to add a command. Common commands: notebook:run-cell, console:execute, fileeditor:run-code') })] }), _jsx(Divider, { sx: { my: 2 } }), _jsxs(Box, { children: [_jsx(Typography, { variant: "body1", gutterBottom: true, children: trans.__('Commands Auto-Rendering MIME Bundles') }), _jsx(Typography, { variant: "caption", color: "text.secondary", gutterBottom: true, sx: { display: 'block' }, children: trans.__('Only these execute_command command IDs can auto-render MIME bundle outputs in chat') }), _jsx(List, { sx: { mb: 2, maxHeight: 200, overflow: 'auto' }, children: (config.commandsAutoRenderMimeBundles ?? []).map((command, index) => (_jsx(ListItem, { divider: true, secondaryAction: _jsx(IconButton, { onClick: () => {
479
+ const newCommands = [
480
+ ...(config.commandsAutoRenderMimeBundles ??
481
+ [])
482
+ ];
483
+ newCommands.splice(index, 1);
484
+ handleConfigUpdate({
485
+ commandsAutoRenderMimeBundles: newCommands
486
+ });
487
+ }, size: "small", children: _jsx(Delete, {}) }), children: _jsx(ListItemText, { primary: command }) }, index))) }), _jsx(TextField, { fullWidth: true, label: trans.__('Add Auto-Render Command'), placeholder: trans.__('e.g., jupyterlab-ai-commands:execute-in-kernel'), onKeyDown: e => {
488
+ if (e.key === 'Enter') {
489
+ const value = e.target.value.trim();
490
+ const existingCommands = config.commandsAutoRenderMimeBundles ?? [];
491
+ if (value && !existingCommands.includes(value)) {
492
+ const newCommands = [...existingCommands, value];
493
+ handleConfigUpdate({
494
+ commandsAutoRenderMimeBundles: newCommands
495
+ });
496
+ e.target.value = '';
497
+ }
498
+ }
499
+ }, helperText: trans.__('Press Enter to add a command. Default: jupyterlab-ai-commands:execute-in-kernel') })] }), _jsx(Divider, { sx: { my: 2 } }), _jsxs(Box, { children: [_jsx(Typography, { variant: "body1", gutterBottom: true, children: trans.__('Trusted MIME Types for Auto-Render') }), _jsx(Typography, { variant: "caption", color: "text.secondary", gutterBottom: true, sx: { display: 'block' }, children: trans.__('When auto-rendering command outputs, these MIME types are marked trusted in chat') }), _jsx(List, { sx: { mb: 2, maxHeight: 200, overflow: 'auto' }, children: (config.trustedMimeTypesForAutoRender ?? []).map((mimeType, index) => (_jsx(ListItem, { divider: true, secondaryAction: _jsx(IconButton, { onClick: () => {
500
+ const newMimeTypes = [
501
+ ...(config.trustedMimeTypesForAutoRender ??
502
+ [])
503
+ ];
504
+ newMimeTypes.splice(index, 1);
505
+ handleConfigUpdate({
506
+ trustedMimeTypesForAutoRender: newMimeTypes
507
+ });
508
+ }, size: "small", children: _jsx(Delete, {}) }), children: _jsx(ListItemText, { primary: mimeType }) }, index))) }), _jsx(TextField, { fullWidth: true, label: trans.__('Add Trusted MIME Type'), placeholder: trans.__('e.g., text/html'), onKeyDown: e => {
509
+ if (e.key === 'Enter') {
510
+ const value = e.target.value.trim();
511
+ const existingMimeTypes = config.trustedMimeTypesForAutoRender ?? [];
512
+ if (value && !existingMimeTypes.includes(value)) {
513
+ const newMimeTypes = [...existingMimeTypes, value];
514
+ handleConfigUpdate({
515
+ trustedMimeTypesForAutoRender: newMimeTypes
516
+ });
517
+ e.target.value = '';
518
+ }
519
+ }
520
+ }, helperText: trans.__('Press Enter to add a MIME type. Default: text/html') })] })] })] }) })), activeTab === 2 && (_jsx(Card, { elevation: 2, children: _jsxs(CardContent, { children: [_jsxs(Box, { sx: {
659
521
  display: 'flex',
660
522
  alignItems: 'center',
661
- gap: 1
662
- } },
663
- React.createElement(Typography, { variant: "body1" }, server.name),
664
- server.enabled &&
665
- agentManagerFactory?.isMCPServerConnected(server.name) && (React.createElement(CheckCircleOutline, { sx: { color: 'success.main', fontSize: 16 } })),
666
- server.enabled &&
667
- !agentManagerFactory?.isMCPServerConnected(server.name) && (React.createElement(ErrorOutline, { sx: { color: 'error.main', fontSize: 16 } })),
668
- React.createElement(Switch, { checked: server.enabled, onChange: e => model.updateMCPServer(server.id, {
669
- enabled: e.target.checked
670
- }), size: "small", color: "primary" })), secondary: React.createElement(Box, null,
671
- React.createElement(Typography, { variant: "body2", color: "text.secondary" }, server.url),
672
- server.enabled && agentManagerFactory && (React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Status: %1', agentManagerFactory.isMCPServerConnected(server.name)
673
- ? trans.__('Connected')
674
- : trans.__('Connection failed'))))) }))))))))),
675
- React.createElement(ProviderConfigDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSave: editingProvider ? handleEditProvider : handleAddProvider, initialConfig: editingProvider, mode: editingProvider ? 'edit' : 'add', providerRegistry: providerRegistry, handleSecretField: handleSecretField, trans: trans }),
676
- React.createElement(Menu, { anchorEl: menuAnchor, open: Boolean(menuAnchor), onClose: handleMenuClose },
677
- React.createElement(MenuItem, { onClick: () => {
678
- const provider = config.providers.find(p => p.id === menuProviderId);
679
- if (provider) {
680
- openEditDialog(provider);
681
- }
682
- } },
683
- React.createElement(Edit, { sx: { mr: 1 } }),
684
- trans.__('Edit')),
685
- React.createElement(MenuItem, { onClick: () => handleDeleteProvider(menuProviderId), sx: { color: 'error.main' } },
686
- React.createElement(Delete, { sx: { mr: 1 } }),
687
- trans.__('Delete'))),
688
- React.createElement(MCPServerDialog, { open: mcpDialogOpen, onClose: () => setMcpDialogOpen(false), onSave: editingMCPServer ? handleEditMCPServer : handleAddMCPServer, initialConfig: editingMCPServer, mode: editingMCPServer ? 'edit' : 'add', trans: trans }),
689
- React.createElement(Menu, { anchorEl: mcpMenuAnchor, open: Boolean(mcpMenuAnchor), onClose: handleMCPMenuClose },
690
- React.createElement(MenuItem, { onClick: () => {
691
- const server = config.mcpServers.find(s => s.id === mcpMenuServerId);
692
- if (server) {
693
- openEditMCPDialog(server);
694
- }
695
- } },
696
- React.createElement(Edit, { sx: { mr: 1 } }),
697
- trans.__('Edit')),
698
- React.createElement(MenuItem, { onClick: () => handleDeleteMCPServer(mcpMenuServerId), sx: { color: 'error.main' } },
699
- React.createElement(Delete, { sx: { mr: 1 } }),
700
- trans.__('Delete'))))));
523
+ justifyContent: 'space-between',
524
+ mb: 2
525
+ }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [_jsx(Cable, { color: "primary" }), _jsx(Typography, { variant: "h6", component: "h2", children: trans.__('Remote MCP Servers') })] }), _jsx(Button, { variant: "contained", startIcon: _jsx(Add, {}), onClick: openAddMCPDialog, size: "small", children: trans.__('Add Server') })] }), _jsx(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: trans.__("Configure remote Model Context Protocol (MCP) servers to extend the AI's capabilities with external tools and data sources.") }), config.mcpServers.length === 0 ? (_jsx(Alert, { severity: "info", children: trans.__('No MCP servers configured yet. Click "Add Server" to connect to remote MCP services.') })) : (_jsx(List, { children: config.mcpServers.map(server => (_jsx(ListItem, { divider: true, secondaryAction: _jsx(IconButton, { onClick: e => handleMCPMenuClick(e, server.id), size: "small", children: _jsx(MoreVert, {}) }), children: _jsx(ListItemText, { primary: _jsxs(Box, { sx: {
526
+ display: 'flex',
527
+ alignItems: 'center',
528
+ gap: 1
529
+ }, children: [_jsx(Typography, { variant: "body1", children: server.name }), server.enabled &&
530
+ agentManagerFactory?.isMCPServerConnected(server.name) && (_jsx(CheckCircleOutline, { sx: { color: 'success.main', fontSize: 16 } })), server.enabled &&
531
+ !agentManagerFactory?.isMCPServerConnected(server.name) && (_jsx(ErrorOutline, { sx: { color: 'error.main', fontSize: 16 } })), _jsx(Switch, { checked: server.enabled, onChange: e => model.updateMCPServer(server.id, {
532
+ enabled: e.target.checked
533
+ }), size: "small", color: "primary" })] }), secondary: _jsxs(Box, { children: [_jsx(Typography, { variant: "body2", color: "text.secondary", children: server.url }), server.enabled && agentManagerFactory && (_jsx(Typography, { variant: "caption", color: "text.secondary", children: trans.__('Status: %1', agentManagerFactory.isMCPServerConnected(server.name)
534
+ ? trans.__('Connected')
535
+ : trans.__('Connection failed')) }))] }) }) }, server.id))) }))] }) })), _jsx(ProviderConfigDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSave: editingProvider ? handleEditProvider : handleAddProvider, initialConfig: editingProvider, mode: editingProvider ? 'edit' : 'add', providerRegistry: providerRegistry, handleSecretField: handleSecretField, trans: trans }), _jsxs(Menu, { anchorEl: menuAnchor, open: Boolean(menuAnchor), onClose: handleMenuClose, children: [_jsxs(MenuItem, { onClick: () => {
536
+ const provider = config.providers.find(p => p.id === menuProviderId);
537
+ if (provider) {
538
+ openEditDialog(provider);
539
+ }
540
+ }, children: [_jsx(Edit, { sx: { mr: 1 } }), trans.__('Edit')] }), _jsxs(MenuItem, { onClick: () => handleDeleteProvider(menuProviderId), sx: { color: 'error.main' }, children: [_jsx(Delete, { sx: { mr: 1 } }), trans.__('Delete')] })] }), _jsx(MCPServerDialog, { open: mcpDialogOpen, onClose: () => setMcpDialogOpen(false), onSave: editingMCPServer ? handleEditMCPServer : handleAddMCPServer, initialConfig: editingMCPServer, mode: editingMCPServer ? 'edit' : 'add', trans: trans }), _jsxs(Menu, { anchorEl: mcpMenuAnchor, open: Boolean(mcpMenuAnchor), onClose: handleMCPMenuClose, children: [_jsxs(MenuItem, { onClick: () => {
541
+ const server = config.mcpServers.find(s => s.id === mcpMenuServerId);
542
+ if (server) {
543
+ openEditMCPDialog(server);
544
+ }
545
+ }, children: [_jsx(Edit, { sx: { mr: 1 } }), trans.__('Edit')] }), _jsxs(MenuItem, { onClick: () => handleDeleteMCPServer(mcpMenuServerId), sx: { color: 'error.main' }, children: [_jsx(Delete, { sx: { mr: 1 } }), trans.__('Delete')] })] })] }) }));
701
546
  };
702
547
  /**
703
548
  * Dialog component for adding/editing MCP server configurations
@@ -747,16 +592,7 @@ const MCPServerDialog = ({ open, onClose, onSave, initialConfig, mode, trans })
747
592
  }
748
593
  };
749
594
  const canSave = name.trim() && url.trim() && _isValidUrl(url.trim());
750
- return (React.createElement(Dialog, { open: open, onClose: onClose, maxWidth: "sm", fullWidth: true },
751
- React.createElement(DialogTitle, null, mode === 'add'
752
- ? trans.__('Add MCP Server')
753
- : trans.__('Edit MCP Server')),
754
- React.createElement(DialogContent, null,
755
- React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2, pt: 1 } },
756
- React.createElement(TextField, { autoFocus: true, fullWidth: true, label: trans.__('Server Name'), value: name, onChange: e => setName(e.target.value), placeholder: trans.__('My MCP Server'), helperText: trans.__('A friendly name to identify this MCP server') }),
757
- React.createElement(TextField, { fullWidth: true, label: trans.__('Server URL'), value: url, onChange: e => setUrl(e.target.value), placeholder: trans.__('https://example.com/mcp'), helperText: trans.__('The HTTP/HTTPS URL of the MCP server'), error: Boolean(url.trim() && !_isValidUrl(url.trim())) }),
758
- React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: enabled, onChange: e => setEnabled(e.target.checked), color: "primary" }), label: trans.__('Enable this server') }))),
759
- React.createElement(DialogActions, null,
760
- React.createElement(Button, { onClick: onClose }, trans.__('Cancel')),
761
- React.createElement(Button, { onClick: handleSave, variant: "contained", disabled: !canSave }, mode === 'add' ? trans.__('Add') : trans.__('Save')))));
595
+ return (_jsxs(Dialog, { open: open, onClose: onClose, maxWidth: "sm", fullWidth: true, children: [_jsx(DialogTitle, { children: mode === 'add'
596
+ ? trans.__('Add MCP Server')
597
+ : trans.__('Edit MCP Server') }), _jsx(DialogContent, { children: _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2, pt: 1 }, children: [_jsx(TextField, { autoFocus: true, fullWidth: true, label: trans.__('Server Name'), value: name, onChange: e => setName(e.target.value), placeholder: trans.__('My MCP Server'), helperText: trans.__('A friendly name to identify this MCP server') }), _jsx(TextField, { fullWidth: true, label: trans.__('Server URL'), value: url, onChange: e => setUrl(e.target.value), placeholder: trans.__('https://example.com/mcp'), helperText: trans.__('The HTTP/HTTPS URL of the MCP server'), error: Boolean(url.trim() && !_isValidUrl(url.trim())) }), _jsx(FormControlLabel, { control: _jsx(Switch, { checked: enabled, onChange: e => setEnabled(e.target.checked), color: "primary" }), label: trans.__('Enable this server') })] }) }), _jsxs(DialogActions, { children: [_jsx(Button, { onClick: onClose, children: trans.__('Cancel') }), _jsx(Button, { onClick: handleSave, variant: "contained", disabled: !canSave, children: mode === 'add' ? trans.__('Add') : trans.__('Save') })] })] }));
762
598
  };