@jupyternaut/persona 0.0.0 → 0.20.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 (49) hide show
  1. package/lib/chat-commands/mention.d.ts +9 -0
  2. package/lib/chat-commands/mention.js +30 -0
  3. package/lib/completion/completion-provider.d.ts +86 -0
  4. package/lib/completion/completion-provider.js +246 -0
  5. package/lib/completion/index.d.ts +2 -0
  6. package/lib/completion/index.js +1 -0
  7. package/lib/components/completion-status.d.ts +26 -0
  8. package/lib/components/completion-status.js +52 -0
  9. package/lib/components/index.d.ts +2 -0
  10. package/lib/components/index.js +1 -0
  11. package/lib/diff-manager.d.ts +25 -0
  12. package/lib/diff-manager.js +60 -0
  13. package/lib/index.d.ts +8 -0
  14. package/lib/index.js +522 -0
  15. package/lib/models/settings-model.d.ts +36 -0
  16. package/lib/models/settings-model.js +356 -0
  17. package/lib/persona-registry.d.ts +15 -0
  18. package/lib/persona-registry.js +29 -0
  19. package/lib/persona.d.ts +66 -0
  20. package/lib/persona.js +414 -0
  21. package/lib/process-attachments.d.ts +5 -0
  22. package/lib/process-attachments.js +287 -0
  23. package/lib/tokens.d.ts +101 -0
  24. package/lib/tokens.js +20 -0
  25. package/lib/widgets/ai-settings.d.ts +54 -0
  26. package/lib/widgets/ai-settings.js +572 -0
  27. package/lib/widgets/provider-config-dialog.d.ts +16 -0
  28. package/lib/widgets/provider-config-dialog.js +384 -0
  29. package/package.json +111 -7
  30. package/schema/settings-model.json +287 -0
  31. package/src/chat-commands/mention.tsx +46 -0
  32. package/src/completion/completion-provider.ts +350 -0
  33. package/src/completion/index.ts +1 -0
  34. package/src/components/completion-status.tsx +93 -0
  35. package/src/components/index.ts +1 -0
  36. package/src/diff-manager.ts +81 -0
  37. package/src/index.ts +710 -0
  38. package/src/models/settings-model.ts +415 -0
  39. package/src/persona-registry.ts +46 -0
  40. package/src/persona.ts +610 -0
  41. package/src/process-attachments.ts +369 -0
  42. package/src/tokens.ts +121 -0
  43. package/src/widgets/ai-settings.tsx +1308 -0
  44. package/src/widgets/provider-config-dialog.tsx +997 -0
  45. package/style/base.css +14 -0
  46. package/style/index.css +1 -0
  47. package/style/index.js +1 -0
  48. package/README.md +0 -3
  49. package/index.js +0 -1
