@jupyterlite/ai 0.8.1 → 0.9.0-a1

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 (162) hide show
  1. package/lib/agent.d.ts +243 -0
  2. package/lib/agent.js +627 -0
  3. package/lib/chat-model.d.ts +195 -0
  4. package/lib/chat-model.js +591 -0
  5. package/lib/completion/completion-provider.d.ts +93 -0
  6. package/lib/completion/completion-provider.js +235 -0
  7. package/lib/completion/index.d.ts +1 -0
  8. package/lib/completion/index.js +1 -0
  9. package/lib/components/clear-button.d.ts +18 -0
  10. package/lib/components/clear-button.js +31 -0
  11. package/lib/components/index.d.ts +3 -0
  12. package/lib/components/index.js +3 -0
  13. package/lib/components/model-select.d.ts +19 -0
  14. package/lib/components/model-select.js +154 -0
  15. package/lib/components/stop-button.d.ts +3 -3
  16. package/lib/components/stop-button.js +8 -9
  17. package/lib/components/token-usage-display.d.ts +45 -0
  18. package/lib/components/token-usage-display.js +74 -0
  19. package/lib/components/tool-select.d.ts +27 -0
  20. package/lib/components/tool-select.js +130 -0
  21. package/lib/icons.d.ts +3 -1
  22. package/lib/icons.js +10 -13
  23. package/lib/index.d.ts +5 -5
  24. package/lib/index.js +341 -169
  25. package/lib/mcp/browser.d.ts +68 -0
  26. package/lib/mcp/browser.js +132 -0
  27. package/lib/models/settings-model.d.ts +70 -0
  28. package/lib/models/settings-model.js +296 -0
  29. package/lib/providers/built-in-providers.d.ts +9 -0
  30. package/lib/providers/built-in-providers.js +266 -0
  31. package/lib/providers/models.d.ts +37 -0
  32. package/lib/providers/models.js +28 -0
  33. package/lib/providers/provider-registry.d.ts +94 -0
  34. package/lib/providers/provider-registry.js +155 -0
  35. package/lib/tokens.d.ts +167 -86
  36. package/lib/tokens.js +25 -12
  37. package/lib/tools/commands.d.ts +11 -0
  38. package/lib/tools/commands.js +126 -0
  39. package/lib/tools/file.d.ts +27 -0
  40. package/lib/tools/file.js +262 -0
  41. package/lib/tools/notebook.d.ts +41 -0
  42. package/lib/tools/notebook.js +779 -0
  43. package/lib/tools/tool-registry.d.ts +35 -0
  44. package/lib/tools/tool-registry.js +55 -0
  45. package/lib/widgets/ai-settings.d.ts +49 -0
  46. package/lib/widgets/ai-settings.js +580 -0
  47. package/lib/widgets/chat-wrapper.d.ts +144 -0
  48. package/lib/widgets/chat-wrapper.js +390 -0
  49. package/lib/widgets/provider-config-dialog.d.ts +14 -0
  50. package/lib/widgets/provider-config-dialog.js +112 -0
  51. package/package.json +151 -40
  52. package/schema/settings-model.json +159 -0
  53. package/src/agent.ts +836 -0
  54. package/src/chat-model.ts +771 -0
  55. package/src/completion/completion-provider.ts +346 -0
  56. package/src/completion/index.ts +1 -0
  57. package/src/components/clear-button.tsx +56 -0
  58. package/src/components/index.ts +3 -0
  59. package/src/components/model-select.tsx +245 -0
  60. package/src/components/stop-button.tsx +11 -11
  61. package/src/components/token-usage-display.tsx +130 -0
  62. package/src/components/tool-select.tsx +218 -0
  63. package/src/icons.ts +12 -14
  64. package/src/index.ts +485 -232
  65. package/src/mcp/browser.ts +213 -0
  66. package/src/models/settings-model.ts +413 -0
  67. package/src/providers/built-in-providers.ts +294 -0
  68. package/src/providers/models.ts +79 -0
  69. package/src/providers/provider-registry.ts +189 -0
  70. package/src/tokens.ts +217 -90
  71. package/src/tools/commands.ts +151 -0
  72. package/src/tools/file.ts +307 -0
  73. package/src/tools/notebook.ts +987 -0
  74. package/src/tools/tool-registry.ts +63 -0
  75. package/src/types.d.ts +4 -0
  76. package/src/widgets/ai-settings.tsx +1233 -0
  77. package/src/widgets/chat-wrapper.tsx +543 -0
  78. package/src/widgets/provider-config-dialog.tsx +272 -0
  79. package/style/base.css +335 -14
  80. package/style/icons/jupyternaut-lite.svg +1 -1
  81. package/lib/base-completer.d.ts +0 -49
  82. package/lib/base-completer.js +0 -14
  83. package/lib/chat-handler.d.ts +0 -56
  84. package/lib/chat-handler.js +0 -201
  85. package/lib/completion-provider.d.ts +0 -34
  86. package/lib/completion-provider.js +0 -32
  87. package/lib/default-prompts.d.ts +0 -2
  88. package/lib/default-prompts.js +0 -31
  89. package/lib/default-providers/Anthropic/completer.d.ts +0 -12
  90. package/lib/default-providers/Anthropic/completer.js +0 -46
  91. package/lib/default-providers/Anthropic/settings-schema.json +0 -70
  92. package/lib/default-providers/ChromeAI/completer.d.ts +0 -12
  93. package/lib/default-providers/ChromeAI/completer.js +0 -56
  94. package/lib/default-providers/ChromeAI/instructions.d.ts +0 -6
  95. package/lib/default-providers/ChromeAI/instructions.js +0 -42
  96. package/lib/default-providers/ChromeAI/settings-schema.json +0 -18
  97. package/lib/default-providers/Gemini/completer.d.ts +0 -12
  98. package/lib/default-providers/Gemini/completer.js +0 -48
  99. package/lib/default-providers/Gemini/instructions.d.ts +0 -2
  100. package/lib/default-providers/Gemini/instructions.js +0 -9
  101. package/lib/default-providers/Gemini/settings-schema.json +0 -64
  102. package/lib/default-providers/MistralAI/completer.d.ts +0 -13
  103. package/lib/default-providers/MistralAI/completer.js +0 -52
  104. package/lib/default-providers/MistralAI/instructions.d.ts +0 -2
  105. package/lib/default-providers/MistralAI/instructions.js +0 -18
  106. package/lib/default-providers/MistralAI/settings-schema.json +0 -75
  107. package/lib/default-providers/Ollama/completer.d.ts +0 -12
  108. package/lib/default-providers/Ollama/completer.js +0 -43
  109. package/lib/default-providers/Ollama/instructions.d.ts +0 -2
  110. package/lib/default-providers/Ollama/instructions.js +0 -70
  111. package/lib/default-providers/Ollama/settings-schema.json +0 -143
  112. package/lib/default-providers/OpenAI/completer.d.ts +0 -12
  113. package/lib/default-providers/OpenAI/completer.js +0 -43
  114. package/lib/default-providers/OpenAI/settings-schema.json +0 -628
  115. package/lib/default-providers/WebLLM/completer.d.ts +0 -21
  116. package/lib/default-providers/WebLLM/completer.js +0 -127
  117. package/lib/default-providers/WebLLM/instructions.d.ts +0 -6
  118. package/lib/default-providers/WebLLM/instructions.js +0 -32
  119. package/lib/default-providers/WebLLM/settings-schema.json +0 -19
  120. package/lib/default-providers/index.d.ts +0 -2
  121. package/lib/default-providers/index.js +0 -179
  122. package/lib/provider.d.ts +0 -144
  123. package/lib/provider.js +0 -412
  124. package/lib/settings/base.json +0 -7
  125. package/lib/settings/index.d.ts +0 -3
  126. package/lib/settings/index.js +0 -3
  127. package/lib/settings/panel.d.ts +0 -226
  128. package/lib/settings/panel.js +0 -510
  129. package/lib/settings/textarea.d.ts +0 -2
  130. package/lib/settings/textarea.js +0 -18
  131. package/lib/settings/utils.d.ts +0 -2
  132. package/lib/settings/utils.js +0 -4
  133. package/lib/types/ai-model.d.ts +0 -24
  134. package/lib/types/ai-model.js +0 -5
  135. package/schema/chat.json +0 -28
  136. package/schema/provider-registry.json +0 -29
  137. package/schema/system-prompts.json +0 -22
  138. package/src/base-completer.ts +0 -75
  139. package/src/chat-handler.ts +0 -262
  140. package/src/completion-provider.ts +0 -64
  141. package/src/default-prompts.ts +0 -33
  142. package/src/default-providers/Anthropic/completer.ts +0 -59
  143. package/src/default-providers/ChromeAI/completer.ts +0 -73
  144. package/src/default-providers/ChromeAI/instructions.ts +0 -45
  145. package/src/default-providers/Gemini/completer.ts +0 -61
  146. package/src/default-providers/Gemini/instructions.ts +0 -9
  147. package/src/default-providers/MistralAI/completer.ts +0 -69
  148. package/src/default-providers/MistralAI/instructions.ts +0 -18
  149. package/src/default-providers/Ollama/completer.ts +0 -54
  150. package/src/default-providers/Ollama/instructions.ts +0 -70
  151. package/src/default-providers/OpenAI/completer.ts +0 -54
  152. package/src/default-providers/WebLLM/completer.ts +0 -151
  153. package/src/default-providers/WebLLM/instructions.ts +0 -33
  154. package/src/default-providers/index.ts +0 -211
  155. package/src/global.d.ts +0 -9
  156. package/src/provider.ts +0 -514
  157. package/src/settings/index.ts +0 -3
  158. package/src/settings/panel.tsx +0 -773
  159. package/src/settings/textarea.tsx +0 -33
  160. package/src/settings/utils.ts +0 -5
  161. package/src/types/ai-model.ts +0 -37
  162. package/src/types/service-worker.d.ts +0 -6
