@jupyternaut/persona 0.0.0 → 0.20.1

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 (55) hide show
  1. package/lib/chat-commands/index.d.ts +3 -0
  2. package/lib/chat-commands/index.js +2 -0
  3. package/lib/chat-commands/mention.d.ts +9 -0
  4. package/lib/chat-commands/mention.js +30 -0
  5. package/lib/chat-commands/skills.d.ts +25 -0
  6. package/lib/chat-commands/skills.js +73 -0
  7. package/lib/completion/completion-provider.d.ts +86 -0
  8. package/lib/completion/completion-provider.js +246 -0
  9. package/lib/completion/index.d.ts +2 -0
  10. package/lib/completion/index.js +1 -0
  11. package/lib/components/completion-status.d.ts +26 -0
  12. package/lib/components/completion-status.js +52 -0
  13. package/lib/components/index.d.ts +2 -0
  14. package/lib/components/index.js +1 -0
  15. package/lib/diff-manager.d.ts +25 -0
  16. package/lib/diff-manager.js +60 -0
  17. package/lib/index.d.ts +8 -0
  18. package/lib/index.js +593 -0
  19. package/lib/models/settings-model.d.ts +36 -0
  20. package/lib/models/settings-model.js +356 -0
  21. package/lib/persona-registry.d.ts +15 -0
  22. package/lib/persona-registry.js +29 -0
  23. package/lib/persona.d.ts +67 -0
  24. package/lib/persona.js +446 -0
  25. package/lib/process-attachments.d.ts +5 -0
  26. package/lib/process-attachments.js +287 -0
  27. package/lib/tokens.d.ts +105 -0
  28. package/lib/tokens.js +20 -0
  29. package/lib/widgets/ai-settings.d.ts +54 -0
  30. package/lib/widgets/ai-settings.js +572 -0
  31. package/lib/widgets/provider-config-dialog.d.ts +16 -0
  32. package/lib/widgets/provider-config-dialog.js +384 -0
  33. package/package.json +120 -7
  34. package/schema/settings-model.json +287 -0
  35. package/src/chat-commands/index.ts +2 -0
  36. package/src/chat-commands/mention.tsx +46 -0
  37. package/src/chat-commands/skills.ts +105 -0
  38. package/src/completion/completion-provider.ts +350 -0
  39. package/src/completion/index.ts +1 -0
  40. package/src/components/completion-status.tsx +93 -0
  41. package/src/components/index.ts +1 -0
  42. package/src/diff-manager.ts +81 -0
  43. package/src/index.ts +808 -0
  44. package/src/models/settings-model.ts +415 -0
  45. package/src/persona-registry.ts +46 -0
  46. package/src/persona.ts +637 -0
  47. package/src/process-attachments.ts +369 -0
  48. package/src/tokens.ts +125 -0
  49. package/src/widgets/ai-settings.tsx +1308 -0
  50. package/src/widgets/provider-config-dialog.tsx +997 -0
  51. package/style/base.css +14 -0
  52. package/style/index.css +1 -0
  53. package/style/index.js +1 -0
  54. package/README.md +0 -3
  55. package/index.js +0 -1
