@jupyterlite/ai 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +5 -214
  2. package/lib/agent.d.ts +58 -66
  3. package/lib/agent.js +274 -300
  4. package/lib/approval-buttons.d.ts +19 -82
  5. package/lib/approval-buttons.js +36 -289
  6. package/lib/chat-model-registry.d.ts +6 -0
  7. package/lib/chat-model-registry.js +4 -1
  8. package/lib/chat-model.d.ts +19 -54
  9. package/lib/chat-model.js +243 -303
  10. package/lib/components/clear-button.d.ts +6 -1
  11. package/lib/components/clear-button.js +8 -3
  12. package/lib/components/completion-status.d.ts +5 -0
  13. package/lib/components/completion-status.js +5 -4
  14. package/lib/components/model-select.d.ts +6 -1
  15. package/lib/components/model-select.js +9 -8
  16. package/lib/components/stop-button.d.ts +6 -1
  17. package/lib/components/stop-button.js +8 -3
  18. package/lib/components/token-usage-display.d.ts +5 -0
  19. package/lib/components/token-usage-display.js +2 -2
  20. package/lib/components/tool-select.d.ts +6 -1
  21. package/lib/components/tool-select.js +6 -5
  22. package/lib/index.js +58 -38
  23. package/lib/models/settings-model.d.ts +1 -1
  24. package/lib/providers/built-in-providers.js +38 -19
  25. package/lib/providers/models.d.ts +3 -3
  26. package/lib/providers/provider-registry.d.ts +3 -4
  27. package/lib/providers/provider-registry.js +1 -4
  28. package/lib/tokens.d.ts +5 -6
  29. package/lib/tools/commands.d.ts +2 -1
  30. package/lib/tools/commands.js +37 -46
  31. package/lib/tools/file.js +49 -73
  32. package/lib/tools/notebook.js +370 -445
  33. package/lib/widgets/ai-settings.d.ts +6 -0
  34. package/lib/widgets/ai-settings.js +72 -71
  35. package/lib/widgets/main-area-chat.d.ts +2 -0
  36. package/lib/widgets/main-area-chat.js +5 -2
  37. package/lib/widgets/provider-config-dialog.d.ts +2 -0
  38. package/lib/widgets/provider-config-dialog.js +34 -34
  39. package/package.json +12 -12
  40. package/src/agent.ts +342 -361
  41. package/src/approval-buttons.ts +43 -389
  42. package/src/chat-model-registry.ts +9 -1
  43. package/src/chat-model.ts +355 -370
  44. package/src/completion/completion-provider.ts +2 -3
  45. package/src/components/clear-button.tsx +16 -3
  46. package/src/components/completion-status.tsx +18 -4
  47. package/src/components/model-select.tsx +21 -8
  48. package/src/components/stop-button.tsx +16 -3
  49. package/src/components/token-usage-display.tsx +14 -2
  50. package/src/components/tool-select.tsx +23 -5
  51. package/src/index.ts +75 -36
  52. package/src/models/settings-model.ts +1 -1
  53. package/src/providers/built-in-providers.ts +38 -19
  54. package/src/providers/models.ts +3 -3
  55. package/src/providers/provider-registry.ts +4 -8
  56. package/src/tokens.ts +5 -6
  57. package/src/tools/commands.ts +39 -50
  58. package/src/tools/file.ts +49 -75
  59. package/src/tools/notebook.ts +451 -510
  60. package/src/widgets/ai-settings.tsx +153 -84
  61. package/src/widgets/main-area-chat.ts +8 -2
  62. package/src/widgets/provider-config-dialog.tsx +54 -41
  63. package/style/base.css +13 -73
  64. package/lib/mcp/browser.d.ts +0 -68
  65. package/lib/mcp/browser.js +0 -138
  66. package/src/mcp/browser.ts +0 -220