package/src/tokens.ts CHANGED
@@ -1,158 +1,285 @@
1
- import { BaseChatModel } from '@langchain/core/language_models/chat_models';
2
- import { ReadonlyPartialJSONObject, Token } from '@lumino/coreutils';
1
+ import { Token } from '@lumino/coreutils';
3
2
  import { ISignal } from '@lumino/signaling';
4
- import { JSONSchema7 } from 'json-schema';
3
+ import { FunctionTool } from '@openai/agents';
4
+ import { LanguageModel } from 'ai';
5
+ import { AgentManager } from './agent';
6
+ import type { AISettingsModel } from './models/settings-model';
7
+ import type { IModelOptions } from './providers/models';
5
8
 
6
- import { IBaseCompleter } from './base-completer';
7
- import { AIChatModel, AICompleter } from './types/ai-model';
9
+ /**
10
+ * Type definition for a tool
11
+ */
12
+ export type ITool = FunctionTool<any, any, any>;
13
+
14
+ /**
15
+ * Interface for token usage statistics from AI model interactions
16
+ */
17
+ export interface ITokenUsage {
18
+ /**
19
+ * Number of input tokens consumed (prompt tokens)
20
+ */
21
+ inputTokens: number;
22
+
23
+ /**
24
+ * Number of output tokens generated (completion tokens)
25
+ */
26
+ outputTokens: number;
27
+ }
28
+
29
+ /**
30
+ * Interface for a named tool (tool with a name identifier)
31
+ */
32
+ export interface INamedTool {
33
+ /**
34
+ * The unique name of the tool
35
+ */
36
+ name: string;
37
+ /**
38
+ * The tool instance
39
+ */
40
+ tool: ITool;
41
+ }
8
42
 
