@jupyterlite/ai 0.17.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 (114) 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 +4 -3
  5. package/lib/chat-model-handler.js +2 -1
  6. package/lib/chat-model.d.ts +148 -8
  7. package/lib/chat-model.js +368 -79
  8. package/lib/completion/completion-provider.d.ts +3 -1
  9. package/lib/completion/completion-provider.js +1 -2
  10. package/lib/completion/index.d.ts +1 -0
  11. package/lib/components/clear-button.d.ts +1 -0
  12. package/lib/components/clear-button.js +3 -4
  13. package/lib/components/completion-status.d.ts +1 -0
  14. package/lib/components/completion-status.js +5 -4
  15. package/lib/components/index.d.ts +1 -0
  16. package/lib/components/model-select.d.ts +1 -0
  17. package/lib/components/model-select.js +62 -67
  18. package/lib/components/save-button.d.ts +3 -2
  19. package/lib/components/save-button.js +4 -5
  20. package/lib/components/stop-button.d.ts +1 -0
  21. package/lib/components/stop-button.js +3 -4
  22. package/lib/components/tool-select.d.ts +3 -1
  23. package/lib/components/tool-select.js +47 -60
  24. package/lib/components/usage-display.d.ts +4 -2
  25. package/lib/components/usage-display.js +50 -61
  26. package/lib/diff-manager.d.ts +3 -1
  27. package/lib/index.d.ts +3 -2
  28. package/lib/index.js +50 -59
  29. package/lib/models/settings-model.d.ts +3 -1
  30. package/lib/models/settings-model.js +1 -0
  31. package/lib/rendered-message-outputarea.d.ts +1 -0
  32. package/lib/tokens.d.ts +48 -597
  33. package/lib/tokens.js +2 -31
  34. package/lib/widgets/ai-settings.d.ts +3 -1
  35. package/lib/widgets/ai-settings.js +185 -344
  36. package/lib/widgets/main-area-chat.d.ts +3 -3
  37. package/lib/widgets/main-area-chat.js +2 -4
  38. package/lib/widgets/provider-config-dialog.d.ts +2 -1
  39. package/lib/widgets/provider-config-dialog.js +102 -167
  40. package/package.json +111 -258
  41. package/schema/settings-model.json +6 -0
  42. package/src/chat-commands/skills.ts +2 -2
  43. package/src/chat-model-handler.ts +10 -6
  44. package/src/chat-model.ts +488 -96
  45. package/src/completion/completion-provider.ts +6 -6
  46. package/src/components/clear-button.tsx +0 -2
  47. package/src/components/completion-status.tsx +2 -2
  48. package/src/components/model-select.tsx +1 -1
  49. package/src/components/save-button.tsx +3 -3
  50. package/src/components/stop-button.tsx +0 -2
  51. package/src/components/tool-select.tsx +10 -9
  52. package/src/components/usage-display.tsx +4 -2
  53. package/src/diff-manager.ts +4 -3
  54. package/src/index.ts +103 -107
  55. package/src/models/settings-model.ts +7 -6
  56. package/src/tokens.ts +54 -744
  57. package/src/widgets/ai-settings.tsx +40 -11
  58. package/src/widgets/main-area-chat.ts +5 -8
  59. package/src/widgets/provider-config-dialog.tsx +8 -8
  60. package/LICENSE +0 -30
  61. package/README.md +0 -49
  62. package/lib/agent.d.ts +0 -277
  63. package/lib/agent.js +0 -1116
  64. package/lib/icons.d.ts +0 -3
  65. package/lib/icons.js +0 -8
  66. package/lib/providers/built-in-providers.d.ts +0 -21
  67. package/lib/providers/built-in-providers.js +0 -233
  68. package/lib/providers/generated-context-windows.d.ts +0 -8
  69. package/lib/providers/generated-context-windows.js +0 -96
  70. package/lib/providers/model-info.d.ts +0 -3
  71. package/lib/providers/model-info.js +0 -58
  72. package/lib/providers/models.d.ts +0 -37
  73. package/lib/providers/models.js +0 -28
  74. package/lib/providers/provider-registry.d.ts +0 -49
  75. package/lib/providers/provider-registry.js +0 -72
  76. package/lib/providers/provider-tools.d.ts +0 -36
  77. package/lib/providers/provider-tools.js +0 -93
  78. package/lib/skills/index.d.ts +0 -4
  79. package/lib/skills/index.js +0 -7
  80. package/lib/skills/parse-skill.d.ts +0 -25
  81. package/lib/skills/parse-skill.js +0 -69
  82. package/lib/skills/skill-loader.d.ts +0 -25
  83. package/lib/skills/skill-loader.js +0 -133
  84. package/lib/skills/skill-registry.d.ts +0 -31
  85. package/lib/skills/skill-registry.js +0 -100
  86. package/lib/skills/types.d.ts +0 -29
  87. package/lib/skills/types.js +0 -5
  88. package/lib/tools/commands.d.ts +0 -11
  89. package/lib/tools/commands.js +0 -154
  90. package/lib/tools/skills.d.ts +0 -9
  91. package/lib/tools/skills.js +0 -73
  92. package/lib/tools/tool-registry.d.ts +0 -35
  93. package/lib/tools/tool-registry.js +0 -55
  94. package/lib/tools/web.d.ts +0 -8
  95. package/lib/tools/web.js +0 -196
  96. package/src/agent.ts +0 -1441
  97. package/src/icons.ts +0 -11
  98. package/src/providers/built-in-providers.ts +0 -241
  99. package/src/providers/generated-context-windows.ts +0 -102
  100. package/src/providers/model-info.ts +0 -88
  101. package/src/providers/models.ts +0 -76
  102. package/src/providers/provider-registry.ts +0 -88
  103. package/src/providers/provider-tools.ts +0 -179
  104. package/src/skills/index.ts +0 -14
  105. package/src/skills/parse-skill.ts +0 -91
  106. package/src/skills/skill-loader.ts +0 -175
  107. package/src/skills/skill-registry.ts +0 -137
  108. package/src/skills/types.ts +0 -37
  109. package/src/tools/commands.ts +0 -210
  110. package/src/tools/skills.ts +0 -84
  111. package/src/tools/tool-registry.ts +0 -63
  112. package/src/tools/web.ts +0 -238
  113. package/src/types.d.ts +0 -4
  114. 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,334 +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.showTokenUsage, onChange: e => handleConfigUpdate({
486
- showTokenUsage: e.target.checked
487
- }), color: "primary" }), label: React.createElement(Box, null,
488
- React.createElement(Typography, { variant: "body1" }, trans.__('Show Token Usage')),
489
- React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Display token usage information in the chat toolbar'))) }),
490
- React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.showContextUsage, onChange: e => handleConfigUpdate({
491
- showContextUsage: e.target.checked
492
- }), color: "primary" }), label: React.createElement(Box, null,
493
- React.createElement(Typography, { variant: "body1" }, trans.__('Show Context Usage')),
494
- React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Display estimated context usage in the chat toolbar'))) }),
495
- React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.showCellDiff, onChange: e => handleConfigUpdate({
496
- showCellDiff: e.target.checked
497
- }), color: "primary" }), label: React.createElement(Box, null,
498
- React.createElement(Typography, { variant: "body1" }, trans.__('Show Cell Diff')),
499
- React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Show diff view when AI modifies cell content'))) }),
500
- config.showCellDiff && (React.createElement(FormControl, { sx: { ml: 4 } },
501
- React.createElement(InputLabel, null, trans.__('Diff Display Mode')),
502
- React.createElement(Select, { value: config.diffDisplayMode, label: trans.__('Diff Display Mode'), onChange: e => handleConfigUpdate({
503
- diffDisplayMode: e.target.value
504
- }) },
505
- React.createElement(MenuItem, { value: "split" }, trans.__('Split View')),
506
- React.createElement(MenuItem, { value: "unified" }, trans.__('Unified View'))))),
507
- React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.showFileDiff, onChange: e => handleConfigUpdate({
508
- showFileDiff: e.target.checked
509
- }), color: "primary" }), label: React.createElement(Box, null,
510
- React.createElement(Typography, { variant: "body1" }, trans.__('Show File Diff')),
511
- React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Show diff view when AI modifies file content'))) }),
512
- React.createElement(TextField, { fullWidth: true, label: trans.__('Chat Backup Directory'), value: config.chatBackupDirectory, onChange: e => handleConfigUpdate({
513
- chatBackupDirectory: e.target.value
514
- }), helperText: trans.__('Directory where chat history backups are saved (relative to the server root)') }),
515
- React.createElement(Divider, { sx: { my: 1 } }),
516
- 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') }),
517
- 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') }),
518
- React.createElement(Divider, { sx: { my: 2 } }),
519
- React.createElement(Box, null,
520
- React.createElement(Typography, { variant: "body1", gutterBottom: true, sx: {
521
- display: 'inline-flex',
522
- alignItems: 'center',
523
- gap: 1
524
- } },
525
- trans.__('Skills Paths'),
526
- 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.') },
527
- React.createElement(InfoOutlined, { sx: { fontSize: 16 } }))),
528
- 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: () => {
529
- const newPaths = [...config.skillsPaths];
530
- newPaths.splice(index, 1);
531
- handleConfigUpdate({ skillsPaths: newPaths });
532
- }, size: "small" },
533
- React.createElement(Delete, null)) },
534
- React.createElement(ListItemText, { primary: skillPath }))))),
535
- React.createElement(TextField, { fullWidth: true, label: trans.__('Add Skills Path'), placeholder: trans.__('e.g., .claude/skills'), onKeyDown: e => {
536
- if (e.key === 'Enter') {
537
- const value = e.target.value.trim();
538
- if (value &&
539
- !(config.skillsPaths ?? []).includes(value)) {
540
- const newPaths = [
541
- ...(config.skillsPaths ?? []),
542
- value
543
- ];
544
- handleConfigUpdate({ skillsPaths: newPaths });
545
- e.target.value = '';
546
- }
547
- }
548
- }, helperText: trans.__('Press Enter to add a path. Defaults: .agents/skills, _agents/skills') })),
549
- React.createElement(Divider, { sx: { my: 2 } }),
550
- React.createElement(Box, null,
551
- React.createElement(Typography, { variant: "body1", gutterBottom: true }, trans.__('Commands Requiring Approval')),
552
- React.createElement(Typography, { variant: "caption", color: "text.secondary", gutterBottom: true, sx: { display: 'block' } }, trans.__('Commands that require user approval before AI can execute them')),
553
- 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: () => {
554
- const newCommands = [
555
- ...config.commandsRequiringApproval
556
- ];
557
- newCommands.splice(index, 1);
558
- handleConfigUpdate({
559
- commandsRequiringApproval: newCommands
560
- });
561
- }, size: "small" },
562
- React.createElement(Delete, null)) },
563
- React.createElement(ListItemText, { primary: command }))))),
564
- React.createElement(TextField, { fullWidth: true, label: trans.__('Add New Command'), placeholder: trans.__('e.g., notebook:run-cell'), onKeyDown: e => {
565
- if (e.key === 'Enter') {
566
- const value = e.target.value.trim();
567
- if (value &&
568
- !config.commandsRequiringApproval.includes(value)) {
569
- const newCommands = [
570
- ...config.commandsRequiringApproval,
571
- value
572
- ];
573
- handleConfigUpdate({
574
- commandsRequiringApproval: newCommands
575
- });
576
- e.target.value = '';
577
- }
578
- }
579
- }, helperText: trans.__('Press Enter to add a command. Common commands: notebook:run-cell, console:execute, fileeditor:run-code') })),
580
- React.createElement(Divider, { sx: { my: 2 } }),
581
- React.createElement(Box, null,
582
- React.createElement(Typography, { variant: "body1", gutterBottom: true }, trans.__('Commands Auto-Rendering MIME Bundles')),
583
- 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')),
584
- 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: () => {
585
- const newCommands = [
586
- ...(config.commandsAutoRenderMimeBundles ??
587
- [])
588
- ];
589
- newCommands.splice(index, 1);
590
- handleConfigUpdate({
591
- commandsAutoRenderMimeBundles: newCommands
592
- });
593
- }, size: "small" },
594
- React.createElement(Delete, null)) },
595
- React.createElement(ListItemText, { primary: command }))))),
596
- React.createElement(TextField, { fullWidth: true, label: trans.__('Add Auto-Render Command'), placeholder: trans.__('e.g., jupyterlab-ai-commands:execute-in-kernel'), onKeyDown: e => {
597
- if (e.key === 'Enter') {
598
- const value = e.target.value.trim();
599
- const existingCommands = config.commandsAutoRenderMimeBundles ?? [];
600
- if (value && !existingCommands.includes(value)) {
601
- const newCommands = [...existingCommands, value];
602
- handleConfigUpdate({
603
- commandsAutoRenderMimeBundles: newCommands
604
- });
605
- e.target.value = '';
606
- }
607
- }
608
- }, helperText: trans.__('Press Enter to add a command. Default: jupyterlab-ai-commands:execute-in-kernel') })),
609
- React.createElement(Divider, { sx: { my: 2 } }),
610
- React.createElement(Box, null,
611
- React.createElement(Typography, { variant: "body1", gutterBottom: true }, trans.__('Trusted MIME Types for Auto-Render')),
612
- 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')),
613
- 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: () => {
614
- const newMimeTypes = [
615
- ...(config.trustedMimeTypesForAutoRender ??
616
- [])
617
- ];
618
- newMimeTypes.splice(index, 1);
619
- handleConfigUpdate({
620
- trustedMimeTypesForAutoRender: newMimeTypes
621
- });
622
- }, size: "small" },
623
- React.createElement(Delete, null)) },
624
- React.createElement(ListItemText, { primary: mimeType }))))),
625
- React.createElement(TextField, { fullWidth: true, label: trans.__('Add Trusted MIME Type'), placeholder: trans.__('e.g., text/html'), onKeyDown: e => {
626
- if (e.key === 'Enter') {
627
- const value = e.target.value.trim();
628
- const existingMimeTypes = config.trustedMimeTypesForAutoRender ?? [];
629
- if (value && !existingMimeTypes.includes(value)) {
630
- const newMimeTypes = [...existingMimeTypes, value];
631
- handleConfigUpdate({
632
- trustedMimeTypesForAutoRender: newMimeTypes
633
- });
634
- e.target.value = '';
635
- }
636
- }
637
- }, helperText: trans.__('Press Enter to add a MIME type. Default: text/html') })))))),
638
- activeTab === 2 && (React.createElement(Card, { elevation: 2 },
639
- React.createElement(CardContent, null,
640
- React.createElement(Box, { sx: {
641
- display: 'flex',
642
- alignItems: 'center',
643
- justifyContent: 'space-between',
644
- mb: 2
645
- } },
646
- React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
647
- React.createElement(Cable, { color: "primary" }),
648
- React.createElement(Typography, { variant: "h6", component: "h2" }, trans.__('Remote MCP Servers'))),
649
- React.createElement(Button, { variant: "contained", startIcon: React.createElement(Add, null), onClick: openAddMCPDialog, size: "small" }, trans.__('Add Server'))),
650
- 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.")),
651
- 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" },
652
- React.createElement(MoreVert, null)) },
653
- 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: {
654
521
  display: 'flex',
655
522
  alignItems: 'center',
656
- gap: 1
657
- } },
658
- React.createElement(Typography, { variant: "body1" }, server.name),
659
- server.enabled &&
660
- agentManagerFactory?.isMCPServerConnected(server.name) && (React.createElement(CheckCircleOutline, { sx: { color: 'success.main', fontSize: 16 } })),
661
- server.enabled &&
662
- !agentManagerFactory?.isMCPServerConnected(server.name) && (React.createElement(ErrorOutline, { sx: { color: 'error.main', fontSize: 16 } })),
663
- React.createElement(Switch, { checked: server.enabled, onChange: e => model.updateMCPServer(server.id, {
664
- enabled: e.target.checked
665
- }), size: "small", color: "primary" })), secondary: React.createElement(Box, null,
666
- React.createElement(Typography, { variant: "body2", color: "text.secondary" }, server.url),
667
- server.enabled && agentManagerFactory && (React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Status: %1', agentManagerFactory.isMCPServerConnected(server.name)
668
- ? trans.__('Connected')
669
- : trans.__('Connection failed'))))) }))))))))),
670
- React.createElement(ProviderConfigDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSave: editingProvider ? handleEditProvider : handleAddProvider, initialConfig: editingProvider, mode: editingProvider ? 'edit' : 'add', providerRegistry: providerRegistry, handleSecretField: handleSecretField, trans: trans }),
671
- React.createElement(Menu, { anchorEl: menuAnchor, open: Boolean(menuAnchor), onClose: handleMenuClose },
672
- React.createElement(MenuItem, { onClick: () => {
673
- const provider = config.providers.find(p => p.id === menuProviderId);
674
- if (provider) {
675
- openEditDialog(provider);
676
- }
677
- } },
678
- React.createElement(Edit, { sx: { mr: 1 } }),
679
- trans.__('Edit')),
680
- React.createElement(MenuItem, { onClick: () => handleDeleteProvider(menuProviderId), sx: { color: 'error.main' } },
681
- React.createElement(Delete, { sx: { mr: 1 } }),
682
- trans.__('Delete'))),
683
- React.createElement(MCPServerDialog, { open: mcpDialogOpen, onClose: () => setMcpDialogOpen(false), onSave: editingMCPServer ? handleEditMCPServer : handleAddMCPServer, initialConfig: editingMCPServer, mode: editingMCPServer ? 'edit' : 'add', trans: trans }),
684
- React.createElement(Menu, { anchorEl: mcpMenuAnchor, open: Boolean(mcpMenuAnchor), onClose: handleMCPMenuClose },
685
- React.createElement(MenuItem, { onClick: () => {
686
- const server = config.mcpServers.find(s => s.id === mcpMenuServerId);
687
- if (server) {
688
- openEditMCPDialog(server);
689
- }
690
- } },
691
- React.createElement(Edit, { sx: { mr: 1 } }),
692
- trans.__('Edit')),
693
- React.createElement(MenuItem, { onClick: () => handleDeleteMCPServer(mcpMenuServerId), sx: { color: 'error.main' } },
694
- React.createElement(Delete, { sx: { mr: 1 } }),
695
- 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')] })] })] }) }));
696
546
  };
697
547
  /**
698
548
  * Dialog component for adding/editing MCP server configurations
@@ -742,16 +592,7 @@ const MCPServerDialog = ({ open, onClose, onSave, initialConfig, mode, trans })
742
592
  }
743
593
  };
744
594
  const canSave = name.trim() && url.trim() && _isValidUrl(url.trim());
745
- return (React.createElement(Dialog, { open: open, onClose: onClose, maxWidth: "sm", fullWidth: true },
746
- React.createElement(DialogTitle, null, mode === 'add'
747
- ? trans.__('Add MCP Server')
748
- : trans.__('Edit MCP Server')),
749
- React.createElement(DialogContent, null,
750
- React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2, pt: 1 } },
751
- 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') }),
752
- 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())) }),
753
- React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: enabled, onChange: e => setEnabled(e.target.checked), color: "primary" }), label: trans.__('Enable this server') }))),
754
- React.createElement(DialogActions, null,
755
- React.createElement(Button, { onClick: onClose }, trans.__('Cancel')),
756
- 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') })] })] }));
757
598
  };