@jupyterlite/ai 0.9.1 → 0.11.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/README.md +5 -214
- package/lib/agent.d.ts +58 -66
- package/lib/agent.js +291 -310
- package/lib/approval-buttons.d.ts +19 -82
- package/lib/approval-buttons.js +36 -289
- package/lib/chat-model-registry.d.ts +6 -0
- package/lib/chat-model-registry.js +4 -1
- package/lib/chat-model.d.ts +26 -54
- package/lib/chat-model.js +277 -303
- package/lib/components/clear-button.d.ts +6 -1
- package/lib/components/clear-button.js +10 -6
- package/lib/components/completion-status.d.ts +5 -0
- package/lib/components/completion-status.js +5 -4
- package/lib/components/model-select.d.ts +6 -1
- package/lib/components/model-select.js +13 -16
- package/lib/components/stop-button.d.ts +6 -1
- package/lib/components/stop-button.js +12 -8
- package/lib/components/token-usage-display.d.ts +5 -0
- package/lib/components/token-usage-display.js +2 -2
- package/lib/components/tool-select.d.ts +6 -1
- package/lib/components/tool-select.js +10 -9
- package/lib/index.d.ts +1 -0
- package/lib/index.js +61 -81
- package/lib/models/settings-model.d.ts +1 -1
- package/lib/models/settings-model.js +40 -26
- package/lib/providers/built-in-providers.js +38 -19
- package/lib/providers/models.d.ts +3 -3
- package/lib/providers/provider-registry.d.ts +3 -4
- package/lib/providers/provider-registry.js +1 -4
- package/lib/tokens.d.ts +5 -6
- package/lib/tools/commands.d.ts +2 -1
- package/lib/tools/commands.js +36 -49
- package/lib/widgets/ai-settings.d.ts +6 -0
- package/lib/widgets/ai-settings.js +72 -71
- package/lib/widgets/main-area-chat.d.ts +2 -0
- package/lib/widgets/main-area-chat.js +5 -2
- package/lib/widgets/provider-config-dialog.d.ts +2 -0
- package/lib/widgets/provider-config-dialog.js +34 -34
- package/package.json +13 -13
- package/schema/settings-model.json +3 -2
- package/src/agent.ts +360 -372
- package/src/approval-buttons.ts +43 -389
- package/src/chat-model-registry.ts +9 -1
- package/src/chat-model.ts +399 -370
- package/src/completion/completion-provider.ts +2 -3
- package/src/components/clear-button.tsx +18 -6
- package/src/components/completion-status.tsx +18 -4
- package/src/components/model-select.tsx +25 -16
- package/src/components/stop-button.tsx +22 -9
- package/src/components/token-usage-display.tsx +14 -2
- package/src/components/tool-select.tsx +27 -9
- package/src/index.ts +78 -134
- package/src/models/settings-model.ts +41 -27
- package/src/providers/built-in-providers.ts +38 -19
- package/src/providers/models.ts +3 -3
- package/src/providers/provider-registry.ts +4 -8
- package/src/tokens.ts +5 -6
- package/src/tools/commands.ts +40 -53
- package/src/widgets/ai-settings.tsx +153 -84
- package/src/widgets/main-area-chat.ts +8 -2
- package/src/widgets/provider-config-dialog.tsx +54 -41
- package/style/base.css +24 -73
- package/lib/mcp/browser.d.ts +0 -68
- package/lib/mcp/browser.js +0 -138
- package/lib/tools/file.d.ts +0 -36
- package/lib/tools/file.js +0 -351
- package/lib/tools/notebook.d.ts +0 -40
- package/lib/tools/notebook.js +0 -779
- package/src/mcp/browser.ts +0 -220
- package/src/tools/file.ts +0 -438
- package/src/tools/notebook.ts +0 -986
package/lib/agent.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Signal } from '@lumino/signaling';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { ToolLoopAgent, stepCountIs } from 'ai';
|
|
3
|
+
import { createMCPClient } from '@ai-sdk/mcp';
|
|
4
4
|
import { createModel } from './providers/models';
|
|
5
5
|
import { SECRETS_NAMESPACE } from './tokens';
|
|
6
6
|
export class AgentManagerFactory {
|
|
@@ -8,7 +8,7 @@ export class AgentManagerFactory {
|
|
|
8
8
|
Private.setToken(options.token);
|
|
9
9
|
this._settingsModel = options.settingsModel;
|
|
10
10
|
this._secretsManager = options.secretsManager;
|
|
11
|
-
this.
|
|
11
|
+
this._mcpClients = [];
|
|
12
12
|
this._mcpConnectionChanged = new Signal(this);
|
|
13
13
|
// Initialize agent on construction
|
|
14
14
|
this._initializeAgents().catch(error => console.warn('Failed to initialize agent in constructor:', error));
|
|
@@ -35,7 +35,23 @@ export class AgentManagerFactory {
|
|
|
35
35
|
* @returns True if the server is connected, false otherwise
|
|
36
36
|
*/
|
|
37
37
|
isMCPServerConnected(serverName) {
|
|
38
|
-
return this.
|
|
38
|
+
return this._mcpClients.some(wrapper => wrapper.name === serverName);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Gets the MCP tools from connected servers
|
|
42
|
+
*/
|
|
43
|
+
async getMCPTools() {
|
|
44
|
+
const mcpTools = {};
|
|
45
|
+
for (const wrapper of this._mcpClients) {
|
|
46
|
+
try {
|
|
47
|
+
const tools = await wrapper.client.tools();
|
|
48
|
+
Object.assign(mcpTools, tools);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.warn(`Failed to get tools from MCP server ${wrapper.name}:`, error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return mcpTools;
|
|
39
55
|
}
|
|
40
56
|
/**
|
|
41
57
|
* Handles settings changes and reinitializes the agent.
|
|
@@ -44,33 +60,36 @@ export class AgentManagerFactory {
|
|
|
44
60
|
this._initializeAgents().catch(error => console.warn('Failed to initialize agent on settings change:', error));
|
|
45
61
|
}
|
|
46
62
|
/**
|
|
47
|
-
* Initializes MCP (Model Context Protocol)
|
|
48
|
-
* Closes existing
|
|
63
|
+
* Initializes MCP (Model Context Protocol) clients based on current settings.
|
|
64
|
+
* Closes existing clients and connects to enabled servers from configuration.
|
|
49
65
|
*/
|
|
50
|
-
async
|
|
66
|
+
async _initializeMCPClients() {
|
|
51
67
|
const config = this._settingsModel.config;
|
|
52
68
|
const enabledServers = config.mcpServers.filter(server => server.enabled);
|
|
53
69
|
let connectionChanged = false;
|
|
54
|
-
// Close existing
|
|
55
|
-
for (const
|
|
70
|
+
// Close existing clients
|
|
71
|
+
for (const wrapper of this._mcpClients) {
|
|
56
72
|
try {
|
|
57
|
-
await
|
|
73
|
+
await wrapper.client.close();
|
|
58
74
|
connectionChanged = true;
|
|
59
75
|
}
|
|
60
76
|
catch (error) {
|
|
61
|
-
console.warn('Error closing MCP
|
|
77
|
+
console.warn('Error closing MCP client:', error);
|
|
62
78
|
}
|
|
63
79
|
}
|
|
64
|
-
this.
|
|
65
|
-
// Initialize new servers
|
|
80
|
+
this._mcpClients = [];
|
|
66
81
|
for (const serverConfig of enabledServers) {
|
|
67
82
|
try {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
83
|
+
const client = await createMCPClient({
|
|
84
|
+
transport: {
|
|
85
|
+
type: 'http',
|
|
86
|
+
url: serverConfig.url
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
this._mcpClients.push({
|
|
90
|
+
name: serverConfig.name,
|
|
91
|
+
client
|
|
71
92
|
});
|
|
72
|
-
await mcpServer.connect();
|
|
73
|
-
this._mcpServers.push(mcpServer);
|
|
74
93
|
connectionChanged = true;
|
|
75
94
|
}
|
|
76
95
|
catch (error) {
|
|
@@ -79,7 +98,7 @@ export class AgentManagerFactory {
|
|
|
79
98
|
}
|
|
80
99
|
// Emit connection change signal if there were any changes
|
|
81
100
|
if (connectionChanged) {
|
|
82
|
-
this._mcpConnectionChanged.emit(this.
|
|
101
|
+
this._mcpConnectionChanged.emit(this._mcpClients.length > 0);
|
|
83
102
|
}
|
|
84
103
|
}
|
|
85
104
|
/**
|
|
@@ -92,10 +111,10 @@ export class AgentManagerFactory {
|
|
|
92
111
|
}
|
|
93
112
|
this._isInitializing = true;
|
|
94
113
|
try {
|
|
95
|
-
await this.
|
|
96
|
-
const
|
|
114
|
+
await this._initializeMCPClients();
|
|
115
|
+
const mcpTools = await this.getMCPTools();
|
|
97
116
|
this._agentManagers.forEach(manager => {
|
|
98
|
-
manager.initializeAgent(
|
|
117
|
+
manager.initializeAgent(mcpTools);
|
|
99
118
|
});
|
|
100
119
|
}
|
|
101
120
|
catch (error) {
|
|
@@ -108,7 +127,7 @@ export class AgentManagerFactory {
|
|
|
108
127
|
_agentManagers = [];
|
|
109
128
|
_settingsModel;
|
|
110
129
|
_secretsManager;
|
|
111
|
-
|
|
130
|
+
_mcpClients;
|
|
112
131
|
_mcpConnectionChanged;
|
|
113
132
|
_isInitializing = false;
|
|
114
133
|
}
|
|
@@ -120,7 +139,7 @@ const DEFAULT_MAX_TURNS = 25;
|
|
|
120
139
|
/**
|
|
121
140
|
* Manages the AI agent lifecycle and execution loop.
|
|
122
141
|
* Provides agent initialization, tool management, MCP server integration,
|
|
123
|
-
* and handles the complete agent execution cycle
|
|
142
|
+
* and handles the complete agent execution cycle.
|
|
124
143
|
* Emits events for UI updates instead of directly manipulating the chat interface.
|
|
125
144
|
*/
|
|
126
145
|
export class AgentManager {
|
|
@@ -135,13 +154,10 @@ export class AgentManager {
|
|
|
135
154
|
this._secretsManager = options.secretsManager;
|
|
136
155
|
this._selectedToolNames = [];
|
|
137
156
|
this._agent = null;
|
|
138
|
-
this._runner = new Runner({ tracingDisabled: true });
|
|
139
157
|
this._history = [];
|
|
140
|
-
this.
|
|
158
|
+
this._mcpTools = {};
|
|
141
159
|
this._isInitializing = false;
|
|
142
160
|
this._controller = null;
|
|
143
|
-
this._pendingApprovals = new Map();
|
|
144
|
-
this._interruptedState = null;
|
|
145
161
|
this._agentEvent = new Signal(this);
|
|
146
162
|
this._tokenUsage = options.tokenUsage ?? {
|
|
147
163
|
inputTokens: 0,
|
|
@@ -199,18 +215,18 @@ export class AgentManager {
|
|
|
199
215
|
this.initializeAgent().catch(error => console.warn('Failed to initialize agent on tools change:', error));
|
|
200
216
|
}
|
|
201
217
|
/**
|
|
202
|
-
* Gets the currently selected tools as
|
|
203
|
-
* @returns
|
|
218
|
+
* Gets the currently selected tools as a record.
|
|
219
|
+
* @returns Record of selected tools
|
|
204
220
|
*/
|
|
205
221
|
get selectedAgentTools() {
|
|
206
222
|
if (!this._toolRegistry) {
|
|
207
|
-
return
|
|
223
|
+
return {};
|
|
208
224
|
}
|
|
209
|
-
const result =
|
|
225
|
+
const result = {};
|
|
210
226
|
for (const name of this._selectedToolNames) {
|
|
211
227
|
const tool = this._toolRegistry.get(name);
|
|
212
228
|
if (tool) {
|
|
213
|
-
result
|
|
229
|
+
result[name] = tool;
|
|
214
230
|
}
|
|
215
231
|
}
|
|
216
232
|
return result;
|
|
@@ -238,14 +254,21 @@ export class AgentManager {
|
|
|
238
254
|
}
|
|
239
255
|
/**
|
|
240
256
|
* Clears conversation history and resets agent state.
|
|
241
|
-
* Removes all conversation history, pending approvals, and interrupted state.
|
|
242
257
|
*/
|
|
243
258
|
clearHistory() {
|
|
244
|
-
|
|
245
|
-
this.
|
|
259
|
+
// Stop any ongoing streaming
|
|
260
|
+
this.stopStreaming();
|
|
261
|
+
// Reject any pending approvals
|
|
262
|
+
for (const [approvalId, pending] of this._pendingApprovals) {
|
|
263
|
+
pending.resolve(false, 'Chat cleared');
|
|
264
|
+
this._agentEvent.emit({
|
|
265
|
+
type: 'tool_approval_resolved',
|
|
266
|
+
data: { approvalId, approved: false }
|
|
267
|
+
});
|
|
268
|
+
}
|
|
246
269
|
this._pendingApprovals.clear();
|
|
247
|
-
|
|
248
|
-
|
|
270
|
+
// Clear history and token usage
|
|
271
|
+
this._history = [];
|
|
249
272
|
this._tokenUsage = { inputTokens: 0, outputTokens: 0 };
|
|
250
273
|
this._tokenUsageChanged.emit(this._tokenUsage);
|
|
251
274
|
}
|
|
@@ -255,13 +278,44 @@ export class AgentManager {
|
|
|
255
278
|
stopStreaming() {
|
|
256
279
|
this._controller?.abort();
|
|
257
280
|
}
|
|
281
|
+
/**
|
|
282
|
+
* Approves a pending tool call.
|
|
283
|
+
* @param approvalId The approval ID to approve
|
|
284
|
+
* @param reason Optional reason for approval
|
|
285
|
+
*/
|
|
286
|
+
approveToolCall(approvalId, reason) {
|
|
287
|
+
const pending = this._pendingApprovals.get(approvalId);
|
|
288
|
+
if (pending) {
|
|
289
|
+
pending.resolve(true, reason);
|
|
290
|
+
this._pendingApprovals.delete(approvalId);
|
|
291
|
+
this._agentEvent.emit({
|
|
292
|
+
type: 'tool_approval_resolved',
|
|
293
|
+
data: { approvalId, approved: true }
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Rejects a pending tool call.
|
|
299
|
+
* @param approvalId The approval ID to reject
|
|
300
|
+
* @param reason Optional reason for rejection
|
|
301
|
+
*/
|
|
302
|
+
rejectToolCall(approvalId, reason) {
|
|
303
|
+
const pending = this._pendingApprovals.get(approvalId);
|
|
304
|
+
if (pending) {
|
|
305
|
+
pending.resolve(false, reason);
|
|
306
|
+
this._pendingApprovals.delete(approvalId);
|
|
307
|
+
this._agentEvent.emit({
|
|
308
|
+
type: 'tool_approval_resolved',
|
|
309
|
+
data: { approvalId, approved: false }
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
258
313
|
/**
|
|
259
314
|
* Generates AI response to user message using the agent.
|
|
260
|
-
* Handles the complete execution cycle including tool calls
|
|
315
|
+
* Handles the complete execution cycle including tool calls.
|
|
261
316
|
* @param message The user message to respond to (may include processed attachment content)
|
|
262
317
|
*/
|
|
263
318
|
async generateResponse(message) {
|
|
264
|
-
const config = this._settingsModel.config;
|
|
265
319
|
this._controller = new AbortController();
|
|
266
320
|
try {
|
|
267
321
|
// Ensure we have an agent
|
|
@@ -271,140 +325,79 @@ export class AgentManager {
|
|
|
271
325
|
if (!this._agent) {
|
|
272
326
|
throw new Error('Failed to initialize agent');
|
|
273
327
|
}
|
|
274
|
-
const shouldUseTools = config.toolsEnabled &&
|
|
275
|
-
this._selectedToolNames.length > 0 &&
|
|
276
|
-
this._toolRegistry &&
|
|
277
|
-
Object.keys(this._toolRegistry.tools).length > 0 &&
|
|
278
|
-
this._supportsToolCalling();
|
|
279
328
|
// Add user message to history
|
|
280
|
-
this._history.push(
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const maxTurns = activeProviderConfig?.parameters?.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
284
|
-
// Main agentic loop
|
|
285
|
-
let result = await this._runner.run(this._agent, this._history, {
|
|
286
|
-
stream: true,
|
|
287
|
-
signal: this._controller.signal,
|
|
288
|
-
...(shouldUseTools && { maxTurns })
|
|
329
|
+
this._history.push({
|
|
330
|
+
role: 'user',
|
|
331
|
+
content: message
|
|
289
332
|
});
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
333
|
+
let continueLoop = true;
|
|
334
|
+
while (continueLoop) {
|
|
335
|
+
const result = await this._agent.stream({
|
|
336
|
+
messages: this._history,
|
|
337
|
+
abortSignal: this._controller.signal
|
|
338
|
+
});
|
|
339
|
+
const streamResult = await this._processStreamResult(result);
|
|
340
|
+
// Get response messages and update token usage
|
|
341
|
+
const responseMessages = await result.response;
|
|
342
|
+
this._updateTokenUsage(await result.usage);
|
|
343
|
+
// Add response messages to history
|
|
344
|
+
if (responseMessages.messages?.length) {
|
|
345
|
+
this._history.push(...responseMessages.messages);
|
|
300
346
|
}
|
|
301
|
-
//
|
|
302
|
-
|
|
303
|
-
|
|
347
|
+
// Add approval response if processed
|
|
348
|
+
if (streamResult.approvalResponse) {
|
|
349
|
+
// Check if the last message is a tool message we can append to
|
|
350
|
+
const lastMsg = this._history[this._history.length - 1];
|
|
351
|
+
if (lastMsg &&
|
|
352
|
+
lastMsg.role === 'tool' &&
|
|
353
|
+
Array.isArray(lastMsg.content) &&
|
|
354
|
+
Array.isArray(streamResult.approvalResponse.content)) {
|
|
355
|
+
const toolContent = lastMsg.content;
|
|
356
|
+
toolContent.push(...streamResult.approvalResponse.content);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
// Add as separate message
|
|
360
|
+
this._history.push(streamResult.approvalResponse);
|
|
361
|
+
}
|
|
304
362
|
}
|
|
305
|
-
|
|
306
|
-
result = await this._runner.run(this._agent, result.state, {
|
|
307
|
-
stream: true,
|
|
308
|
-
signal: this._controller.signal,
|
|
309
|
-
maxTurns
|
|
310
|
-
});
|
|
311
|
-
await this._processRunResult(result);
|
|
312
|
-
hasInterruptions =
|
|
313
|
-
result.interruptions && result.interruptions.length > 0;
|
|
363
|
+
continueLoop = streamResult.approvalProcessed;
|
|
314
364
|
}
|
|
315
|
-
// Clear interrupted state
|
|
316
|
-
this._interruptedState = null;
|
|
317
|
-
this._history = result.history;
|
|
318
365
|
}
|
|
319
366
|
catch (error) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
367
|
+
if (error.name !== 'AbortError') {
|
|
368
|
+
this._agentEvent.emit({
|
|
369
|
+
type: 'error',
|
|
370
|
+
data: { error: error }
|
|
371
|
+
});
|
|
372
|
+
}
|
|
324
373
|
}
|
|
325
374
|
finally {
|
|
326
375
|
this._controller = null;
|
|
327
376
|
}
|
|
328
377
|
}
|
|
329
378
|
/**
|
|
330
|
-
*
|
|
331
|
-
* @param interruptionId The interruption ID to approve
|
|
332
|
-
*/
|
|
333
|
-
async approveToolCall(interruptionId) {
|
|
334
|
-
const pending = this._pendingApprovals.get(interruptionId);
|
|
335
|
-
if (!pending) {
|
|
336
|
-
console.warn(`No pending approval found for interruption ${interruptionId}`);
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
if (this._interruptedState) {
|
|
340
|
-
this._interruptedState.state.approve(pending.interruption);
|
|
341
|
-
}
|
|
342
|
-
pending.approved = true;
|
|
343
|
-
this._pendingApprovals.delete(interruptionId);
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* Rejects a tool call by interruption ID.
|
|
347
|
-
* @param interruptionId The interruption ID to reject
|
|
348
|
-
*/
|
|
349
|
-
async rejectToolCall(interruptionId) {
|
|
350
|
-
const pending = this._pendingApprovals.get(interruptionId);
|
|
351
|
-
if (!pending) {
|
|
352
|
-
console.warn(`No pending approval found for interruption ${interruptionId}`);
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
if (this._interruptedState) {
|
|
356
|
-
this._interruptedState.state.reject(pending.interruption);
|
|
357
|
-
}
|
|
358
|
-
pending.approved = false;
|
|
359
|
-
this._pendingApprovals.delete(interruptionId);
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* Approves all tools in a group by group ID.
|
|
363
|
-
* @param groupId The group ID containing the tool calls
|
|
364
|
-
* @param interruptionIds Array of interruption IDs to approve
|
|
365
|
-
*/
|
|
366
|
-
async approveGroupedToolCalls(groupId, interruptionIds) {
|
|
367
|
-
for (const interruptionId of interruptionIds) {
|
|
368
|
-
const pending = this._pendingApprovals.get(interruptionId);
|
|
369
|
-
if (pending && pending.groupId === groupId) {
|
|
370
|
-
if (this._interruptedState) {
|
|
371
|
-
this._interruptedState.state.approve(pending.interruption);
|
|
372
|
-
}
|
|
373
|
-
pending.approved = true;
|
|
374
|
-
this._pendingApprovals.delete(interruptionId);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
/**
|
|
379
|
-
* Rejects all tools in a group by group ID.
|
|
380
|
-
* @param groupId The group ID containing the tool calls
|
|
381
|
-
* @param interruptionIds Array of interruption IDs to reject
|
|
379
|
+
* Updates token usage statistics.
|
|
382
380
|
*/
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
this._interruptedState.state.reject(pending.interruption);
|
|
389
|
-
}
|
|
390
|
-
pending.approved = false;
|
|
391
|
-
this._pendingApprovals.delete(interruptionId);
|
|
392
|
-
}
|
|
381
|
+
_updateTokenUsage(usage) {
|
|
382
|
+
if (usage) {
|
|
383
|
+
this._tokenUsage.inputTokens += usage.inputTokens ?? 0;
|
|
384
|
+
this._tokenUsage.outputTokens += usage.outputTokens ?? 0;
|
|
385
|
+
this._tokenUsageChanged.emit(this._tokenUsage);
|
|
393
386
|
}
|
|
394
387
|
}
|
|
395
388
|
/**
|
|
396
389
|
* Initializes the AI agent with current settings and tools.
|
|
397
|
-
* Sets up the agent with model configuration, tools, and MCP
|
|
390
|
+
* Sets up the agent with model configuration, tools, and MCP tools.
|
|
398
391
|
*/
|
|
399
|
-
initializeAgent = async (
|
|
392
|
+
initializeAgent = async (mcpTools) => {
|
|
400
393
|
if (this._isInitializing) {
|
|
401
394
|
return;
|
|
402
395
|
}
|
|
403
396
|
this._isInitializing = true;
|
|
404
397
|
try {
|
|
405
398
|
const config = this._settingsModel.config;
|
|
406
|
-
if (
|
|
407
|
-
this.
|
|
399
|
+
if (mcpTools !== undefined) {
|
|
400
|
+
this._mcpTools = mcpTools;
|
|
408
401
|
}
|
|
409
402
|
const model = await this._createModel();
|
|
410
403
|
const shouldUseTools = config.toolsEnabled &&
|
|
@@ -412,24 +405,23 @@ export class AgentManager {
|
|
|
412
405
|
this._toolRegistry &&
|
|
413
406
|
Object.keys(this._toolRegistry.tools).length > 0 &&
|
|
414
407
|
this._supportsToolCalling();
|
|
415
|
-
const tools = shouldUseTools
|
|
408
|
+
const tools = shouldUseTools
|
|
409
|
+
? { ...this.selectedAgentTools, ...this._mcpTools }
|
|
410
|
+
: this._mcpTools;
|
|
416
411
|
const activeProviderConfig = this._settingsModel.getProvider(this._activeProvider);
|
|
417
412
|
const temperature = activeProviderConfig?.parameters?.temperature ?? DEFAULT_TEMPERATURE;
|
|
418
|
-
const maxTokens = activeProviderConfig?.parameters?.
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
model
|
|
425
|
-
|
|
413
|
+
const maxTokens = activeProviderConfig?.parameters?.maxOutputTokens;
|
|
414
|
+
const maxTurns = activeProviderConfig?.parameters?.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
415
|
+
const instructions = shouldUseTools
|
|
416
|
+
? this._getEnhancedSystemPrompt(config.systemPrompt || '')
|
|
417
|
+
: config.systemPrompt || 'You are a helpful assistant.';
|
|
418
|
+
this._agent = new ToolLoopAgent({
|
|
419
|
+
model,
|
|
420
|
+
instructions,
|
|
426
421
|
tools,
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
maxTokens
|
|
431
|
-
}
|
|
432
|
-
})
|
|
422
|
+
temperature,
|
|
423
|
+
maxOutputTokens: maxTokens,
|
|
424
|
+
stopWhen: stepCountIs(maxTurns)
|
|
433
425
|
});
|
|
434
426
|
}
|
|
435
427
|
catch (error) {
|
|
@@ -441,175 +433,159 @@ export class AgentManager {
|
|
|
441
433
|
}
|
|
442
434
|
};
|
|
443
435
|
/**
|
|
444
|
-
* Processes the result
|
|
436
|
+
* Processes the stream result from agent execution.
|
|
445
437
|
* Handles message streaming, tool calls, and emits appropriate events.
|
|
446
|
-
* @param result The
|
|
438
|
+
* @param result The stream result from agent execution
|
|
439
|
+
* @returns Processing result including approval info if applicable
|
|
447
440
|
*/
|
|
448
|
-
async
|
|
441
|
+
async _processStreamResult(result) {
|
|
449
442
|
let fullResponse = '';
|
|
450
443
|
let currentMessageId = null;
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
this._agentEvent.emit({
|
|
458
|
-
type: 'message_start',
|
|
459
|
-
data: { messageId: currentMessageId }
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
|
-
else if (data.type === 'output_text_delta') {
|
|
463
|
-
if (currentMessageId) {
|
|
464
|
-
const chunk = data.delta || '';
|
|
465
|
-
fullResponse += chunk;
|
|
444
|
+
const processResult = { approvalProcessed: false };
|
|
445
|
+
for await (const part of result.fullStream) {
|
|
446
|
+
switch (part.type) {
|
|
447
|
+
case 'text-delta':
|
|
448
|
+
if (!currentMessageId) {
|
|
449
|
+
currentMessageId = `msg-${Date.now()}-${Math.random()}`;
|
|
466
450
|
this._agentEvent.emit({
|
|
467
|
-
type: '
|
|
468
|
-
data: {
|
|
469
|
-
messageId: currentMessageId,
|
|
470
|
-
chunk,
|
|
471
|
-
fullContent: fullResponse
|
|
472
|
-
}
|
|
451
|
+
type: 'message_start',
|
|
452
|
+
data: { messageId: currentMessageId }
|
|
473
453
|
});
|
|
474
454
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
455
|
+
fullResponse += part.text;
|
|
456
|
+
this._agentEvent.emit({
|
|
457
|
+
type: 'message_chunk',
|
|
458
|
+
data: {
|
|
459
|
+
messageId: currentMessageId,
|
|
460
|
+
chunk: part.text,
|
|
461
|
+
fullContent: fullResponse
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
break;
|
|
465
|
+
case 'tool-call':
|
|
466
|
+
// Complete current message before tool call
|
|
467
|
+
if (currentMessageId && fullResponse) {
|
|
468
|
+
this._emitMessageComplete(currentMessageId, fullResponse);
|
|
485
469
|
currentMessageId = null;
|
|
470
|
+
fullResponse = '';
|
|
486
471
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
472
|
+
this._agentEvent.emit({
|
|
473
|
+
type: 'tool_call_start',
|
|
474
|
+
data: {
|
|
475
|
+
callId: part.toolCallId,
|
|
476
|
+
toolName: part.toolName,
|
|
477
|
+
input: this._formatToolInput(JSON.stringify(part.input))
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
break;
|
|
481
|
+
case 'tool-result':
|
|
482
|
+
this._handleToolResult(part);
|
|
483
|
+
break;
|
|
484
|
+
case 'tool-approval-request':
|
|
485
|
+
// Complete current message before approval
|
|
486
|
+
if (currentMessageId && fullResponse) {
|
|
487
|
+
this._emitMessageComplete(currentMessageId, fullResponse);
|
|
488
|
+
currentMessageId = null;
|
|
489
|
+
fullResponse = '';
|
|
497
490
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
}
|
|
491
|
+
await this._handleApprovalRequest(part, processResult);
|
|
492
|
+
break;
|
|
493
|
+
// Ignore: text-start, text-end, finish, error, and others
|
|
494
|
+
default:
|
|
495
|
+
break;
|
|
504
496
|
}
|
|
505
497
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
* @param input The tool input string to format
|
|
510
|
-
* @returns Pretty-printed JSON string
|
|
511
|
-
*/
|
|
512
|
-
_formatToolInput(input) {
|
|
513
|
-
try {
|
|
514
|
-
// Parse and re-stringify with formatting
|
|
515
|
-
const parsed = JSON.parse(input);
|
|
516
|
-
return JSON.stringify(parsed, null, 2);
|
|
517
|
-
}
|
|
518
|
-
catch {
|
|
519
|
-
// If parsing fails, return the string as-is
|
|
520
|
-
return input;
|
|
498
|
+
// Complete final message if content remains
|
|
499
|
+
if (currentMessageId && fullResponse) {
|
|
500
|
+
this._emitMessageComplete(currentMessageId, fullResponse);
|
|
521
501
|
}
|
|
502
|
+
return processResult;
|
|
522
503
|
}
|
|
523
504
|
/**
|
|
524
|
-
*
|
|
525
|
-
* @param modelEvent The model event containing tool call information
|
|
505
|
+
* Emits a message_complete event.
|
|
526
506
|
*/
|
|
527
|
-
|
|
528
|
-
const toolCallId = modelEvent.toolCallId;
|
|
529
|
-
const toolName = modelEvent.toolName;
|
|
530
|
-
const toolInput = modelEvent.input;
|
|
507
|
+
_emitMessageComplete(messageId, content) {
|
|
531
508
|
this._agentEvent.emit({
|
|
532
|
-
type: '
|
|
533
|
-
data: {
|
|
534
|
-
callId: toolCallId,
|
|
535
|
-
toolName,
|
|
536
|
-
input: this._formatToolInput(toolInput)
|
|
537
|
-
}
|
|
509
|
+
type: 'message_complete',
|
|
510
|
+
data: { messageId, content }
|
|
538
511
|
});
|
|
539
512
|
}
|
|
540
513
|
/**
|
|
541
|
-
* Handles tool
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
const isError = toolCallOutput.rawItem.type === 'function_call_result' &&
|
|
552
|
-
toolCallOutput.rawItem.status === 'incomplete';
|
|
553
|
-
const toolName = toolCallOutput.rawItem.type === 'function_call_result'
|
|
554
|
-
? toolCallOutput.rawItem.name
|
|
555
|
-
: 'Unknown Tool';
|
|
514
|
+
* Handles tool-result stream parts.
|
|
515
|
+
*/
|
|
516
|
+
_handleToolResult(part) {
|
|
517
|
+
const output = typeof part.output === 'string'
|
|
518
|
+
? part.output
|
|
519
|
+
: JSON.stringify(part.output, null, 2);
|
|
520
|
+
const isError = typeof part.output === 'object' &&
|
|
521
|
+
part.output !== null &&
|
|
522
|
+
'success' in part.output &&
|
|
523
|
+
part.output.success === false;
|
|
556
524
|
this._agentEvent.emit({
|
|
557
525
|
type: 'tool_call_complete',
|
|
558
526
|
data: {
|
|
559
|
-
callId,
|
|
560
|
-
toolName,
|
|
561
|
-
output
|
|
527
|
+
callId: part.toolCallId,
|
|
528
|
+
toolName: part.toolName,
|
|
529
|
+
output,
|
|
562
530
|
isError
|
|
563
531
|
}
|
|
564
532
|
});
|
|
565
533
|
}
|
|
566
534
|
/**
|
|
567
|
-
* Handles approval
|
|
568
|
-
* @param interruption The tool approval interruption item
|
|
535
|
+
* Handles tool-approval-request stream parts.
|
|
569
536
|
*/
|
|
570
|
-
async
|
|
571
|
-
const
|
|
572
|
-
const toolInput = interruption.rawItem.arguments || '{}';
|
|
573
|
-
const interruptionId = `int-${Date.now()}-${Math.random()}`;
|
|
574
|
-
const callId = interruption.rawItem.type === 'function_call'
|
|
575
|
-
? interruption.rawItem.callId
|
|
576
|
-
: undefined;
|
|
577
|
-
this._pendingApprovals.set(interruptionId, { interruption });
|
|
537
|
+
async _handleApprovalRequest(part, result) {
|
|
538
|
+
const { approvalId, toolCall } = part;
|
|
578
539
|
this._agentEvent.emit({
|
|
579
|
-
type: '
|
|
540
|
+
type: 'tool_approval_request',
|
|
580
541
|
data: {
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
542
|
+
approvalId,
|
|
543
|
+
toolCallId: toolCall.toolCallId,
|
|
544
|
+
toolName: toolCall.toolName,
|
|
545
|
+
args: toolCall.input
|
|
585
546
|
}
|
|
586
547
|
});
|
|
548
|
+
const approved = await this._waitForApproval(approvalId);
|
|
549
|
+
result.approvalProcessed = true;
|
|
550
|
+
result.approvalResponse = {
|
|
551
|
+
role: 'tool',
|
|
552
|
+
content: [
|
|
553
|
+
{
|
|
554
|
+
type: 'tool-approval-response',
|
|
555
|
+
approvalId,
|
|
556
|
+
approved
|
|
557
|
+
}
|
|
558
|
+
]
|
|
559
|
+
};
|
|
587
560
|
}
|
|
588
561
|
/**
|
|
589
|
-
*
|
|
590
|
-
* @param
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
interruptionId,
|
|
601
|
-
toolName,
|
|
602
|
-
toolInput: this._formatToolInput(toolInput)
|
|
603
|
-
};
|
|
604
|
-
});
|
|
605
|
-
this._agentEvent.emit({
|
|
606
|
-
type: 'grouped_approval_required',
|
|
607
|
-
data: {
|
|
608
|
-
groupId,
|
|
609
|
-
approvals
|
|
610
|
-
}
|
|
562
|
+
* Waits for user approval of a tool call.
|
|
563
|
+
* @param approvalId The approval ID to wait for
|
|
564
|
+
* @returns Promise that resolves to true if approved, false if rejected
|
|
565
|
+
*/
|
|
566
|
+
_waitForApproval(approvalId) {
|
|
567
|
+
return new Promise(resolve => {
|
|
568
|
+
this._pendingApprovals.set(approvalId, {
|
|
569
|
+
resolve: (approved) => {
|
|
570
|
+
resolve(approved);
|
|
571
|
+
}
|
|
572
|
+
});
|
|
611
573
|
});
|
|
612
574
|
}
|
|
575
|
+
/**
|
|
576
|
+
* Formats tool input for display by pretty-printing JSON strings.
|
|
577
|
+
* @param input The tool input string to format
|
|
578
|
+
* @returns Pretty-printed JSON string
|
|
579
|
+
*/
|
|
580
|
+
_formatToolInput(input) {
|
|
581
|
+
try {
|
|
582
|
+
const parsed = JSON.parse(input);
|
|
583
|
+
return JSON.stringify(parsed, null, 2);
|
|
584
|
+
}
|
|
585
|
+
catch {
|
|
586
|
+
return input;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
613
589
|
/**
|
|
614
590
|
* Checks if the current provider supports tool calling.
|
|
615
591
|
* @returns True if the provider supports tool calling, false otherwise
|
|
@@ -678,17 +654,24 @@ Guidelines:
|
|
|
678
654
|
- End with a brief summary of accomplishments
|
|
679
655
|
- Use natural, conversational tone throughout
|
|
680
656
|
|
|
681
|
-
COMMAND
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
657
|
+
PRIMARY TOOL USAGE - COMMAND-BASED OPERATIONS:
|
|
658
|
+
Most operations in JupyterLab should be performed using the command system:
|
|
659
|
+
1. Use 'discover_commands' to find available commands and their metadata
|
|
660
|
+
2. Use 'execute_command' to perform the actual operation
|
|
661
|
+
|
|
662
|
+
COMMAND DISCOVERY WORKFLOW:
|
|
663
|
+
- For file and notebook operations, use query 'jupyterlab-ai-commands' to discover the curated set of AI commands (~17 commands for file/notebook/directory operations)
|
|
664
|
+
- For other JupyterLab operations (terminal, launcher, UI), use specific keywords like 'terminal', 'launcher', etc.
|
|
665
|
+
- IMPORTANT: Always use 'jupyterlab-ai-commands' as the query for file/notebook tasks - this returns a focused set of commands instead of 100+ generic JupyterLab commands
|
|
686
666
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
-
|
|
690
|
-
-
|
|
691
|
-
-
|
|
667
|
+
KERNEL PREFERENCE FOR NOTEBOOKS AND CONSOLES:
|
|
668
|
+
When creating notebooks or consoles for a specific programming language, use the 'kernelPreference' argument to specify the kernel:
|
|
669
|
+
- To specify by language: { "kernelPreference": { "language": "python" } } or { "kernelPreference": { "language": "julia" } }
|
|
670
|
+
- To specify by kernel name: { "kernelPreference": { "name": "python3" } } or { "kernelPreference": { "name": "julia-1.10" } }
|
|
671
|
+
- Example: execute_command with commandId="notebook:create-new" and args={ "kernelPreference": { "language": "python" } }
|
|
672
|
+
- Example: execute_command with commandId="console:create" and args={ "kernelPreference": { "name": "python3" } }
|
|
673
|
+
- Common kernel names: "python3" (Python), "julia-1.10" (Julia), "ir" (R), "xpython" (xeus-python)
|
|
674
|
+
- If unsure of exact kernel name, prefer using "language" which will match any kernel supporting that language
|
|
692
675
|
`;
|
|
693
676
|
return baseSystemPrompt + progressReportingPrompt;
|
|
694
677
|
}
|
|
@@ -699,18 +682,16 @@ TOOL SELECTION GUIDELINES:
|
|
|
699
682
|
_secretsManager;
|
|
700
683
|
_selectedToolNames;
|
|
701
684
|
_agent;
|
|
702
|
-
_runner;
|
|
703
685
|
_history;
|
|
704
|
-
|
|
686
|
+
_mcpTools;
|
|
705
687
|
_isInitializing;
|
|
706
688
|
_controller;
|
|
707
|
-
_pendingApprovals;
|
|
708
|
-
_interruptedState;
|
|
709
689
|
_agentEvent;
|
|
710
690
|
_tokenUsage;
|
|
711
691
|
_tokenUsageChanged;
|
|
712
692
|
_activeProvider = '';
|
|
713
693
|
_activeProviderChanged = new Signal(this);
|
|
694
|
+
_pendingApprovals = new Map();
|
|
714
695
|
}
|
|
715
696
|
var Private;
|
|
716
697
|
(function (Private) {
|