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