9
- export const PLUGIN_IDS = {
10
- chat: '@jupyterlite/ai:chat',
11
- chatCommandRegistry: '@jupyterlite/ai:autocompletion-registry',
12
- completer: '@jupyterlite/ai:completer',
13
- providerRegistry: '@jupyterlite/ai:provider-registry',
14
- settingsConnector: '@jupyterlite/ai:settings-connector',
15
- systemPrompts: '@jupyterlite/ai:system-prompts'
16
- };
43
+ /**
44
+ * The tool registry interface for managing AI tools
45
+ */
46
+ export interface IToolRegistry {
47
+ /**
48
+ * The registered tools as a record (name -> tool mapping).
49
+ */
50
+ readonly tools: Record<string, ITool>;
51
+
52
+ /**
53
+ * The registered named tools array.
54
+ */
55
+ readonly namedTools: INamedTool[];
56
+
57
+ /**
58
+ * A signal triggered when the tools have changed.
59
+ */
60
+ readonly toolsChanged: ISignal<IToolRegistry, void>;
61
+
62
+ /**
63
+ * Add a new tool to the registry.
64
+ */
65
+ add(name: string, tool: ITool): void;
66
+
67
+ /**
68
+ * Get a tool for a given name.
69
+ * Return null if the name is not provided or if there is no registered tool with the
70
+ * given name.
71
+ */
72
+ get(name: string | null): ITool | null;
73
+
74
+ /**
75
+ * Remove a tool from the registry by name.
76
+ */
77
+ remove(name: string): boolean;
78
+ }
79
+
80
+ /**
81
+ * The tool registry token.
82
+ */
83
+ export const IToolRegistry = new Token<IToolRegistry>(
84
+ '@jupyterlite/ai:tool-registry',
85
+ 'Tool registry for AI agent functionality'
86
+ );
87
+
88
+ /**
89
+ * Token for the chat provider registry.
90
+ */
91
+ export const IChatProviderRegistry = new Token<IChatProviderRegistry>(
92
+ '@jupyterlite/ai:chat-provider-registry',
93
+ 'Registry for chat AI providers'
94
+ );
17
95
 