package/lib/index.js ADDED
@@ -0,0 +1,593 @@
1
+ import { IChatTracker, IChatCommandRegistry } from '@jupyter/chat';
2
+ import { ILayoutRestorer } from '@jupyterlab/application';
3
+ import { ICommandPalette, IThemeManager } from '@jupyterlab/apputils';
4
+ import { ICompletionProviderManager } from '@jupyterlab/completer';
5
+ import { IDocumentManager } from '@jupyterlab/docmanager';
6
+ import { ISettingRegistry } from '@jupyterlab/settingregistry';
7
+ import { IStatusBar } from '@jupyterlab/statusbar';
8
+ import { PathExt } from '@jupyterlab/coreutils';
9
+ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
10
+ import { IFormRendererRegistry, settingsIcon } from '@jupyterlab/ui-components';
11
+ import { anthropicProvider, createBrowserFetchTool, createDiscoverCommandsTool, createDiscoverSkillsTool, createExecuteCommandTool, createLoadSkillTool, genericProvider, googleProvider, loadSkillsFromPaths, mistralProvider, openaiProvider, AgentManagerFactory, IAgentManagerFactory, IAISettingsModel, IDiffManager, IProviderRegistry, IToolRegistry, ISkillRegistry, ProviderRegistry, SECRETS_NAMESPACE, SkillRegistry, ToolRegistry } from '@jupyternaut/agent';
12
+ import { DisposableSet } from '@lumino/disposable';
13
+ import { IComponentsRendererFactory } from 'jupyter-chat-components';
14
+ import { IMcpManager } from 'jupyter-mcp-manager';
15
+ import { ISecretsManager, SecretsManager } from 'jupyter-secrets-manager';
16
+ import { MentionCommandProvider, SkillsCommandProvider } from './chat-commands';
17
+ import { AICompletionProvider } from './completion';
18
+ import { CompletionStatusWidget } from './components';
19
+ import { DiffManager } from './diff-manager';
20
+ import { AISettingsModel } from './models/settings-model';
21
+ import { PersonaRegistry } from './persona-registry';
22
+ import { CommandIds, IPersonaRegistry, DEFAULT_PERSONA } from './tokens';
23
+ import { AISettingsWidget } from './widgets/ai-settings';
24
+ var Private;
25
+ (function (Private) {
26
+ let aiSecretsToken = null;
27
+ function setAISecretsToken(token) {
28
+ aiSecretsToken = token;
29
+ }
30
+ Private.setAISecretsToken = setAISecretsToken;
31
+ function createAISecretsAccess(secretsManager) {
32
+ return {
33
+ get isAvailable() {
34
+ return !!(aiSecretsToken && secretsManager);
35
+ },
36
+ async get(id) {
37
+ if (!aiSecretsToken || !secretsManager) {
38
+ return;
39
+ }
40
+ const secret = await secretsManager.get(aiSecretsToken, SECRETS_NAMESPACE, id);
41
+ return secret?.value;
42
+ },
43
+ async set(id, value) {
44
+ if (!aiSecretsToken || !secretsManager) {
45
+ return;
46
+ }
47
+ await secretsManager.set(aiSecretsToken, SECRETS_NAMESPACE, id, {
48
+ namespace: SECRETS_NAMESPACE,
49
+ id,
50
+ value
51
+ });
52
+ },
53
+ async attach(id, input, callback) {
54
+ if (!aiSecretsToken || !secretsManager) {
55
+ return;
56
+ }
57
+ await secretsManager.attach(aiSecretsToken, SECRETS_NAMESPACE, id, input, callback);
58
+ }
59
+ };
60
+ }
61
+ Private.createAISecretsAccess = createAISecretsAccess;
62
+ })(Private || (Private = {}));
63
+ /**
64
+ * Provider registry plugin
65
+ */
66
+ const providerRegistryPlugin = {
67
+ id: '@jupyternaut/persona:provider-registry',
68
+ description: 'AI provider registry',
69
+ autoStart: true,
70
+ provides: IProviderRegistry,
71
+ activate: () => {
72
+ return new ProviderRegistry();
73
+ }
74
+ };
75
+ /**
76
+ * Anthropic provider plugin
77
+ */
78
+ const anthropicProviderPlugin = {
79
+ id: '@jupyternaut/persona:anthropic-provider',
80
+ description: 'Register Anthropic provider',
81
+ autoStart: true,
82
+ requires: [IProviderRegistry],
83
+ activate: (app, providerRegistry) => {
84
+ providerRegistry.registerProvider(anthropicProvider);
85
+ }
86
+ };
87
+ /**
88
+ * Google provider plugin
89
+ */
90
+ const googleProviderPlugin = {
91
+ id: '@jupyternaut/persona:google-provider',
92
+ description: 'Register Google Generative AI provider',
93
+ autoStart: true,
94
+ requires: [IProviderRegistry],
95
+ activate: (app, providerRegistry) => {
96
+ providerRegistry.registerProvider(googleProvider);
97
+ }
98
+ };
99
+ /**
100
+ * Mistral provider plugin
101
+ */
102
+ const mistralProviderPlugin = {
103
+ id: '@jupyternaut/persona:mistral-provider',
104
+ description: 'Register Mistral provider',
105
+ autoStart: true,
106
+ requires: [IProviderRegistry],
107
+ activate: (app, providerRegistry) => {
108
+ providerRegistry.registerProvider(mistralProvider);
109
+ }
110
+ };
111
+ /**
112
+ * OpenAI provider plugin
113
+ */
114
+ const openaiProviderPlugin = {
115
+ id: '@jupyternaut/persona:openai-provider',
116
+ description: 'Register OpenAI provider',
117
+ autoStart: true,
118
+ requires: [IProviderRegistry],
119
+ activate: (app, providerRegistry) => {
120
+ providerRegistry.registerProvider(openaiProvider);
121
+ }
122
+ };
123
+ /**
124
+ * Generic provider plugin
125
+ */
126
+ const genericProviderPlugin = {
127
+ id: '@jupyternaut/persona:generic-provider',
128
+ description: 'Register Generic OpenAI-compatible provider',
129
+ autoStart: true,
130
+ requires: [IProviderRegistry],
131
+ activate: (app, providerRegistry) => {
132
+ providerRegistry.registerProvider(genericProvider);
133
+ }
134
+ };
135
+ /**
136
+ * Provides the persona handler registry without any IChatTracker dependency,
137
+ * so it can be consumed by the toolbar factory without creating a plugin cycle.
138
+ */
139
+ const personaRegistry = {
140
+ id: '@jupyternaut/persona:registry',
141
+ description: 'Registry mapping chat models to their persona handlers',
142
+ autoStart: true,
143
+ provides: IPersonaRegistry,
144
+ requires: [IAISettingsModel],
145
+ optional: [IProviderRegistry, IDocumentManager],
146
+ activate: (app, settingsModel, providerRegistry, documentManager) => {
147
+ return new PersonaRegistry({
148
+ persona: DEFAULT_PERSONA,
149
+ settingsModel,
150
+ providerRegistry,
151
+ documentManager
152
+ });
153
+ }
154
+ };
155
+ /**
156
+ * Connects the persona handler registry to the chat tracker, creating a
157
+ * PersonaHandler for each chat widget that is opened.
158
+ */
159
+ const persona = {
160
+ id: '@jupyternaut/persona:plugin',
161
+ description: 'Attach persona handlers to chat widgets as they are opened',
162
+ autoStart: true,
163
+ requires: [IPersonaRegistry, IAgentManagerFactory, IAISettingsModel],
164
+ optional: [IChatTracker, IProviderRegistry, IToolRegistry],
165
+ activate: (app, registry, agentManagerFactory, settingsModel, chatTracker, providerRegistry, toolRegistry) => {
166
+ const attachPersona = (widget) => {
167
+ if (registry.get(widget.model)) {
168
+ return;
169
+ }
170
+ const agentManager = agentManagerFactory.createAgent({
171
+ settingsModel,
172
+ providerRegistry,
173
+ toolRegistry
174
+ });
175
+ registry.register(widget.model, agentManager);
176
+ widget.disposed.connect(() => {
177
+ registry.unregister(widget.model);
178
+ });
179
+ };
180
+ chatTracker?.forEach(widget => attachPersona(widget));
181
+ chatTracker?.widgetAdded.connect((_, widget) => attachPersona(widget));
182
+ }
183
+ };
184
+ /**
185
+ * Registers groupedToolCall callbacks to route tool call approval decisions
186
+ * to the correct persona's agent manager.
187
+ */
188
+ const chatComponentsCallbacks = {
189
+ id: '@jupyternaut/persona:chat-components-callbacks',
190
+ description: 'Register chat components callbacks for the persona.',
191
+ autoStart: true,
192
+ requires: [IPersonaRegistry],
193
+ optional: [IChatTracker, IComponentsRendererFactory],
194
+ activate: (_app, personaRegistry, chatTracker, chatComponentsFactory) => {
195
+ if (!chatComponentsFactory) {
196
+ return;
197
+ }
198
+ const findPersona = (sessionId) => {
199
+ const model = chatTracker?.find(chat => chat.model.name === sessionId)?.model;
200
+ return model ? personaRegistry.get(model) : undefined;
201
+ };
202
+ chatComponentsFactory.groupedToolCallCallbacks = {
203
+ ...chatComponentsFactory.groupedToolCallCallbacks,
204
+ toolCallPermissionDecision: (sessionId, toolCallId, optionId) => {
205
+ const agent = findPersona(sessionId)?.agentManager;
206
+ if (!agent) {
207
+ return;
208
+ }
209
+ if (optionId === 'approve') {
210
+ agent.approveToolCall(toolCallId);
211
+ }
212
+ else {
213
+ agent.rejectToolCall(toolCallId);
214
+ }
215
+ }
216
+ };
217
+ }
218
+ };
219
+ /**
220
+ * Clear chat command plugin.
221
+ */
222
+ const mentionCommandPlugin = {
223
+ id: '@jupyternaut/persona:mention',
224
+ description: 'Register the Jupyternaut mention chat command.',
225
+ autoStart: true,
226
+ requires: [IChatCommandRegistry],
227
+ activate: (app, registry) => {
228
+ registry.addProvider(new MentionCommandProvider());
229
+ }
230
+ };
231
+ /**
232
+ * Skills chat command plugin.
233
+ */
234
+ const skillsCommandPlugin = {
235
+ id: '@jupyternaut/persona:skills-command',
236
+ description: 'Register the /skills chat command.',
237
+ autoStart: true,
238
+ requires: [IChatCommandRegistry, ISkillRegistry, IPersonaRegistry],
239
+ optional: [IChatTracker],
240
+ activate: (app, registry, skillRegistry, personaRegistry, chatTracker) => {
241
+ const findModel = (chatName) => chatTracker?.find(c => c.model.name === chatName)?.model;
242
+ const isDefault = (chatName) => {
243
+ const model = findModel(chatName);
244
+ if (!model) {
245
+ return false;
246
+ }
247
+ const persona = personaRegistry.get(model);
248
+ return persona ? !persona.requireMention : false;
249
+ };
250
+ const sendSystemMessage = (chatName, body) => {
251
+ const model = findModel(chatName);
252
+ if (model) {
253
+ personaRegistry.get(model)?.sendSystemMessage(body);
254
+ }
255
+ };
256
+ registry.addProvider(new SkillsCommandProvider({
257
+ skillRegistry,
258
+ commands: app.commands,
259
+ isDefault,
260
+ sendSystemMessage
261
+ }));
262
+ }
263
+ };
264
+ /**
265
+ * A plugin to provide the agent manager factory and completion provider.
266
+ * These objects require the secrets manager token with the same namespace.
267
+ */
268
+ const agentManagerFactory = SecretsManager.sign(SECRETS_NAMESPACE, token => {
269
+ Private.setAISecretsToken(token);
270
+ return {
271
+ id: SECRETS_NAMESPACE,
272
+ description: 'Provide the AI agent manager',
273
+ autoStart: true,
274
+ provides: IAgentManagerFactory,
275
+ requires: [IAISettingsModel, IProviderRegistry],
276
+ optional: [
277
+ ISkillRegistry,
278
+ ICompletionProviderManager,
279
+ ISecretsManager,
280
+ IMcpManager
281
+ ],
282
+ activate: (app, settingsModel, providerRegistry, skillRegistry, completionManager, secretsManager, mcpManager) => {
283
+ const agentManagerFactory = new AgentManagerFactory({
284
+ settingsModel,
285
+ skillRegistry,
286
+ mcpManager,
287
+ secretsManager,
288
+ token
289
+ });
290
+ // Build the completion provider
291
+ if (completionManager) {
292
+ const completionProvider = new AICompletionProvider({
293
+ settingsModel,
294
+ providerRegistry,
295
+ secretsManager,
296
+ token
297
+ });
298
+ completionManager.registerInlineProvider(completionProvider);
299
+ }
300
+ else {
301
+ console.info('Completion provider manager not available, skipping AI completion setup');
302
+ }
303
+ return agentManagerFactory;
304
+ }
305
+ };
306
+ });
307
+ /**
308
+ * AI settings panel plugin.
309
+ */
310
+ const settingsPanelPlugin = {
311
+ id: '@jupyternaut/persona:settings-panel',
312
+ description: 'Provide the AI settings panel',
313
+ autoStart: true,
314
+ requires: [IAISettingsModel, IAgentManagerFactory, IProviderRegistry],
315
+ optional: [
316
+ ICommandPalette,
317
+ ILayoutRestorer,
318
+ ISecretsManager,
319
+ IThemeManager,
320
+ ITranslator,
321
+ IFormRendererRegistry,
322
+ // This token is not used, but depending on it ensures that the renderer has been
323
+ // added to the form renderer registry.
324
+ IMcpManager
325
+ ],
326
+ activate: (app, settingsModel, agentManagerFactory, providerRegistry, palette, restorer, secretsManager, themeManager, translator, formRenderer) => {
327
+ const trans = (translator ?? nullTranslator).load('jupyterlite_ai');
328
+ const secretsAccess = Private.createAISecretsAccess(secretsManager);
329
+ // Get the renderer for MCP servers settings
330
+ const mcpServerRenderer = formRenderer?.getRenderer('jupyter-mcp-manager:manager.mcpSettings').fieldRenderer;
331
+ const settingsWidget = new AISettingsWidget({
332
+ settingsModel,
333
+ agentManagerFactory,
334
+ themeManager,
335
+ providerRegistry,
336
+ secretsAccess,
337
+ trans,
338
+ mcpServerRenderer
339
+ });
340
+ settingsWidget.title.icon = settingsIcon;
341
+ settingsWidget.title.iconClass = 'jp-ai-settings-icon';
342
+ const open = () => {
343
+ let widget = Array.from(app.shell.widgets('main')).find(w => w.id === 'jupyternaut-persona-settings');
344
+ if (!widget) {
345
+ widget = settingsWidget;
346
+ app.shell.add(widget, 'main');
347
+ }
348
+ app.shell.activateById(widget.id);
349
+ };
350
+ if (restorer) {
351
+ restorer.add(settingsWidget, settingsWidget.id);
352
+ }
353
+ app.commands.addCommand(CommandIds.openSettings, {
354
+ label: trans.__('Jupyternaut Settings'),
355
+ caption: trans.__('Configure AI providers and behavior'),
356
+ icon: settingsIcon,
357
+ iconClass: 'jp-ai-settings-icon',
358
+ execute: () => {
359
+ open();
360
+ },
361
+ describedBy: {
362
+ args: {}
363
+ }
364
+ });
365
+ if (palette) {
366
+ palette.addItem({
367
+ command: CommandIds.openSettings,
368
+ category: trans.__('AI Assistant')
369
+ });
370
+ }
371
+ }
372
+ };
373
+ /**
374
+ * Built-in completion providers plugin
375
+ */
376
+ const settingsModel = {
377
+ id: '@jupyternaut/persona:settings-model',
378
+ description: 'Provide the AI settings model',
379
+ autoStart: true,
380
+ provides: IAISettingsModel,
381
+ requires: [ISettingRegistry],
382
+ activate: (app, settingRegistry) => {
383
+ return new AISettingsModel({ settingRegistry });
384
+ }
385
+ };
386
+ /**
387
+ * Diff manager plugin
388
+ */
389
+ const diffManager = {
390
+ id: '@jupyternaut/persona:diff-manager',
391
+ description: 'Provide the diff manager for notebook cell diffs',
392
+ autoStart: true,
393
+ provides: IDiffManager,
394
+ requires: [IAISettingsModel],
395
+ activate: (app, settingsModel) => {
396
+ return new DiffManager({
397
+ commands: app.commands,
398
+ settingsModel
399
+ });
400
+ }
401
+ };
402
+ /**
403
+ * Skill registry plugin
404
+ */
405
+ const skillRegistryPlugin = {
406
+ id: '@jupyternaut/persona:skill-registry',
407
+ description: 'Provide the skill registry',
408
+ autoStart: true,
409
+ provides: ISkillRegistry,
410
+ activate: () => {
411
+ return new SkillRegistry();
412
+ }
413
+ };
414
+ const toolRegistry = {
415
+ id: '@jupyternaut/persona:tool-registry',
416
+ description: 'Provide the AI tool registry',
417
+ autoStart: true,
418
+ optional: [ISkillRegistry],
419
+ provides: IToolRegistry,
420
+ activate: (app, skillRegistry) => {
421
+ const toolRegistry = new ToolRegistry();
422
+ // Add command operation tools
423
+ const discoverCommandsTool = createDiscoverCommandsTool(app.commands);
424
+ const executeCommandTool = createExecuteCommandTool(app.commands);
425
+ toolRegistry.add('discover_commands', discoverCommandsTool);
426
+ toolRegistry.add('execute_command', executeCommandTool);
427
+ toolRegistry.add('browser_fetch', createBrowserFetchTool());
428
+ if (skillRegistry) {
429
+ toolRegistry.add('discover_skills', createDiscoverSkillsTool(skillRegistry));
430
+ toolRegistry.add('load_skill', createLoadSkillTool(skillRegistry));
431
+ }
432
+ return toolRegistry;
433
+ }
434
+ };
435
+ const completionStatus = {
436
+ id: '@jupyternaut/persona:completion-status',
437
+ description: 'The completion status displayed in the status bar',
438
+ autoStart: true,
439
+ requires: [IAISettingsModel],
440
+ optional: [IStatusBar, ITranslator],
441
+ activate: (app, settingsModel, statusBar, translator) => {
442
+ if (!statusBar) {
443
+ return;
444
+ }
445
+ const trans = (translator ?? nullTranslator).load('jupyterlite_ai');
446
+ const item = new CompletionStatusWidget({
447
+ settingsModel,
448
+ translator: trans
449
+ });
450
+ statusBar?.registerStatusItem('completionState', {
451
+ item,
452
+ align: 'right',
453
+ rank: 10
454
+ });
455
+ }
456
+ };
457
+ /**
458
+ * Skills plugin: discovers and registers agent skills from the filesystem.
459
+ */
460
+ const skillsPlugin = {
461
+ id: '@jupyternaut/persona:skills',
462
+ description: 'Discover and register agent skills',
463
+ autoStart: true,
464
+ requires: [IAISettingsModel, IDocumentManager, ISkillRegistry],
465
+ optional: [ICommandPalette, ITranslator],
466
+ activate: async (app, settingsModel, docManager, skillRegistry, palette, translator) => {
467
+ const trans = (translator ?? nullTranslator).load('jupyterlite_ai');
468
+ const validateResourcePath = (resourcePath) => {
469
+ if (resourcePath.startsWith('/')) {
470
+ return null;
471
+ }
472
+ const normalized = PathExt.normalize(resourcePath);
473
+ if (normalized.startsWith('..') || normalized === '') {
474
+ return null;
475
+ }
476
+ return normalized;
477
+ };
478
+ let currentSkillsPaths = settingsModel.config.skillsPaths;
479
+ let currentSkillDisposables = new DisposableSet();
480
+ const loadAndRegister = async () => {
481
+ const skillsPaths = settingsModel.config.skillsPaths;
482
+ const skills = await loadSkillsFromPaths(docManager.services.contents, skillsPaths);
483
+ const registrations = skills.map(skill => ({
484
+ name: skill.name,
485
+ description: skill.description,
486
+ instructions: skill.instructions,
487
+ resources: skill.resources,
488
+ loadResource: async (resource) => {
489
+ const validatedPath = validateResourcePath(resource);
490
+ if (validatedPath === null) {
491
+ return {
492
+ name: skill.name,
493
+ resource,
494
+ error: 'Invalid resource path: path traversal not allowed'
495
+ };
496
+ }
497
+ if (!skill.resources.includes(validatedPath)) {
498
+ return {
499
+ name: skill.name,
500
+ resource,
501
+ error: `Resource not found: ${resource}`
502
+ };
503
+ }
504
+ const resourcePath = `${skill.path}/${validatedPath}`;
505
+ try {
506
+ const fileModel = await docManager.services.contents.get(resourcePath, {
507
+ content: true
508
+ });
509
+ if (typeof fileModel.content !== 'string') {
510
+ return {
511
+ name: skill.name,
512
+ resource,
513
+ error: 'Resource content is not a string'
514
+ };
515
+ }
516
+ return {
517
+ name: skill.name,
518
+ resource,
519
+ content: fileModel.content
520
+ };
521
+ }
522
+ catch (error) {
523
+ return {
524
+ name: skill.name,
525
+ resource,
526
+ error: `Failed to read resource: ${error}`
527
+ };
528
+ }
529
+ }
530
+ }));
531
+ currentSkillDisposables.dispose();
532
+ currentSkillDisposables = new DisposableSet();
533
+ for (const registration of registrations) {
534
+ currentSkillDisposables.add(skillRegistry.registerSkill(registration));
535
+ }
536
+ };
537
+ app.commands.addCommand(CommandIds.refreshSkills, {
538
+ label: trans.__('Refresh Agents Skills'),
539
+ caption: trans.__('Re-scan the agents skills directory and update the registry'),
540
+ execute: async () => {
541
+ await loadAndRegister();
542
+ }
543
+ });
544
+ if (palette) {
545
+ palette.addItem({
546
+ command: CommandIds.refreshSkills,
547
+ category: trans.__('AI Assistant')
548
+ });
549
+ }
550
+ loadAndRegister().catch(error => console.warn('Failed to load skills on activation:', error));
551
+ settingsModel.stateChanged.connect(() => {
552
+ const newPaths = settingsModel.config.skillsPaths;
553
+ if (newPaths.length === currentSkillsPaths.length &&
554
+ newPaths.every((p, i) => p === currentSkillsPaths[i])) {
555
+ return;
556
+ }
557
+ currentSkillsPaths = newPaths;
558
+ loadAndRegister().catch(error => console.warn('Failed to reload skills:', error));
559
+ });
560
+ }
561
+ };
562
+ export default [
563
+ // Provider registry and builtin providers
564
+ providerRegistryPlugin,
565
+ anthropicProviderPlugin,
566
+ googleProviderPlugin,
567
+ mistralProviderPlugin,
568
+ openaiProviderPlugin,
569
+ genericProviderPlugin,
570
+ // Agent
571
+ agentManagerFactory,
572
+ completionStatus,
573
+ // Skills
574
+ skillRegistryPlugin,
575
+ skillsPlugin,
576
+ // Tools
577
+ toolRegistry,
578
+ // Persona
579
+ personaRegistry,
580
+ persona,
581
+ chatComponentsCallbacks,
582
+ mentionCommandPlugin,
583
+ skillsCommandPlugin,
584
+ // Settings
585
+ settingsModel,
586
+ settingsPanelPlugin,
587
+ // Diff manager (to be removed ?)
588
+ diffManager
589
+ ];
590
+ // Export extension points for other extensions to use
591
+ export * from './tokens';
592
+ // Export helper functions
593
+ export { processAttachments } from './process-attachments';
@@ -0,0 +1,36 @@
1
+ import type { IAIConfig, IAISettingsModel, IProviderConfig } from '@jupyternaut/agent';
2
+ import { VDomModel } from '@jupyterlab/ui-components';
3
+ import { ISettingRegistry } from '@jupyterlab/settingregistry';
4
+ export declare class AISettingsModel extends VDomModel implements IAISettingsModel {
5
+ private _config;
6
+ private _settingRegistry;
7
+ private _settings;
8
+ constructor(options: AISettingsModel.IOptions);
9
+ private _initializeSettings;
10
+ private _onSettingsChanged;
11
+ private _loadFromSettings;
12
+ get config(): IAIConfig;
13
+ get providers(): IProviderConfig[];
14
+ getProvider(id: string): IProviderConfig | undefined;
15
+ getDefaultProvider(): IProviderConfig | undefined;
16
+ getCompleterProvider(): IProviderConfig | undefined;
17
+ addProvider(providerConfig: Omit<IProviderConfig, 'id'>): Promise<string>;
18
+ removeProvider(id: string): Promise<void>;
19
+ updateProvider(id: string, updates: Partial<IProviderConfig>): Promise<void>;
20
+ setActiveProvider(id: string): Promise<void>;
21
+ setActiveCompleterProvider(id: string | undefined): Promise<void>;
22
+ updateConfig(updates: Partial<IAIConfig>): Promise<void>;
23
+ /**
24
+ * Get the API key saved in the settings file for a given provider.
25
+ *
26
+ * @param id - the id of the provider.
27
+ */
28
+ getApiKey(id: string): string;
29
+ private _saveSetting;
30
+ }
31
+ export declare namespace AISettingsModel {
32
+ interface IOptions {
33
+ settingRegistry: ISettingRegistry;
34
+ }
35
+ }
36
+ //# sourceMappingURL=settings-model.d.ts.map