@@ -11,15 +11,17 @@ export const anthropicProvider = {
11
11
  name: 'Anthropic Claude',
12
12
  apiKeyRequirement: 'required',
13
13
  defaultModels: [
14
+ 'claude-opus-4-5',
15
+ 'claude-opus-4-5-20251101',
14
16
  'claude-sonnet-4-5',
15
17
  'claude-sonnet-4-5-20250929',
16
18
  'claude-haiku-4-5',
17
19
  'claude-haiku-4-5-20251001',
18
20
  'claude-opus-4-1',
19
- 'claude-opus-4-0',
20
- 'claude-sonnet-4-0',
21
21
  'claude-opus-4-1-20250805',
22
+ 'claude-opus-4-0',
22
23
  'claude-opus-4-20250514',
24
+ 'claude-sonnet-4-0',
23
25
  'claude-sonnet-4-20250514',
24
26
  'claude-3-7-sonnet-latest',
25
27
  'claude-3-7-sonnet-20250219',
@@ -53,8 +55,11 @@ export const googleProvider = {
53
55
  name: 'Google Generative AI',
54
56
  apiKeyRequirement: 'required',
55
57
  defaultModels: [
56
- 'gemini-2.5-flash',
58
+ 'gemini-3-pro-preview',
59
+ 'gemini-3-pro-image-preview',
60
+ 'gemini-3-flash-preview',
57
61
  'gemini-2.5-pro',
62
+ 'gemini-2.5-flash',
58
63
  'gemini-2.5-flash-image-preview',
59
64
  'gemini-2.5-flash-lite',
60
65
  'gemini-2.5-flash-lite-preview-09-2025',
@@ -79,6 +84,9 @@ export const googleProvider = {
79
84
  'gemini-1.5-pro-latest',
80
85
  'gemini-1.5-pro-001',
81
86
  'gemini-1.5-pro-002',
87
+ 'gemini-pro-latest',
88
+ 'gemini-flash-latest',
89
+ 'gemini-flash-lite-latest',
82
90
  'gemini-exp-1206',
83
91
  'gemma-3-12b-it',
84
92
  'gemma-3-27b-it'
@@ -143,12 +151,32 @@ export const openaiProvider = {
143
151
  name: 'OpenAI',
144
152
  apiKeyRequirement: 'required',
145
153
  defaultModels: [
146
- 'o1',
147
- 'o1-2024-12-17',
148
- 'o3-mini',
149
- 'o3-mini-2025-01-31',
154
+ 'gpt-5.2',
155
+ 'gpt-5.2-chat-latest',
156
+ 'gpt-5.2-pro',
157
+ 'gpt-5.1',
158
+ 'gpt-5.1-chat-latest',
159
+ 'gpt-5.1-codex',
160
+ 'gpt-5.1-codex-mini',
161
+ 'gpt-5.1-codex-max',
162
+ 'gpt-5',
163
+ 'gpt-5-2025-08-07',
164
+ 'gpt-5-chat-latest',
165
+ 'gpt-5-codex',
166
+ 'gpt-5-pro',
167
+ 'gpt-5-pro-2025-10-06',
168
+ 'gpt-5-mini',
169
+ 'gpt-5-mini-2025-08-07',
170
+ 'gpt-5-nano',
171
+ 'gpt-5-nano-2025-08-07',
150
172
  'o3',
151
173
  'o3-2025-04-16',
174
+ 'o3-mini',
175
+ 'o3-mini-2025-01-31',
176
+ 'o1',
177
+ 'o1-2024-12-17',
178
+ 'gpt-4.5-preview',
179
+ 'gpt-4.5-preview-2025-02-27',
152
180
  'gpt-4.1',
153
181
  'gpt-4.1-2025-04-14',
154
182
  'gpt-4.1-mini',
@@ -161,23 +189,14 @@ export const openaiProvider = {
161
189
  'gpt-4o-2024-11-20',
162
190
  'gpt-4o-mini',
163
191
  'gpt-4o-mini-2024-07-18',
192
+ 'chatgpt-4o-latest',
164
193
  'gpt-4-turbo',
165
194
  'gpt-4-turbo-2024-04-09',
166
195
  'gpt-4',
167
196
  'gpt-4-0613',
168
- 'gpt-4.5-preview',
169
- 'gpt-4.5-preview-2025-02-27',
170
- 'gpt-3.5-turbo-0125',
171
197
  'gpt-3.5-turbo',
172
- 'gpt-3.5-turbo-1106',
173
- 'chatgpt-4o-latest',
174
- 'gpt-5',
175
- 'gpt-5-2025-08-07',
176
- 'gpt-5-mini',
177
- 'gpt-5-mini-2025-08-07',
178
- 'gpt-5-nano',
179
- 'gpt-5-nano-2025-08-07',
180
- 'gpt-5-chat-latest'
198
+ 'gpt-3.5-turbo-0125',
199
+ 'gpt-3.5-turbo-1106'
181
200
  ],
182
201
  supportsBaseURL: true,
183
202
  supportsHeaders: true,
@@ -1,4 +1,4 @@
1
- import type { LanguageModelV2 } from '@ai-sdk/provider';
1
+ import type { LanguageModel } from 'ai';
2
2
  import type { IProviderRegistry } from '../tokens';
3
3
  /**
4
4
  * Configuration options for creating language models.
@@ -29,9 +29,9 @@ export interface IModelOptions {
29
29
  * Create a completion model using the provider registry.
30
30
  * Built-in providers are automatically registered during extension initialization.
31
31
  */
32
- export declare function createCompletionModel(options: IModelOptions, registry?: IProviderRegistry): LanguageModelV2;
32
+ export declare function createCompletionModel(options: IModelOptions, registry?: IProviderRegistry): LanguageModel;
33
33
  /**
34
34
  * Create a chat model using the provider registry.
35
35
  * Built-in providers are automatically registered during extension initialization.
36
36
  */
37
- export declare function createModel(options: IModelOptions, registry?: IProviderRegistry): import("@openai/agents-core").Model;
37
+ export declare function createModel(options: IModelOptions, registry?: IProviderRegistry): LanguageModel;
@@ -1,6 +1,5 @@
1
1
  import { ISignal } from '@lumino/signaling';
2
- import type { LanguageModelV2 } from '@ai-sdk/provider';
3
- import type { Model } from '@openai/agents';
2
+ import type { LanguageModel } from 'ai';
4
3
  import type { IModelOptions } from './models';
5
4
  import { IProviderInfo, IProviderRegistry } from '../tokens';
6
5
  /**
@@ -32,14 +31,14 @@ export declare class ProviderRegistry implements IProviderRegistry {
32
31
  * @param options Model configuration options
33
32
  * @returns Chat model instance or null if creation fails
34
33
  */
35
- createChatModel(id: string, options: IModelOptions): Model | null;
34
+ createChatModel(id: string, options: IModelOptions): LanguageModel | null;
36
35
  /**
37
36
  * Create a completion model instance using the specified provider
38
37
  * @param id Provider ID
39
38
  * @param options Model configuration options
40
39
  * @returns Language model instance or null if creation fails
41
40
  */
42
- createCompletionModel(id: string, options: IModelOptions): LanguageModelV2 | null;
41
+ createCompletionModel(id: string, options: IModelOptions): LanguageModel | null;
43
42
  /**
44
43
  * Get list of all available provider IDs
45
44
  * @returns Array of provider IDs
@@ -1,5 +1,4 @@
1
1
  import { Signal } from '@lumino/signaling';
2
- import { aisdk } from '@openai/agents-extensions';
3
2
  /**
4
3
  * Implementation of the provider registry
5
4
  */
@@ -46,9 +45,7 @@ export class ProviderRegistry {
46
45
  if (!provider) {
47
46
  return null;
48
47
  }
49
- const languageModel = provider.factory(options);
50
- // wrap with aisdk for compatibility with the agent framework
51
- return aisdk(languageModel);
48
+ return provider.factory(options);
52
49
  }
53
50
  /**
54
51
  * Create a completion model instance using the specified provider
package/lib/tokens.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { Token } from '@lumino/coreutils';
2
2
  import { ISignal } from '@lumino/signaling';
3
- import { FunctionTool, Model } from '@openai/agents';
4
- import { LanguageModelV2 } from '@ai-sdk/provider';
3
+ import type { Tool, LanguageModel } from 'ai';
5
4
  import { AgentManager } from './agent';
6
5
  import type { AISettingsModel } from './models/settings-model';
7
6
  import type { IModelOptions } from './providers/models';
@@ -19,7 +18,7 @@ export declare namespace CommandIds {
19
18
  /**
20
19
  * Type definition for a tool
21
20
  */
22
- export type ITool = FunctionTool<any, any, any>;
21
+ export type ITool = Tool;
23
22
  /**
24
23
  * Interface for token usage statistics from AI model interactions
25
24
  */
@@ -89,7 +88,7 @@ export declare const IProviderRegistry: Token<IProviderRegistry>;
89
88
  * Interface for a provider factory function that creates language models
90
89
  */
91
90
  export interface IProviderFactory {
92
- (options: IModelOptions): LanguageModelV2;
91
+ (options: IModelOptions): LanguageModel;
93
92
  }
94
93
  /**
95
94
  * Provider information
@@ -165,11 +164,11 @@ export interface IProviderRegistry {
165
164
  /**
166
165
  * Create a chat model instance for the given provider.
167
166
  */
168
- createChatModel(id: string, options: IModelOptions): Model | null;
167
+ createChatModel(id: string, options: IModelOptions): LanguageModel | null;
169
168
  /**
170
169
  * Create a completion model instance for the given provider.
171
170
  */
172
- createCompletionModel(id: string, options: IModelOptions): LanguageModelV2 | null;
171
+ createCompletionModel(id: string, options: IModelOptions): LanguageModel | null;
173
172
  /**
174
173
  * Get all available provider IDs.
175
174
  */
@@ -6,6 +6,7 @@ import { AISettingsModel } from '../models/settings-model';
6
6
  */
7
7
  export declare function createDiscoverCommandsTool(commands: CommandRegistry): ITool;
8
8
  /**
9
- * Create a tool to execute a specific JupyterLab command
9
+ * Create a tool to execute a specific JupyterLab command.
10
+ * Commands in the settings' commandsRequiringApproval list will need approval.
10
11
  */
11
12
  export declare function createExecuteCommandTool(commands: CommandRegistry, settingsModel: AISettingsModel): ITool;
@@ -1,14 +1,13 @@
1
- import { tool } from '@openai/agents';
1
+ import { tool } from 'ai';
2
2
  import { z } from 'zod';
3
3
  /**
4
4
  * Create a tool to discover all available commands and their metadata
5
5
  */
6
6
  export function createDiscoverCommandsTool(commands) {
7
7
  return tool({
8
- name: 'discover_commands',
8
+ title: 'Discover Commands',
9
9
  description: 'Discover all available JupyterLab commands with their metadata, arguments, and descriptions',
10
- parameters: z.object({
11
- // currently unused, but could be used to filter commands by a search term
10
+ inputSchema: z.object({
12
11
  query: z
13
12
  .string()
14
13
  .optional()
@@ -57,23 +56,23 @@ export function createDiscoverCommandsTool(commands) {
57
56
  });
58
57
  }
59
58
  /**
60
- * Create a tool to execute a specific JupyterLab command
59
+ * Create a tool to execute a specific JupyterLab command.
60
+ * Commands in the settings' commandsRequiringApproval list will need approval.
61
61
  */
62
62
  export function createExecuteCommandTool(commands, settingsModel) {
63
63
  return tool({
64
- name: 'execute_command',
64
+ title: 'Execute Command',
65
65
  description: 'Execute a specific JupyterLab command with optional arguments',
66
- parameters: z.object({
66
+ inputSchema: z.object({
67
67
  commandId: z.string().describe('The ID of the command to execute'),
68
68
  args: z
69
69
  .any()
70
70
  .optional()
71
71
  .describe('Optional arguments to pass to the command')
72
72
  }),
73
- needsApproval: async (context, { commandId }) => {
74
- // Use configurable list of commands requiring approval
75
- const commandsRequiringApproval = settingsModel.config.commandsRequiringApproval;
76
- return commandsRequiringApproval.some(cmd => commandId.includes(cmd) || cmd.includes(commandId));
73
+ needsApproval: (input) => {
74
+ const commandsRequiringApproval = settingsModel.config.commandsRequiringApproval || [];
75
+ return commandsRequiringApproval.includes(input.commandId);
77
76
  },
78
77
  execute: async (input) => {
79
78
  const { commandId, args } = input;
@@ -84,44 +83,36 @@ export function createExecuteCommandTool(commands, settingsModel) {
84
83
  error: `Command '${commandId}' does not exist. Use 'discover_commands' to see available commands.`
85
84
  };
86
85
  }
87
- try {
88
- // Execute the command
89
- const result = await commands.execute(commandId, args);
90
- // Handle Widget objects specially (including subclasses like DocumentWidget)
91
- let serializedResult;
92
- if (result &&
93
- typeof result === 'object' &&
94
- (result.constructor?.name?.includes('Widget') || result.id)) {
95
- serializedResult = {
96
- type: result.constructor?.name || 'Widget',
97
- id: result.id,
98
- title: result.title?.label || result.title,
99
- className: result.className
100
- };
101
- }
102
- else {
103
- // For other objects, try JSON serialization with fallback
104
- try {
105
- serializedResult = JSON.parse(JSON.stringify(result));
106
- }
107
- catch {
108
- serializedResult = result
109
- ? '[Complex object - cannot serialize]'
110
- : 'Command executed successfully';
111
- }
112
- }
113
- return {
114
- success: true,
115
- commandId,
116
- result: serializedResult
86
+ // Execute the command
87
+ const result = await commands.execute(commandId, args);
88
+ // Handle Widget objects specially (including subclasses like DocumentWidget)
89
+ let serializedResult;
90
+ if (result &&
91
+ typeof result === 'object' &&
92
+ (result.constructor?.name?.includes('Widget') || result.id)) {
93
+ serializedResult = {
94
+ type: result.constructor?.name || 'Widget',
95
+ id: result.id,
96
+ title: result.title?.label || result.title,
97
+ className: result.className
117
98
  };
118
99
  }
119
- catch (error) {
120
- return {
121
- success: false,
122
- error: `Failed to execute command '${commandId}': ${error instanceof Error ? error.message : String(error)}`
123
- };
100
+ else {
101
+ // For other objects, try JSON serialization with fallback
102
+ try {
103
+ serializedResult = JSON.parse(JSON.stringify(result));
104
+ }
105
+ catch {
106
+ serializedResult = result
107
+ ? '[Complex object - cannot serialize]'
108
+ : 'Command executed successfully';
109
+ }
124
110
  }
111
+ return {
112
+ success: true,
113
+ commandId,
114
+ result: serializedResult
115
+ };
125
116
  }
126
117
  });
127
118
  }
package/lib/tools/file.js CHANGED
@@ -1,14 +1,14 @@
1
1
  import { PathExt } from '@jupyterlab/coreutils';
2
- import { tool } from '@openai/agents';
2
+ import { tool } from 'ai';
3
3
  import { z } from 'zod';
4
4
  /**
5
5
  * Create a tool for creating new files of various types
6
6
  */
7
7
  export function createNewFileTool(docManager) {
8
8
  return tool({
9
- name: 'create_file',
9
+ title: 'New File',
10
10
  description: 'Create a new file of specified type (text, python, markdown, json, etc.)',
11
- parameters: z.object({
11
+ inputSchema: z.object({
12
12
  fileName: z.string().describe('Name of the file to create'),
13
13
  fileType: z
14
14
  .string()
@@ -25,12 +25,6 @@ export function createNewFileTool(docManager) {
25
25
  .nullable()
26
26
  .describe('Directory where to create the file (optional)')
27
27
  }),
28
- errorFunction: (context, error) => {
29
- return JSON.stringify({
30
- success: false,
31
- error: `Failed to create file: ${error instanceof Error ? error.message : String(error)}`
32
- });
33
- },
34
28
  execute: async (input) => {
35
29
  const { fileName, content = '', cwd, fileType = 'text' } = input;
36
30
  const registeredFileType = docManager.registry.getFileType(fileType);
@@ -77,22 +71,19 @@ export function createNewFileTool(docManager) {
77
71
  */
78
72
  export function createOpenFileTool(docManager) {
79
73
  return tool({
80
- name: 'open_file',
74
+ title: 'Open File',
81
75
  description: 'Open a file in the editor',
82
- parameters: z.object({
76
+ inputSchema: z.object({
83
77
  filePath: z.string().describe('Path to the file to open')
84
78
  }),
85
- errorFunction: (context, error) => {
86
- return JSON.stringify({
87
- success: false,
88
- error: `Failed to open file: ${error instanceof Error ? error.message : String(error)}`
89
- });
90
- },
91
79
  execute: async (input) => {
92
80
  const { filePath } = input;
93
81
  const widget = docManager.openOrReveal(filePath);
94
82
  if (!widget) {
95
- throw new Error(`Could not open file: ${filePath}`);
83
+ return {
84
+ success: false,
85
+ error: `Could not open file: ${filePath}`
86
+ };
96
87
  }
97
88
  return {
98
89
  success: true,
@@ -108,17 +99,11 @@ export function createOpenFileTool(docManager) {
108
99
  */
109
100
  export function createDeleteFileTool(docManager) {
110
101
  return tool({
111
- name: 'delete_file',
102
+ title: 'Delete File',
112
103
  description: 'Delete a file from the file system',
113
- parameters: z.object({
104
+ inputSchema: z.object({
114
105
  filePath: z.string().describe('Path to the file to delete')
115
106
  }),
116
- errorFunction: (context, error) => {
117
- return JSON.stringify({
118
- success: false,
119
- error: `Failed to delete file: ${error instanceof Error ? error.message : String(error)}`
120
- });
121
- },
122
107
  execute: async (input) => {
123
108
  const { filePath } = input;
124
109
  await docManager.services.contents.delete(filePath);
@@ -135,18 +120,12 @@ export function createDeleteFileTool(docManager) {
135
120
  */
136
121
  export function createRenameFileTool(docManager) {
137
122
  return tool({
138
- name: 'rename_file',
123
+ title: 'Rename File',
139
124
  description: 'Rename a file or move it to a different location',
140
- parameters: z.object({
125
+ inputSchema: z.object({
141
126
  oldPath: z.string().describe('Current path of the file'),
142
127
  newPath: z.string().describe('New path/name for the file')
143
128
  }),
144
- errorFunction: (context, error) => {
145
- return JSON.stringify({
146
- success: false,
147
- error: `Failed to rename file: ${error instanceof Error ? error.message : String(error)}`
148
- });
149
- },
150
129
  execute: async (input) => {
151
130
  const { oldPath, newPath } = input;
152
131
  await docManager.services.contents.rename(oldPath, newPath);
@@ -164,20 +143,14 @@ export function createRenameFileTool(docManager) {
164
143
  */
165
144
  export function createCopyFileTool(docManager) {
166
145
  return tool({
167
- name: 'copy_file',
146
+ title: 'Copy File',
168
147
  description: 'Copy a file to a new location',
169
- parameters: z.object({
148
+ inputSchema: z.object({
170
149
  sourcePath: z.string().describe('Path of the file to copy'),
171
150
  destinationPath: z
172
151
  .string()
173
152
  .describe('Destination path for the copied file')
174
153
  }),
175
- errorFunction: (context, error) => {
176
- return JSON.stringify({
177
- success: false,
178
- error: `Failed to copy file: ${error instanceof Error ? error.message : String(error)}`
179
- });
180
- },
181
154
  execute: async (input) => {
182
155
  const { sourcePath, destinationPath } = input;
183
156
  await docManager.services.contents.copy(sourcePath, destinationPath);
@@ -195,17 +168,11 @@ export function createCopyFileTool(docManager) {
195
168
  */
196
169
  export function createNavigateToDirectoryTool(commands) {
197
170
  return tool({
198
- name: 'navigate_to_directory',
171
+ title: 'Navigate to Directory',
199
172
  description: 'Navigate to a specific directory in the file browser',
200
- parameters: z.object({
173
+ inputSchema: z.object({
201
174
  directoryPath: z.string().describe('Path to the directory to navigate to')
202
175
  }),
203
- errorFunction: (context, error) => {
204
- return JSON.stringify({
205
- success: false,
206
- error: `Failed to navigate to directory: ${error instanceof Error ? error.message : String(error)}`
207
- });
208
- },
209
176
  execute: async (input) => {
210
177
  const { directoryPath } = input;
211
178
  await commands.execute('filebrowser:go-to-path', {
@@ -224,21 +191,15 @@ export function createNavigateToDirectoryTool(commands) {
224
191
  */
225
192
  export function createGetFileInfoTool(docManager, editorTracker) {
226
193
  return tool({
227
- name: 'get_file_info',
194
+ title: 'Get File Info',
228
195
  description: 'Get information about a file including its path, name, extension, and content. Works with text-based files like Python files, markdown, JSON, etc. For Jupyter notebooks, use dedicated notebook tools instead. If no file path is provided, returns information about the currently active file in the editor.',
229
- parameters: z.object({
196
+ inputSchema: z.object({
230
197
  filePath: z
231
198
  .string()
232
199
  .optional()
233
200
  .nullable()
234
201
  .describe('Path to the file to read (e.g., "script.py", "README.md", "config.json"). If not provided, uses the currently active file in the editor.')
235
202
  }),
236
- errorFunction: (context, error) => {
237
- return JSON.stringify({
238
- success: false,
239
- error: `Failed to get file info: ${error instanceof Error ? error.message : String(error)}`
240
- });
241
- },
242
203
  execute: async (input) => {
243
204
  const { filePath } = input;
244
205
  let widget = null;
@@ -248,22 +209,34 @@ export function createGetFileInfoTool(docManager, editorTracker) {
248
209
  docManager.openOrReveal(filePath) ??
249
210
  null;
250
211
  if (!widget) {
251
- throw new Error(`Failed to open file at path: ${filePath}`);
212
+ return JSON.stringify({
213
+ success: false,
214
+ error: `Failed to open file at path: ${filePath}`
215
+ });
252
216
  }
253
217
  }
254
218
  else {
255
219
  widget = editorTracker?.currentWidget ?? null;
256
220
  if (!widget) {
257
- throw new Error('No active file in the editor and no file path provided');
221
+ return JSON.stringify({
222
+ success: false,
223
+ error: 'No active file in the editor and no file path provided'
224
+ });
258
225
  }
259
226
  }
260
227
  if (!widget.context) {
261
- throw new Error('Widget is not a document');
228
+ return JSON.stringify({
229
+ success: false,
230
+ error: 'Widget is not a document'
231
+ });
262
232
  }
263
233
  await widget.context.ready;
264
234
  const model = widget.context.model;
265
235
  if (!model) {
266
- throw new Error('File model not available');
236
+ return JSON.stringify({
237
+ success: false,
238
+ error: 'File model not available'
239
+ });
267
240
  }
268
241
  const sharedModel = model.sharedModel;
269
242
  const content = sharedModel.getSource();
@@ -288,9 +261,9 @@ export function createGetFileInfoTool(docManager, editorTracker) {
288
261
  */
289
262
  export function createSetFileContentTool(docManager, diffManager) {
290
263
  return tool({
291
- name: 'set_file_content',
264
+ title: 'Set File Content',
292
265
  description: 'Set or update the content of an existing file. This will replace the entire content of the file. For Jupyter notebooks, use dedicated notebook tools instead.',
293
- parameters: z.object({
266
+ inputSchema: z.object({
294
267
  filePath: z
295
268
  .string()
296
269
  .describe('Path to the file to update (e.g., "script.py", "README.md", "config.json")'),
@@ -301,12 +274,6 @@ export function createSetFileContentTool(docManager, diffManager) {
301
274
  .default(true)
302
275
  .describe('Whether to save the file after updating (default: true)')
303
276
  }),
304
- errorFunction: (context, error) => {
305
- return JSON.stringify({
306
- success: false,
307
- error: `Failed to set file content: ${error instanceof Error ? error.message : String(error)}`
308
- });
309
- },
310
277
  execute: async (input) => {
311
278
  const { filePath, content, save = true } = input;
312
279
  let widget = docManager.findWidget(filePath);
@@ -314,15 +281,24 @@ export function createSetFileContentTool(docManager, diffManager) {
314
281
  widget = docManager.openOrReveal(filePath);
315
282
  }
316
283
  if (!widget) {
317
- throw new Error(`Failed to open file at path: ${filePath}`);
284
+ return JSON.stringify({
285
+ success: false,
286
+ error: `Failed to open file at path: ${filePath}`
287
+ });
318
288
  }
319
289
  await widget.context.ready;
320
290
  const model = widget.context.model;
321
291
  if (!model) {
322
- throw new Error('File model not available');
292
+ return JSON.stringify({
293
+ success: false,
294
+ error: 'File model not available'
295
+ });
323
296
  }
324
297
  if (model.readOnly) {
325
- throw new Error('File is read-only and cannot be modified');
298
+ return JSON.stringify({
299
+ success: false,
300
+ error: 'File is read-only and cannot be modified'
301
+ });
326
302
  }
327
303
  const sharedModel = model.sharedModel;
328
304
  const originalContent = sharedModel.getSource();