18
- export type ModelRole = 'chat' | 'completer';
96
+ /**
97
+ * Token for the completion provider registry.
98
+ */
99
+ export const ICompletionProviderRegistry =
100
+ new Token<ICompletionProviderRegistry>(
101
+ '@jupyterlite/ai:completion-provider-registry',
102
+ 'Registry for completion providers'
103
+ );
19
104
 
20
- export interface IDict<T = any> {
21
- [key: string]: T;
105
+ /**
106
+ * Interface for a provider factory function that creates chat models
107
+ */
108
+ export interface IChatProviderFactory {
109
+ (options: IModelOptions): any; // Returns the model instance for @openai/agents
22
110
  }
23
111
 
24
- export interface IType<T> {
25
- new (...args: any[]): T;
112
+ /**
113
+ * Interface for a provider factory function that creates completion models
114
+ */
115
+ export interface ICompletionProviderFactory {
116
+ (options: IModelOptions): LanguageModel;
26
117
  }
27
118
 
28
119
  /**
29
- * The provider interface.
120
+ * Base information about a registered provider
30
121
  */
31
- export interface IAIProvider {
122
+ export interface IBaseProviderInfo {
32
123
  /**
33
- * The name of the provider.
124
+ * Unique identifier for the provider
34
125
  */
35
- name: string;
126
+ id: string;
127
+
36
128
  /**
37
- * The chat model class to use.
129
+ * Display name for the provider
38
130
  */
39
- chat?: IType<BaseChatModel>;
131
+ name: string;
132
+
40
133
  /**
41
- * The completer class to use.
134
+ * Whether this provider requires an API key
42
135
  */
43
- completer?: IType<IBaseCompleter>;
136
+ requiresApiKey: boolean;
137
+
44
138
  /**
45
- * the settings schema for the provider.
139
+ * Default model names for this provider
46
140
  */
47
- settingsSchema?: any;
141
+ defaultModels: string[];
142
+
48
143
  /**
49
- * The instructions to be displayed in the settings, as helper to use the provider.
50
- * A markdown renderer is used to render the instructions.
144
+ * Whether this provider supports custom base URLs
51
145
  */
52
- instructions?: string;
146
+ supportsBaseURL?: boolean;
147
+
53
148
  /**
54
- * A function that extract the error message from the provider API error.
55
- * Default to `(error) => error.message`.
149
+ * Whether this provider supports custom headers
56
150
  */
57
- errorMessage?: (error: any) => string;
151
+ supportsHeaders?: boolean;
152
+
58
153
  /**
59
- * Compatibility check function, to determine if the provider is compatible with the
60
- * current environment.
154
+ * Whether this provider supports tool calling
61
155
  */
62
- compatibilityCheck?: () => Promise<string | null>;
156
+ supportsToolCalling?: boolean;
157
+
63
158
  /**
64
- * Whether to expose or not the chat model.
65
- *
66
- * ### CAUTION
67
- * This flag will expose the whole chat model API, which may contain private keys.
68
- * Be sure to use it with a model that does not expose sensitive information in the
69
- * API.
159
+ * Additional provider-specific configuration schema
70
160
  */
71
- exposeChatModel?: boolean;
161
+ customSettings?: Record<string, any>;
72
162
  }
73
163
 
74
164
  /**
75
- * The provider registry interface.
165
+ * Information about a chat provider
76
166
  */
77
- export interface IAIProviderRegistry {
167
+ export interface IChatProviderInfo extends IBaseProviderInfo {
78
168
  /**
79
- * Get the list of provider names.
169
+ * Factory function for creating chat models
80
170
  */
81
- readonly providers: string[];
171
+ factory: IChatProviderFactory;
172
+ }
173
+
174
+ /**
175
+ * Information about a completion provider
176
+ */
177
+ export interface ICompletionProviderInfo extends IBaseProviderInfo {
82
178
  /**
83
- * Add a new provider.
179
+ * Factory function for creating completion models
84
180
  */
85
- add(provider: IAIProvider): void;
181
+ factory: ICompletionProviderFactory;
182
+ }
183
+
184
+ /**
185
+ * Registry for chat AI providers
186
+ */
187
+ export interface IChatProviderRegistry {
86
188
  /**
87
- * Get the current provider name.
189
+ * The registered providers as a record (id -> info mapping).
88
190
  */
89
- currentName(role: ModelRole): string;
191
+ readonly providers: Record<string, IChatProviderInfo>;
192
+
90
193
  /**
91
- * Get the current completer of the completion provider.
194
+ * A signal triggered when providers have changed.
92
195
  */
93
- currentCompleter: AICompleter | null;
196
+ readonly providersChanged: ISignal<IChatProviderRegistry, void>;
197
+
94
198
  /**
95
- * Getter/setter for the completer system prompt.
199
+ * Register a new chat provider.
96
200
  */
97
- completerSystemPrompt: string;
201
+ registerProvider(info: IChatProviderInfo): void;
202
+
98
203
  /**
99
- * Get the current llm chat model.
204
+ * Unregister a chat provider.
100
205
  */
101
- currentChatModel: AIChatModel | null;
206
+ unregisterProvider(id: string): boolean;
207
+
102
208
  /**
103
- * Getter/setter for the chat system prompt.
209
+ * Get provider info by id.
104
210
  */
105
- chatSystemPrompt: string;
211
+ getProviderInfo(id: string): IChatProviderInfo | null;
212
+
106
213
  /**
107
- * Get the settings schema of a given provider.
214
+ * Create a chat model instance for the given provider.
108
215
  */
109
- getSettingsSchema(provider: string): JSONSchema7;
216
+ createChatModel(id: string, options: IModelOptions): any | null;
217
+
110
218
  /**
111
- * Get the instructions of a given provider.
219
+ * Get all available provider IDs.
112
220
  */
113
- getInstructions(provider: string): string | undefined;
221
+ getAvailableProviders(): string[];
222
+ }
223
+
224
+ /**
225
+ * Registry for completion providers
226
+ */
227
+ export interface ICompletionProviderRegistry {
114
228
  /**
115
- * Get the compatibility check function of a given provider.
229
+ * The registered providers as a record (id -> info mapping).
116
230
  */
117
- getCompatibilityCheck(
118
- provider: string
119
- ): (() => Promise<string | null>) | undefined;
231
+ readonly providers: Record<string, ICompletionProviderInfo>;
232
+
120
233
  /**
121
- * Format an error message from the current provider.
234
+ * A signal triggered when providers have changed.
122
235
  */
123
- formatErrorMessage(error: any): string;
236
+ readonly providersChanged: ISignal<ICompletionProviderRegistry, void>;
237
+
124
238
  /**
125
- * Set the completer provider.
126
- * Creates the provider if the name has changed, otherwise only updates its config.
127
- *
128
- * @param options - An object with the name and the settings of the provider to use.
239
+ * Register a new completion provider.
129
240
  */
130
- setCompleterProvider(settings: ReadonlyPartialJSONObject): void;
241
+ registerProvider(info: ICompletionProviderInfo): void;
242
+
131
243
  /**
132
- * Set the chat provider.
133
- * Creates the provider if the name has changed, otherwise only updates its config.
134
- *
135
- * @param options - An object with the name and the settings of the provider to use.
244
+ * Unregister a completion provider.
136
245
  */
137
- setChatProvider(settings: ReadonlyPartialJSONObject): void;
246
+ unregisterProvider(id: string): boolean;
247
+
138
248
  /**
139
- * A signal emitting when the provider or its settings has changed.
249
+ * Get provider info by id.
140
250
  */
141
- readonly providerChanged: ISignal<IAIProviderRegistry, ModelRole>;
251
+ getProviderInfo(id: string): ICompletionProviderInfo | null;
252
+
142
253
  /**
143
- * Get the current chat error;
254
+ * Create a completion model instance for the given provider.
144
255
  */
145
- readonly chatError: string;
256
+ createCompletionModel(
257
+ id: string,
258
+ options: IModelOptions
259
+ ): LanguageModel | null;
260
+
146
261
  /**
147
- * get the current completer error.
262
+ * Get all available provider IDs.
148
263
  */
149
- readonly completerError: string;
264
+ getAvailableProviders(): string[];
150
265
  }
151
266
 
152
267
  /**
153
- * The provider registry token.
268
+ * Token for the AI settings model.
154
269
  */
155
- export const IAIProviderRegistry = new Token<IAIProviderRegistry>(
156
- '@jupyterlite/ai:provider-registry',
157
- 'Provider for chat and completion LLM provider'
270
+ export const IAISettingsModel = new Token<AISettingsModel>(
271
+ '@jupyterlite/ai:IAISettingsModel'
158
272
  );
273
+
274
+ /**
275
+ * Token for the agent manager.
276
+ */
277
+ export const IAgentManager = new Token<AgentManager>(
278
+ '@jupyterlite/ai:agent-manager'
279
+ );
280
+
281
+ /**
282
+ * The string that replaces a secret key in settings.
283
+ */
284
+ export const SECRETS_NAMESPACE = '@jupyterlite/ai:providers';
285
+ export const SECRETS_REPLACEMENT = '***';
@@ -0,0 +1,151 @@
1
+ import { CommandRegistry } from '@lumino/commands';
2
+ import { tool } from '@openai/agents';
3
+ import { z } from 'zod';
4
+ import { ITool } from '../tokens';
5
+ import { AISettingsModel } from '../models/settings-model';
6
+
7
+ /**
8
+ * Create a tool to discover all available commands and their metadata
9
+ */
10
+ export function createDiscoverCommandsTool(commands: CommandRegistry): ITool {
11
+ return tool({
12
+ name: 'discover_commands',
13
+ description:
14
+ 'Discover all available JupyterLab commands with their metadata, arguments, and descriptions',
15
+ parameters: z.object({
16
+ // currently unused, but could be used to filter commands by a search term
17
+ query: z
18
+ .string()
19
+ .optional()
20
+ .nullable()
21
+ .describe('Optional search query to filter commands')
22
+ }),
23
+ execute: async () => {
24
+ try {
25
+ const commandList: Array<{
26
+ id: string;
27
+ label?: string;
28
+ caption?: string;
29
+ description?: string;
30
+ args?: any;
31
+ }> = [];
32
+
33
+ // Get all command IDs
34
+ const commandIds = commands.listCommands();
35
+
36
+ for (const id of commandIds) {
37
+ try {
38
+ // Get command metadata using various CommandRegistry methods
39
+ const description = await commands.describedBy(id);
40
+ const label = commands.label(id);
41
+ const caption = commands.caption(id);
42
+ const usage = commands.usage(id);
43
+
44
+ commandList.push({
45
+ id,
46
+ label: label || undefined,
47
+ caption: caption || undefined,
48
+ description: usage || undefined,
49
+ args: description?.args || undefined
50
+ });
51
+ } catch (error) {
52
+ // Some commands might not have descriptions, skip them
53
+ commandList.push({ id });
54
+ }
55
+ }
56
+
57
+ return {
58
+ success: true,
59
+ commandCount: commandList.length,
60
+ commands: commandList
61
+ };
62
+ } catch (error) {
63
+ return {
64
+ success: false,
65
+ error: `Failed to discover commands: ${error instanceof Error ? error.message : String(error)}`
66
+ };
67
+ }
68
+ }
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Create a tool to execute a specific JupyterLab command
74
+ */
75
+ export function createExecuteCommandTool(
76
+ commands: CommandRegistry,
77
+ settingsModel: AISettingsModel
78
+ ): ITool {
79
+ return tool({
80
+ name: 'execute_command',
81
+ description:
82
+ 'Execute a specific JupyterLab command with optional arguments',
83
+ parameters: z.object({
84
+ commandId: z.string().describe('The ID of the command to execute'),
85
+ args: z
86
+ .any()
87
+ .optional()
88
+ .describe('Optional arguments to pass to the command')
89
+ }),
90
+ needsApproval: async (_context, { commandId }) => {
91
+ // Use configurable list of commands requiring approval
92
+ const commandsRequiringApproval =
93
+ settingsModel.config.commandsRequiringApproval;
94
+
95
+ return commandsRequiringApproval.some(
96
+ cmd => commandId.includes(cmd) || cmd.includes(commandId)
97
+ );
98
+ },
99
+ execute: async (input: { commandId: string; args?: any }) => {
100
+ const { commandId, args } = input;
101
+
102
+ // Check if command exists
103
+ if (!commands.hasCommand(commandId)) {
104
+ return {
105
+ success: false,
106
+ error: `Command '${commandId}' does not exist. Use 'discover_commands' to see available commands.`
107
+ };
108
+ }
109
+
110
+ try {
111
+ // Execute the command
112
+ const result = await commands.execute(commandId, args);
113
+
114
+ // Handle Widget objects specially (including subclasses like DocumentWidget)
115
+ let serializedResult;
116
+ if (
117
+ result &&
118
+ typeof result === 'object' &&
119
+ (result.constructor?.name?.includes('Widget') || result.id)
120
+ ) {
121
+ serializedResult = {
122
+ type: result.constructor?.name || 'Widget',
123
+ id: result.id,
124
+ title: result.title?.label || result.title,
125
+ className: result.className
126
+ };
127
+ } else {
128
+ // For other objects, try JSON serialization with fallback
129
+ try {
130
+ serializedResult = JSON.parse(JSON.stringify(result));
131
+ } catch {
132
+ serializedResult = result
133
+ ? '[Complex object - cannot serialize]'
134
+ : 'Command executed successfully';
135
+ }
136
+ }
137
+
138
+ return {
139
+ success: true,
140
+ commandId,
141
+ result: serializedResult
142
+ };
143
+ } catch (error) {
144
+ return {
145
+ success: false,
146
+ error: `Failed to execute command '${commandId}': ${error instanceof Error ? error.message : String(error)}`
147
+ };
148
+ }
149
+ }
150
+ });
151
+ }