@@ -0,0 +1,384 @@
1
+ import { getProviderModelInfo } from '@jupyternaut/agent';
2
+ import ExpandMore from '@mui/icons-material/ExpandMore';
3
+ import Delete from '@mui/icons-material/Delete';
4
+ import Visibility from '@mui/icons-material/Visibility';
5
+ import VisibilityOff from '@mui/icons-material/VisibilityOff';
6
+ import { Accordion, AccordionDetails, AccordionSummary, Autocomplete, Box, Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, FormControl, FormControlLabel, IconButton, InputAdornment, InputLabel, List, ListItem, ListItemText, MenuItem, Select, Slider, Switch, TextField, Typography } from '@mui/material';
7
+ import React from 'react';
8
+ /**
9
+ * Default parameter values for provider configuration
10
+ */
11
+ const DEFAULT_TEMPERATURE = 0.7;
12
+ const DEFAULT_MAX_TURNS = 25;
13
+ const DOMAIN_FIELD_MAP = {
14
+ 'webSearch.allowedDomains': {
15
+ section: 'webSearch',
16
+ key: 'allowedDomains'
17
+ },
18
+ 'webSearch.blockedDomains': {
19
+ section: 'webSearch',
20
+ key: 'blockedDomains'
21
+ },
22
+ 'webFetch.allowedDomains': {
23
+ section: 'webFetch',
24
+ key: 'allowedDomains'
25
+ },
26
+ 'webFetch.blockedDomains': {
27
+ section: 'webFetch',
28
+ key: 'blockedDomains'
29
+ }
30
+ };
31
+ function createEmptyDomainInputs() {
32
+ return {
33
+ 'webSearch.allowedDomains': '',
34
+ 'webSearch.blockedDomains': '',
35
+ 'webFetch.allowedDomains': '',
36
+ 'webFetch.blockedDomains': ''
37
+ };
38
+ }
39
+ function toRecord(value) {
40
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
41
+ return value;
42
+ }
43
+ return {};
44
+ }
45
+ function toStringArray(value) {
46
+ if (!Array.isArray(value)) {
47
+ return [];
48
+ }
49
+ return value.filter((item) => typeof item === 'string');
50
+ }
51
+ function sanitizeCustomSettingsForProvider(customSettings, capabilities) {
52
+ const result = { ...customSettings };
53
+ const webSearch = toRecord(customSettings.webSearch);
54
+ const webFetch = toRecord(customSettings.webFetch);
55
+ const supportsWebSearch = !!capabilities?.webSearch;
56
+ const supportsWebFetch = !!capabilities?.webFetch;
57
+ if (supportsWebSearch && webSearch.enabled === true) {
58
+ result.webSearch = webSearch;
59
+ }
60
+ else {
61
+ delete result.webSearch;
62
+ }
63
+ if (supportsWebFetch && webFetch.enabled === true) {
64
+ result.webFetch = webFetch;
65
+ }
66
+ else {
67
+ delete result.webFetch;
68
+ }
69
+ return result;
70
+ }
71
+ export const ProviderConfigDialog = ({ open, onClose, onSave, initialConfig, mode, providerRegistry, handleSecretField, trans }) => {
72
+ const [name, setName] = React.useState(initialConfig?.name || '');
73
+ const [provider, setProvider] = React.useState(initialConfig?.provider || 'anthropic');
74
+ const [model, setModel] = React.useState(initialConfig?.model || '');
75
+ const [apiKey, setApiKey] = React.useState(initialConfig?.apiKey || '');
76
+ const [baseURL, setBaseURL] = React.useState(initialConfig?.baseURL || '');
77
+ const [showApiKey, setShowApiKey] = React.useState(false);
78
+ const [customSettings, setCustomSettings] = React.useState(initialConfig?.customSettings || {});
79
+ const [domainInputs, setDomainInputs] = React.useState(createEmptyDomainInputs());
80
+ const [parameters, setParameters] = React.useState(initialConfig?.parameters || {});
81
+ const [expandedAdvanced, setExpandedAdvanced] = React.useState(false);
82
+ const selectedProviderInfo = React.useMemo(() => providerRegistry.getProviderInfo(provider), [providerRegistry, provider]);
83
+ const providerToolCapabilities = selectedProviderInfo?.providerToolCapabilities;
84
+ const selectedModelInfo = React.useMemo(() => getProviderModelInfo(selectedProviderInfo, model), [selectedProviderInfo, model]);
85
+ const webSearchImplementation = providerToolCapabilities?.webSearch?.implementation;
86
+ const supportsWebSearch = !!providerToolCapabilities?.webSearch;
87
+ const supportsWebFetch = !!providerToolCapabilities?.webFetch;
88
+ const webSearchSettings = React.useMemo(() => toRecord(customSettings.webSearch), [customSettings]);
89
+ const webFetchSettings = React.useMemo(() => toRecord(customSettings.webFetch), [customSettings]);
90
+ // Get provider options from registry
91
+ const providerOptions = React.useMemo(() => {
92
+ const providers = providerRegistry.providers;
93
+ return Object.keys(providers).map(id => {
94
+ const info = providers[id];
95
+ return {
96
+ value: id,
97
+ label: info.name,
98
+ models: info.defaultModels,
99
+ apiKeyRequirement: info.apiKeyRequirement,
100
+ supportsBaseURL: info.supportsBaseURL,
101
+ description: info.description,
102
+ baseUrls: info.baseUrls
103
+ };
104
+ });
105
+ }, [providerRegistry]);
106
+ const selectedProvider = providerOptions.find(p => p.value === provider);
107
+ React.useEffect(() => {
108
+ if (open) {
109
+ // Reset form when dialog opens
110
+ const initialProvider = initialConfig?.provider || 'anthropic';
111
+ const initialProviderInfo = providerRegistry.getProviderInfo(initialProvider);
112
+ setName(initialConfig?.name || '');
113
+ setProvider(initialProvider);
114
+ setModel(initialConfig?.model || initialProviderInfo?.defaultModels[0] || '');
115
+ setApiKey(initialConfig?.apiKey || '');
116
+ setBaseURL(initialConfig?.baseURL || '');
117
+ setParameters(initialConfig?.parameters || {});
118
+ setCustomSettings(initialConfig?.customSettings || {});
119
+ setDomainInputs(createEmptyDomainInputs());
120
+ setShowApiKey(false);
121
+ setExpandedAdvanced(false);
122
+ }
123
+ else {
124
+ // Reset expanded state when dialog closes
125
+ setDomainInputs(createEmptyDomainInputs());
126
+ setExpandedAdvanced(false);
127
+ }
128
+ }, [open, initialConfig, providerRegistry]);
129
+ const handleRef = React.useCallback((node) => {
130
+ if (open && node) {
131
+ handleSecretField(node, provider, 'apiKey');
132
+ }
133
+ }, [provider, handleSecretField, open]);
134
+ const handleProviderChange = React.useCallback((newProvider) => {
135
+ const newProviderInfo = providerRegistry.getProviderInfo(newProvider);
136
+ setProvider(newProvider);
137
+ setModel(newProviderInfo?.defaultModels[0] || '');
138
+ }, [providerRegistry]);
139
+ const updateCustomSetting = React.useCallback((section, key, value) => {
140
+ setCustomSettings(prev => {
141
+ const next = { ...prev };
142
+ const sectionSettings = { ...toRecord(next[section]) };
143
+ const shouldDelete = value === undefined ||
144
+ value === null ||
145
+ value === '' ||
146
+ (Array.isArray(value) && value.length === 0);
147
+ if (shouldDelete) {
148
+ delete sectionSettings[key];
149
+ }
150
+ else {
151
+ sectionSettings[key] = value;
152
+ }
153
+ if (Object.keys(sectionSettings).length === 0) {
154
+ delete next[section];
155
+ }
156
+ else {
157
+ next[section] = sectionSettings;
158
+ }
159
+ return next;
160
+ });
161
+ }, []);
162
+ const addDomainValue = React.useCallback((fieldId) => {
163
+ const valueToAdd = domainInputs[fieldId].trim();
164
+ if (!valueToAdd) {
165
+ return;
166
+ }
167
+ const { section, key } = DOMAIN_FIELD_MAP[fieldId];
168
+ const currentValues = toStringArray(toRecord(customSettings[section])[key]);
169
+ if (currentValues.includes(valueToAdd)) {
170
+ setDomainInputs(prev => ({
171
+ ...prev,
172
+ [fieldId]: ''
173
+ }));
174
+ return;
175
+ }
176
+ const nextValues = [...currentValues, valueToAdd];
177
+ updateCustomSetting(section, key, nextValues);
178
+ setDomainInputs(prev => ({
179
+ ...prev,
180
+ [fieldId]: ''
181
+ }));
182
+ }, [customSettings, domainInputs, updateCustomSetting]);
183
+ const removeDomainValue = React.useCallback((fieldId, valueToRemove) => {
184
+ const { section, key } = DOMAIN_FIELD_MAP[fieldId];
185
+ const currentValues = toStringArray(toRecord(customSettings[section])[key]);
186
+ const nextValues = currentValues.filter(value => value !== valueToRemove);
187
+ updateCustomSetting(section, key, nextValues.length > 0 ? nextValues : undefined);
188
+ }, [customSettings, updateCustomSetting]);
189
+ const renderDomainList = React.useCallback((fieldId, label, placeholder, values) => {
190
+ const domainValues = toStringArray(values);
191
+ return (React.createElement(Box, null,
192
+ React.createElement(Typography, { variant: "body2", gutterBottom: true }, label),
193
+ React.createElement(List, { dense: true, sx: {
194
+ mb: 1,
195
+ maxHeight: 160,
196
+ overflow: 'auto',
197
+ border: 1,
198
+ borderColor: 'divider',
199
+ borderRadius: 1
200
+ } }, domainValues.length === 0 ? (React.createElement(ListItem, null,
201
+ React.createElement(ListItemText, { secondary: trans.__('No domains added.'), slotProps: {
202
+ secondary: {
203
+ color: 'text.secondary'
204
+ }
205
+ } }))) : (domainValues.map(value => (React.createElement(ListItem, { key: value, secondaryAction: React.createElement(IconButton, { onClick: () => removeDomainValue(fieldId, value), size: "small" },
206
+ React.createElement(Delete, { fontSize: "small" })) },
207
+ React.createElement(ListItemText, { primary: value })))))),
208
+ React.createElement(TextField, { fullWidth: true, size: "small", label: trans.__('Add Domain'), value: domainInputs[fieldId], onChange: e => setDomainInputs(prev => ({
209
+ ...prev,
210
+ [fieldId]: e.target.value
211
+ })), onKeyDown: e => {
212
+ if (e.key === 'Enter') {
213
+ e.preventDefault();
214
+ addDomainValue(fieldId);
215
+ }
216
+ }, placeholder: placeholder, helperText: trans.__('Press Enter to add one domain.') })));
217
+ }, [addDomainValue, domainInputs, removeDomainValue, trans]);
218
+ const handleSave = () => {
219
+ if (!name.trim() || !provider || !model) {
220
+ return;
221
+ }
222
+ // Only include parameters if at least one is set
223
+ const hasParameters = Object.keys(parameters).some(key => parameters[key] !== undefined);
224
+ const sanitizedCustomSettings = sanitizeCustomSettingsForProvider(customSettings, providerToolCapabilities);
225
+ const config = {
226
+ name: name.trim(),
227
+ provider: provider,
228
+ model,
229
+ ...(apiKey && { apiKey }),
230
+ ...(baseURL && { baseURL }),
231
+ ...(hasParameters && { parameters }),
232
+ ...(Object.keys(sanitizedCustomSettings).length > 0 && {
233
+ customSettings: sanitizedCustomSettings
234
+ })
235
+ };
236
+ onSave(config);
237
+ onClose();
238
+ };
239
+ const isValid = name.trim() &&
240
+ provider &&
241
+ model &&
242
+ (selectedProvider?.apiKeyRequirement !== 'required' || apiKey);
243
+ return (React.createElement(Dialog, { open: open, onClose: onClose, maxWidth: "md", fullWidth: true },
244
+ React.createElement(DialogTitle, null, mode === 'add'
245
+ ? trans.__('Add New Provider')
246
+ : trans.__('Edit Provider')),
247
+ React.createElement(DialogContent, null,
248
+ React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2, pt: 1 } },
249
+ React.createElement(TextField, { fullWidth: true, label: trans.__('Provider Name'), value: name, onChange: e => setName(e.target.value), placeholder: trans.__('e.g., My Anthropic Config, Work Provider'), helperText: trans.__('A friendly name to identify this provider configuration'), required: true }),
250
+ React.createElement(FormControl, { fullWidth: true, required: true },
251
+ React.createElement(InputLabel, null, trans.__('Provider Type')),
252
+ React.createElement(Select, { value: provider, label: trans.__('Provider Type'), onChange: e => handleProviderChange(e.target.value) }, providerOptions.map(option => (React.createElement(MenuItem, { key: option.value, value: option.value },
253
+ React.createElement(Box, null,
254
+ React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
255
+ option.label,
256
+ option.apiKeyRequirement === 'required' && (React.createElement(Chip, { size: "small", label: trans.__('API Key'), color: "default", variant: "outlined" }))),
257
+ option.description && (React.createElement(Typography, { variant: "caption", color: "text.secondary" }, option.description)))))))),
258
+ React.createElement(Autocomplete, { freeSolo: true, fullWidth: true, options: selectedProvider?.models ?? [], value: model, onChange: (_, value) => {
259
+ setModel(typeof value === 'string' ? value : '');
260
+ }, inputValue: model, onInputChange: (_, value) => {
261
+ setModel(value);
262
+ }, renderInput: params => (React.createElement(TextField, { ...params, fullWidth: true, label: trans.__('Model'), placeholder: trans.__('Select or type a model ID'), required: true, helperText: trans.__('Choose from the list or enter a custom model ID') })), clearOnBlur: false }),
263
+ selectedProvider &&
264
+ selectedProvider?.apiKeyRequirement !== 'none' && (React.createElement(TextField, { fullWidth: true, inputRef: handleRef, label: selectedProvider?.apiKeyRequirement === 'required'
265
+ ? trans.__('API Key')
266
+ : trans.__('API Key (Optional)'), type: showApiKey ? 'text' : 'password', value: apiKey, onChange: e => setApiKey(e.target.value), placeholder: trans.__('Enter your API key...'), required: selectedProvider?.apiKeyRequirement === 'required', InputProps: {
267
+ endAdornment: (React.createElement(InputAdornment, { position: "end" },
268
+ React.createElement(IconButton, { onClick: () => setShowApiKey(!showApiKey), edge: "end" }, showApiKey ? React.createElement(VisibilityOff, null) : React.createElement(Visibility, null))))
269
+ } })),
270
+ selectedProvider?.supportsBaseURL && (React.createElement(Autocomplete, { freeSolo: true, fullWidth: true, options: (selectedProvider.baseUrls ?? []).map(option => option.url), value: baseURL || '', onChange: (_, value) => {
271
+ if (value && typeof value === 'string') {
272
+ setBaseURL(value);
273
+ }
274
+ }, inputValue: baseURL || '', renderOption: (props, option) => {
275
+ const urlOption = (selectedProvider.baseUrls ?? []).find(u => u.url === option);
276
+ return (React.createElement(Box, { component: "li", ...props, key: option },
277
+ React.createElement(Box, null,
278
+ React.createElement(Typography, { variant: "body2" }, option),
279
+ urlOption?.description && (React.createElement(Typography, { variant: "caption", color: "text.secondary" }, urlOption.description)))));
280
+ }, renderInput: params => (React.createElement(TextField, { ...params, fullWidth: true, label: trans.__('Base URL'), placeholder: "https://api.example.com/v1", onChange: e => setBaseURL(e.target.value) })), clearOnBlur: false })),
281
+ React.createElement(Accordion, { expanded: expandedAdvanced, onChange: (_, isExpanded) => setExpandedAdvanced(isExpanded), sx: {
282
+ mt: 2,
283
+ bgcolor: 'transparent',
284
+ boxShadow: 'none',
285
+ border: 1,
286
+ borderColor: 'divider',
287
+ borderRadius: 1
288
+ } },
289
+ React.createElement(AccordionSummary, { expandIcon: React.createElement(ExpandMore, null) },
290
+ React.createElement(Typography, { variant: "subtitle1", fontWeight: "medium" }, trans.__('Advanced Settings'))),
291
+ React.createElement(AccordionDetails, { sx: { bgcolor: 'transparent' } },
292
+ React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
293
+ React.createElement(Box, null,
294
+ React.createElement(Typography, { gutterBottom: true }, trans.__('Temperature: %1', parameters.temperature ?? trans.__('Default'))),
295
+ React.createElement(Slider, { value: parameters.temperature ?? DEFAULT_TEMPERATURE, onChange: (_, value) => setParameters({
296
+ ...parameters,
297
+ temperature: value
298
+ }), min: 0, max: 2, step: 0.1, valueLabelDisplay: "auto" }),
299
+ React.createElement(Typography, { variant: "caption", color: "text.secondary" }, trans.__('Temperature for the model (lower values are more deterministic)'))),
300
+ React.createElement(TextField, { fullWidth: true, label: trans.__('Max Tokens (Optional)'), type: "number", value: parameters.maxOutputTokens ?? '', onChange: e => setParameters({
301
+ ...parameters,
302
+ maxOutputTokens: e.target.value
303
+ ? Number(e.target.value)
304
+ : undefined
305
+ }), placeholder: trans.__('Leave empty for provider default'), helperText: trans.__('Maximum length of AI responses'), slotProps: { htmlInput: { min: 1 } } }),
306
+ React.createElement(TextField, { fullWidth: true, label: trans.__('Max Turns (Optional)'), type: "number", value: parameters.maxTurns ?? '', onChange: e => setParameters({
307
+ ...parameters,
308
+ maxTurns: e.target.value
309
+ ? Number(e.target.value)
310
+ : undefined
311
+ }), placeholder: trans.__('Default: %1', DEFAULT_MAX_TURNS), helperText: trans.__('Maximum number of tool execution turns'), slotProps: { htmlInput: { min: 1, max: 100 } } }),
312
+ React.createElement(TextField, { fullWidth: true, label: trans.__('Context Window (Optional)'), type: "number", value: parameters.contextWindow ?? '', onChange: e => setParameters({
313
+ ...parameters,
314
+ contextWindow: e.target.value
315
+ ? Number(e.target.value)
316
+ : undefined
317
+ }), placeholder: selectedModelInfo?.contextWindow !== undefined
318
+ ? trans.__('Default: %1', selectedModelInfo.contextWindow.toLocaleString())
319
+ : trans.__('e.g., 128000'), helperText: selectedModelInfo?.contextWindow !== undefined &&
320
+ parameters.contextWindow === undefined
321
+ ? trans.__('Using provider metadata default of %1 tokens for this model unless you override it here.', selectedModelInfo.contextWindow.toLocaleString())
322
+ : trans.__('Model context window size in tokens (used for context usage estimation)'), slotProps: { htmlInput: { min: 1 } } }),
323
+ React.createElement(Typography, { variant: "body2", color: "text.secondary", sx: { mt: 2, mb: 1 } }, trans.__('Completion Options')),
324
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: parameters.supportsFillInMiddle ?? false, onChange: e => setParameters({
325
+ ...parameters,
326
+ supportsFillInMiddle: e.target.checked
327
+ }) }), label: trans.__('Fill-in-the-middle support') }),
328
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: parameters.useFilterText ?? false, onChange: e => setParameters({
329
+ ...parameters,
330
+ useFilterText: e.target.checked
331
+ }) }), label: trans.__('Use filter text') }),
332
+ (supportsWebSearch || supportsWebFetch) && (React.createElement(React.Fragment, null,
333
+ React.createElement(Typography, { variant: "body2", color: "text.secondary", sx: { mt: 2, mb: 1 } }, trans.__('Provider Web Tools')),
334
+ supportsWebSearch && (React.createElement(React.Fragment, null,
335
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: webSearchSettings.enabled === true, onChange: e => updateCustomSetting('webSearch', 'enabled', e.target.checked) }), label: trans.__('Enable Web Search') }),
336
+ webSearchSettings.enabled === true && (React.createElement(Box, { sx: {
337
+ pl: 2,
338
+ borderLeft: 2,
339
+ borderColor: 'divider',
340
+ display: 'flex',
341
+ flexDirection: 'column',
342
+ gap: 1.5
343
+ } },
344
+ (webSearchImplementation === 'openai' ||
345
+ webSearchImplementation === 'anthropic') &&
346
+ renderDomainList('webSearch.allowedDomains', trans.__('Allowed Domains'), trans.__('example.com'), webSearchSettings.allowedDomains),
347
+ webSearchImplementation === 'openai' && (React.createElement(React.Fragment, null,
348
+ React.createElement(FormControl, { fullWidth: true },
349
+ React.createElement(InputLabel, null, trans.__('Search Context Size')),
350
+ React.createElement(Select, { value: webSearchSettings.searchContextSize ??
351
+ 'medium', label: trans.__('Search Context Size'), onChange: e => updateCustomSetting('webSearch', 'searchContextSize', e.target.value) },
352
+ React.createElement(MenuItem, { value: "low" }, trans.__('Low')),
353
+ React.createElement(MenuItem, { value: "medium" }, trans.__('Medium')),
354
+ React.createElement(MenuItem, { value: "high" }, trans.__('High')))),
355
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: webSearchSettings.externalWebAccess !==
356
+ false, onChange: e => updateCustomSetting('webSearch', 'externalWebAccess', e.target.checked) }), label: trans.__('Use External Web Access') }))),
357
+ webSearchImplementation === 'anthropic' && (React.createElement(React.Fragment, null,
358
+ React.createElement(TextField, { fullWidth: true, label: trans.__('Web Search Max Uses'), type: "number", value: webSearchSettings.maxUses ?? '', onChange: e => updateCustomSetting('webSearch', 'maxUses', e.target.value
359
+ ? Number(e.target.value)
360
+ : undefined), slotProps: { htmlInput: { min: 1 } } }),
361
+ renderDomainList('webSearch.blockedDomains', trans.__('Blocked Domains'), trans.__('spam.example.com'), webSearchSettings.blockedDomains))))))),
362
+ supportsWebFetch && (React.createElement(React.Fragment, null,
363
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: webFetchSettings.enabled === true, onChange: e => updateCustomSetting('webFetch', 'enabled', e.target.checked) }), label: trans.__('Enable Web Fetch') }),
364
+ webFetchSettings.enabled === true && (React.createElement(Box, { sx: {
365
+ pl: 2,
366
+ borderLeft: 2,
367
+ borderColor: 'divider',
368
+ display: 'flex',
369
+ flexDirection: 'column',
370
+ gap: 1.5
371
+ } },
372
+ React.createElement(TextField, { fullWidth: true, label: trans.__('Web Fetch Max Uses'), type: "number", value: webFetchSettings.maxUses ?? '', onChange: e => updateCustomSetting('webFetch', 'maxUses', e.target.value
373
+ ? Number(e.target.value)
374
+ : undefined), slotProps: { htmlInput: { min: 1 } } }),
375
+ React.createElement(TextField, { fullWidth: true, label: trans.__('Web Fetch Max Content Tokens'), type: "number", value: webFetchSettings.maxContentTokens ?? '', onChange: e => updateCustomSetting('webFetch', 'maxContentTokens', e.target.value
376
+ ? Number(e.target.value)
377
+ : undefined), slotProps: { htmlInput: { min: 1 } } }),
378
+ renderDomainList('webFetch.allowedDomains', trans.__('Allowed Domains'), trans.__('docs.example.com'), webFetchSettings.allowedDomains),
379
+ renderDomainList('webFetch.blockedDomains', trans.__('Blocked Domains'), trans.__('spam.example.com'), webFetchSettings.blockedDomains),
380
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: webFetchSettings.citationsEnabled === true, onChange: e => updateCustomSetting('webFetch', 'citationsEnabled', e.target.checked) }), label: trans.__('Enable Citations') })))))))))))),
381
+ React.createElement(DialogActions, null,
382
+ React.createElement(Button, { onClick: onClose }, trans.__('Cancel')),
383
+ React.createElement(Button, { onClick: handleSave, variant: "contained", disabled: !isValid }, mode === 'add' ? trans.__('Add Provider') : trans.__('Save Changes')))));
384
+ };
package/package.json CHANGED
@@ -1,10 +1,114 @@
1
1
  {
2
2
  "name": "@jupyternaut/persona",
3
- "version": "0.0.0",
4
- "description": "Placeholder",
5
- "main": "index.js",
6
- "scripts": {},
7
- "keywords": [],
8
- "author": "",
9
- "license": "BSD-3-Clause"
3
+ "version": "0.20.0",
4
+ "description": "AI code completions and chat for JupyterLite",
5
+ "keywords": [
6
+ "jupyter",
7
+ "jupyterlab",
8
+ "jupyterlab-extension"
9
+ ],
10
+ "homepage": "https://github.com/jupyterlite/ai",
11
+ "bugs": {
12
+ "url": "https://github.com/jupyterlite/ai/issues"
13
+ },
14
+ "license": "BSD-3-Clause",
15
+ "author": "JupyterLite Contributors",
16
+ "files": [
17
+ "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf,md}",
18
+ "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
19
+ "src/**/*.{ts,tsx}",
20
+ "schema/*.json"
21
+ ],
22
+ "main": "lib/index.js",
23
+ "types": "lib/index.d.ts",
24
+ "style": "style/index.css",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/jupyterlite/ai.git",
28
+ "directory": "packages/ai"
29
+ },
30
+ "scripts": {
31
+ "build": "echo 'Building @jupyternaut/persona' && jlpm build:lib && jlpm build:labextension:dev",
32
+ "build:dev": "echo 'Building @jupyternaut/persona' && jlpm build:lib && jlpm build:labextension:dev",
33
+ "build:prod": "echo 'Building @jupyternaut/persona' && jlpm clean && jlpm build:lib:prod && jlpm build:labextension",
34
+ "build:labextension": "jupyter-builder build .",
35
+ "build:labextension:dev": "jupyter-builder build --development True .",
36
+ "build:lib": "tsc --sourceMap",
37
+ "build:lib:prod": "tsc",
38
+ "clean": "jlpm clean:lib",
39
+ "clean:lib": "rimraf lib tsconfig.tsbuildinfo",
40
+ "install:extension": "jlpm build",
41
+ "watch": "run-p watch:src watch:labextension",
42
+ "watch:src": "tsc -w --sourceMap",
43
+ "watch:labextension": "jupyter-builder watch ."
44
+ },
45
+ "dependencies": {
46
+ "@jupyter/chat": "^0.25.0",
47
+ "@jupyter/ydoc": "^4.0.0",
48
+ "@jupyterlab/application": "^4.5.8",
49
+ "@jupyterlab/apputils": "^4.6.8",
50
+ "@jupyterlab/completer": "^4.5.8",
51
+ "@jupyterlab/coreutils": "^6.5.8",
52
+ "@jupyterlab/docmanager": "^4.5.8",
53
+ "@jupyterlab/docregistry": "^4.5.8",
54
+ "@jupyterlab/nbformat": "^4.5.8",
55
+ "@jupyterlab/notebook": "^4.5.8",
56
+ "@jupyterlab/rendermime": "^4.5.8",
57
+ "@jupyterlab/settingregistry": "^4.5.8",
58
+ "@jupyterlab/statusbar": "^4.5.8",
59
+ "@jupyterlab/translation": "^4.5.8",
60
+ "@jupyterlab/ui-components": "^4.5.8",
61
+ "@jupyternaut/agent": "^0.20.0",
62
+ "@lumino/commands": "^2.3.2",
63
+ "@lumino/coreutils": "^2.2.1",
64
+ "@lumino/disposable": "^2.1.4",
65
+ "@lumino/polling": "^2.1.4",
66
+ "@lumino/signaling": "^2.1.4",
67
+ "@mui/icons-material": "^7",
68
+ "@mui/material": "^7",
69
+ "ai": "^7.0.106",
70
+ "jupyter-mcp-manager": "^0.2.0",
71
+ "jupyter-secrets-manager": "^0.5.0",
72
+ "react": "^18.3.1"
73
+ },
74
+ "devDependencies": {
75
+ "@jupyter/builder": "^1.2.2",
76
+ "@jupyterlab/testutils": "^4.0.0",
77
+ "@types/json-schema": "^7.0.11",
78
+ "@types/node": "^24.3.0",
79
+ "@types/react": "^18.0.26",
80
+ "@types/react-addons-linked-state-mixin": "^0.14.22",
81
+ "css-loader": "^6.7.1",
82
+ "npm-run-all2": "^7.0.1",
83
+ "rimraf": "^5.0.1",
84
+ "source-map-loader": "^1.0.2",
85
+ "typescript": "~5.8.0"
86
+ },
87
+ "sideEffects": [
88
+ "style/*.css",
89
+ "style/index.js"
90
+ ],
91
+ "styleModule": "style/index.js",
92
+ "publishConfig": {
93
+ "access": "public"
94
+ },
95
+ "jupyterlab": {
96
+ "extension": true,
97
+ "outputDir": "../../python/jupyternaut-persona/jupyternaut_persona/labextension",
98
+ "schemaDir": "schema",
99
+ "sharedPackages": {
100
+ "@emotion/react": {
101
+ "bundled": true,
102
+ "singleton": true
103
+ },
104
+ "@jupyternaut/agent": {
105
+ "bundled": true,
106
+ "singleton": true
107
+ },
108
+ "jupyter-mcp-manager": {
109
+ "bundled": false,
110
+ "singleton": true
111
+ }
112
+ }
113
+ }
10
114
  }