@dexto/tools-filesystem 1.5.8 → 1.6.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 (83) hide show
  1. package/dist/directory-approval.cjs +94 -0
  2. package/dist/directory-approval.d.cts +22 -0
  3. package/dist/directory-approval.d.ts +20 -0
  4. package/dist/directory-approval.d.ts.map +1 -0
  5. package/dist/directory-approval.integration.test.cjs +303 -269
  6. package/dist/directory-approval.integration.test.d.ts +14 -2
  7. package/dist/directory-approval.integration.test.d.ts.map +1 -0
  8. package/dist/directory-approval.integration.test.js +309 -270
  9. package/dist/directory-approval.js +59 -0
  10. package/dist/edit-file-tool.cjs +57 -90
  11. package/dist/edit-file-tool.d.cts +20 -3
  12. package/dist/edit-file-tool.d.ts +22 -9
  13. package/dist/edit-file-tool.d.ts.map +1 -0
  14. package/dist/edit-file-tool.js +53 -76
  15. package/dist/edit-file-tool.test.cjs +66 -29
  16. package/dist/edit-file-tool.test.d.ts +7 -2
  17. package/dist/edit-file-tool.test.d.ts.map +1 -0
  18. package/dist/edit-file-tool.test.js +66 -29
  19. package/dist/error-codes.d.ts +2 -3
  20. package/dist/error-codes.d.ts.map +1 -0
  21. package/dist/errors.d.ts +4 -7
  22. package/dist/errors.d.ts.map +1 -0
  23. package/dist/file-tool-types.d.cts +7 -35
  24. package/dist/file-tool-types.d.ts +8 -40
  25. package/dist/file-tool-types.d.ts.map +1 -0
  26. package/dist/filesystem-service.cjs +18 -1
  27. package/dist/filesystem-service.d.cts +11 -6
  28. package/dist/filesystem-service.d.ts +14 -12
  29. package/dist/filesystem-service.d.ts.map +1 -0
  30. package/dist/filesystem-service.js +18 -1
  31. package/dist/filesystem-service.test.cjs +10 -2
  32. package/dist/filesystem-service.test.d.ts +7 -2
  33. package/dist/filesystem-service.test.d.ts.map +1 -0
  34. package/dist/filesystem-service.test.js +10 -2
  35. package/dist/glob-files-tool.cjs +22 -47
  36. package/dist/glob-files-tool.d.cts +17 -3
  37. package/dist/glob-files-tool.d.ts +19 -9
  38. package/dist/glob-files-tool.d.ts.map +1 -0
  39. package/dist/glob-files-tool.js +23 -48
  40. package/dist/grep-content-tool.cjs +29 -46
  41. package/dist/grep-content-tool.d.cts +26 -3
  42. package/dist/grep-content-tool.d.ts +28 -9
  43. package/dist/grep-content-tool.d.ts.map +1 -0
  44. package/dist/grep-content-tool.js +30 -47
  45. package/dist/index.cjs +3 -3
  46. package/dist/index.d.cts +4 -2
  47. package/dist/index.d.ts +10 -5
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +2 -2
  50. package/dist/path-validator.d.cts +2 -2
  51. package/dist/path-validator.d.ts +6 -9
  52. package/dist/path-validator.d.ts.map +1 -0
  53. package/dist/path-validator.test.d.ts +7 -2
  54. package/dist/path-validator.test.d.ts.map +1 -0
  55. package/dist/read-file-tool.cjs +21 -60
  56. package/dist/read-file-tool.d.cts +17 -3
  57. package/dist/read-file-tool.d.ts +19 -9
  58. package/dist/read-file-tool.d.ts.map +1 -0
  59. package/dist/read-file-tool.js +22 -51
  60. package/dist/tool-factory-config.cjs +61 -0
  61. package/dist/{tool-provider.d.ts → tool-factory-config.d.cts} +9 -23
  62. package/dist/{tool-provider.d.cts → tool-factory-config.d.ts} +13 -30
  63. package/dist/tool-factory-config.d.ts.map +1 -0
  64. package/dist/tool-factory-config.js +36 -0
  65. package/dist/tool-factory.cjs +102 -0
  66. package/dist/tool-factory.d.cts +7 -0
  67. package/dist/tool-factory.d.ts +4 -0
  68. package/dist/tool-factory.d.ts.map +1 -0
  69. package/dist/tool-factory.js +81 -0
  70. package/dist/types.d.ts +17 -18
  71. package/dist/types.d.ts.map +1 -0
  72. package/dist/write-file-tool.cjs +45 -73
  73. package/dist/write-file-tool.d.cts +20 -3
  74. package/dist/write-file-tool.d.ts +22 -9
  75. package/dist/write-file-tool.d.ts.map +1 -0
  76. package/dist/write-file-tool.js +46 -68
  77. package/dist/write-file-tool.test.cjs +76 -32
  78. package/dist/write-file-tool.test.d.ts +7 -2
  79. package/dist/write-file-tool.test.d.ts.map +1 -0
  80. package/dist/write-file-tool.test.js +76 -32
  81. package/package.json +4 -3
  82. package/dist/tool-provider.cjs +0 -123
  83. package/dist/tool-provider.js +0 -99
@@ -29,13 +29,25 @@ var import_write_file_tool = require("./write-file-tool.js");
29
29
  var import_filesystem_service = require("./filesystem-service.js");
30
30
  var import_core = require("@dexto/core");
31
31
  var import_core2 = require("@dexto/core");
32
- const createMockLogger = () => ({
33
- debug: import_vitest.vi.fn(),
34
- info: import_vitest.vi.fn(),
35
- warn: import_vitest.vi.fn(),
36
- error: import_vitest.vi.fn(),
37
- createChild: import_vitest.vi.fn().mockReturnThis()
38
- });
32
+ const createMockLogger = () => {
33
+ const logger = {
34
+ debug: import_vitest.vi.fn(),
35
+ silly: import_vitest.vi.fn(),
36
+ info: import_vitest.vi.fn(),
37
+ warn: import_vitest.vi.fn(),
38
+ error: import_vitest.vi.fn(),
39
+ trackException: import_vitest.vi.fn(),
40
+ createChild: import_vitest.vi.fn(() => logger),
41
+ setLevel: import_vitest.vi.fn(),
42
+ getLevel: import_vitest.vi.fn(() => "debug"),
43
+ getLogFilePath: import_vitest.vi.fn(() => null),
44
+ destroy: import_vitest.vi.fn(async () => void 0)
45
+ };
46
+ return logger;
47
+ };
48
+ function createToolContext(logger, overrides = {}) {
49
+ return { logger, ...overrides };
50
+ }
39
51
  (0, import_vitest.describe)("write_file tool", () => {
40
52
  let mockLogger;
41
53
  let tempDir;
@@ -67,7 +79,7 @@ const createMockLogger = () => ({
67
79
  });
68
80
  (0, import_vitest.describe)("File Modification Detection - Existing Files", () => {
69
81
  (0, import_vitest.it)("should succeed when existing file is not modified between preview and execute", async () => {
70
- const tool = (0, import_write_file_tool.createWriteFileTool)({ fileSystemService });
82
+ const tool = (0, import_write_file_tool.createWriteFileTool)(async () => fileSystemService);
71
83
  const testFile = path.join(tempDir, "test.txt");
72
84
  await fs.writeFile(testFile, "original content");
73
85
  const toolCallId = "test-call-123";
@@ -75,17 +87,24 @@ const createMockLogger = () => ({
75
87
  file_path: testFile,
76
88
  content: "new content"
77
89
  };
78
- const preview = await tool.generatePreview(input, { toolCallId });
90
+ const parsedInput = tool.inputSchema.parse(input);
91
+ const preview = await tool.generatePreview(
92
+ parsedInput,
93
+ createToolContext(mockLogger, { toolCallId })
94
+ );
79
95
  (0, import_vitest.expect)(preview).toBeDefined();
80
96
  (0, import_vitest.expect)(preview?.type).toBe("diff");
81
- const result = await tool.execute(input, { toolCallId });
97
+ const result = await tool.execute(
98
+ parsedInput,
99
+ createToolContext(mockLogger, { toolCallId })
100
+ );
82
101
  (0, import_vitest.expect)(result.success).toBe(true);
83
102
  (0, import_vitest.expect)(result.path).toBe(testFile);
84
103
  const content = await fs.readFile(testFile, "utf-8");
85
104
  (0, import_vitest.expect)(content).toBe("new content");
86
105
  });
87
106
  (0, import_vitest.it)("should fail when existing file is modified between preview and execute", async () => {
88
- const tool = (0, import_write_file_tool.createWriteFileTool)({ fileSystemService });
107
+ const tool = (0, import_write_file_tool.createWriteFileTool)(async () => fileSystemService);
89
108
  const testFile = path.join(tempDir, "test.txt");
90
109
  await fs.writeFile(testFile, "original content");
91
110
  const toolCallId = "test-call-456";
@@ -93,10 +112,11 @@ const createMockLogger = () => ({
93
112
  file_path: testFile,
94
113
  content: "new content"
95
114
  };
96
- await tool.generatePreview(input, { toolCallId });
115
+ const parsedInput = tool.inputSchema.parse(input);
116
+ await tool.generatePreview(parsedInput, createToolContext(mockLogger, { toolCallId }));
97
117
  await fs.writeFile(testFile, "user modified this");
98
118
  try {
99
- await tool.execute(input, { toolCallId });
119
+ await tool.execute(parsedInput, createToolContext(mockLogger, { toolCallId }));
100
120
  import_vitest.expect.fail("Should have thrown an error");
101
121
  } catch (error) {
102
122
  (0, import_vitest.expect)(error).toBeInstanceOf(import_core2.DextoRuntimeError);
@@ -108,7 +128,7 @@ const createMockLogger = () => ({
108
128
  (0, import_vitest.expect)(content).toBe("user modified this");
109
129
  });
110
130
  (0, import_vitest.it)("should fail when existing file is deleted between preview and execute", async () => {
111
- const tool = (0, import_write_file_tool.createWriteFileTool)({ fileSystemService });
131
+ const tool = (0, import_write_file_tool.createWriteFileTool)(async () => fileSystemService);
112
132
  const testFile = path.join(tempDir, "test.txt");
113
133
  await fs.writeFile(testFile, "original content");
114
134
  const toolCallId = "test-call-deleted";
@@ -116,10 +136,11 @@ const createMockLogger = () => ({
116
136
  file_path: testFile,
117
137
  content: "new content"
118
138
  };
119
- await tool.generatePreview(input, { toolCallId });
139
+ const parsedInput = tool.inputSchema.parse(input);
140
+ await tool.generatePreview(parsedInput, createToolContext(mockLogger, { toolCallId }));
120
141
  await fs.unlink(testFile);
121
142
  try {
122
- await tool.execute(input, { toolCallId });
143
+ await tool.execute(parsedInput, createToolContext(mockLogger, { toolCallId }));
123
144
  import_vitest.expect.fail("Should have thrown an error");
124
145
  } catch (error) {
125
146
  (0, import_vitest.expect)(error).toBeInstanceOf(import_core2.DextoRuntimeError);
@@ -131,35 +152,46 @@ const createMockLogger = () => ({
131
152
  });
132
153
  (0, import_vitest.describe)("File Modification Detection - New Files", () => {
133
154
  (0, import_vitest.it)("should succeed when creating new file that still does not exist", async () => {
134
- const tool = (0, import_write_file_tool.createWriteFileTool)({ fileSystemService });
155
+ const tool = (0, import_write_file_tool.createWriteFileTool)(async () => fileSystemService);
135
156
  const testFile = path.join(tempDir, "new-file.txt");
136
157
  const toolCallId = "test-call-new";
137
158
  const input = {
138
159
  file_path: testFile,
139
160
  content: "brand new content"
140
161
  };
141
- const preview = await tool.generatePreview(input, { toolCallId });
162
+ const parsedInput = tool.inputSchema.parse(input);
163
+ const preview = await tool.generatePreview(
164
+ parsedInput,
165
+ createToolContext(mockLogger, { toolCallId })
166
+ );
142
167
  (0, import_vitest.expect)(preview).toBeDefined();
143
168
  (0, import_vitest.expect)(preview?.type).toBe("file");
144
169
  (0, import_vitest.expect)(preview.operation).toBe("create");
145
- const result = await tool.execute(input, { toolCallId });
170
+ const result = await tool.execute(
171
+ parsedInput,
172
+ createToolContext(mockLogger, { toolCallId })
173
+ );
146
174
  (0, import_vitest.expect)(result.success).toBe(true);
147
175
  const content = await fs.readFile(testFile, "utf-8");
148
176
  (0, import_vitest.expect)(content).toBe("brand new content");
149
177
  });
150
178
  (0, import_vitest.it)("should fail when file is created by someone else between preview and execute", async () => {
151
- const tool = (0, import_write_file_tool.createWriteFileTool)({ fileSystemService });
179
+ const tool = (0, import_write_file_tool.createWriteFileTool)(async () => fileSystemService);
152
180
  const testFile = path.join(tempDir, "race-condition.txt");
153
181
  const toolCallId = "test-call-race";
154
182
  const input = {
155
183
  file_path: testFile,
156
184
  content: "agent content"
157
185
  };
158
- const preview = await tool.generatePreview(input, { toolCallId });
186
+ const parsedInput = tool.inputSchema.parse(input);
187
+ const preview = await tool.generatePreview(
188
+ parsedInput,
189
+ createToolContext(mockLogger, { toolCallId })
190
+ );
159
191
  (0, import_vitest.expect)(preview?.type).toBe("file");
160
192
  await fs.writeFile(testFile, "someone else created this");
161
193
  try {
162
- await tool.execute(input, { toolCallId });
194
+ await tool.execute(parsedInput, createToolContext(mockLogger, { toolCallId }));
163
195
  import_vitest.expect.fail("Should have thrown an error");
164
196
  } catch (error) {
165
197
  (0, import_vitest.expect)(error).toBeInstanceOf(import_core2.DextoRuntimeError);
@@ -173,7 +205,7 @@ const createMockLogger = () => ({
173
205
  });
174
206
  (0, import_vitest.describe)("Cache Cleanup", () => {
175
207
  (0, import_vitest.it)("should clean up hash cache after successful execution", async () => {
176
- const tool = (0, import_write_file_tool.createWriteFileTool)({ fileSystemService });
208
+ const tool = (0, import_write_file_tool.createWriteFileTool)(async () => fileSystemService);
177
209
  const testFile = path.join(tempDir, "test.txt");
178
210
  await fs.writeFile(testFile, "original");
179
211
  const toolCallId = "test-call-cleanup";
@@ -181,20 +213,28 @@ const createMockLogger = () => ({
181
213
  file_path: testFile,
182
214
  content: "first write"
183
215
  };
184
- await tool.generatePreview(input, { toolCallId });
185
- await tool.execute(input, { toolCallId });
216
+ const parsedInput = tool.inputSchema.parse(input);
217
+ await tool.generatePreview(parsedInput, createToolContext(mockLogger, { toolCallId }));
218
+ await tool.execute(parsedInput, createToolContext(mockLogger, { toolCallId }));
186
219
  const input2 = {
187
220
  file_path: testFile,
188
221
  content: "second write"
189
222
  };
190
- await tool.generatePreview(input2, { toolCallId });
191
- const result = await tool.execute(input2, { toolCallId });
223
+ const parsedInput2 = tool.inputSchema.parse(input2);
224
+ await tool.generatePreview(
225
+ parsedInput2,
226
+ createToolContext(mockLogger, { toolCallId })
227
+ );
228
+ const result = await tool.execute(
229
+ parsedInput2,
230
+ createToolContext(mockLogger, { toolCallId })
231
+ );
192
232
  (0, import_vitest.expect)(result.success).toBe(true);
193
233
  const content = await fs.readFile(testFile, "utf-8");
194
234
  (0, import_vitest.expect)(content).toBe("second write");
195
235
  });
196
236
  (0, import_vitest.it)("should clean up hash cache after failed execution", async () => {
197
- const tool = (0, import_write_file_tool.createWriteFileTool)({ fileSystemService });
237
+ const tool = (0, import_write_file_tool.createWriteFileTool)(async () => fileSystemService);
198
238
  const testFile = path.join(tempDir, "test.txt");
199
239
  await fs.writeFile(testFile, "original");
200
240
  const toolCallId = "test-call-fail";
@@ -202,15 +242,19 @@ const createMockLogger = () => ({
202
242
  file_path: testFile,
203
243
  content: "new content"
204
244
  };
205
- await tool.generatePreview(input, { toolCallId });
245
+ const parsedInput = tool.inputSchema.parse(input);
246
+ await tool.generatePreview(parsedInput, createToolContext(mockLogger, { toolCallId }));
206
247
  await fs.writeFile(testFile, "modified");
207
248
  try {
208
- await tool.execute(input, { toolCallId });
249
+ await tool.execute(parsedInput, createToolContext(mockLogger, { toolCallId }));
209
250
  } catch {
210
251
  }
211
252
  await fs.writeFile(testFile, "reset content");
212
- await tool.generatePreview(input, { toolCallId });
213
- const result = await tool.execute(input, { toolCallId });
253
+ await tool.generatePreview(parsedInput, createToolContext(mockLogger, { toolCallId }));
254
+ const result = await tool.execute(
255
+ parsedInput,
256
+ createToolContext(mockLogger, { toolCallId })
257
+ );
214
258
  (0, import_vitest.expect)(result.success).toBe(true);
215
259
  });
216
260
  });
@@ -1,2 +1,7 @@
1
-
2
- export { }
1
+ /**
2
+ * Write File Tool Tests
3
+ *
4
+ * Tests for the write_file tool including file modification detection.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=write-file-tool.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-file-tool.test.d.ts","sourceRoot":"","sources":["../src/write-file-tool.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -6,13 +6,25 @@ import { createWriteFileTool } from "./write-file-tool.js";
6
6
  import { FileSystemService } from "./filesystem-service.js";
7
7
  import { ToolErrorCode } from "@dexto/core";
8
8
  import { DextoRuntimeError } from "@dexto/core";
9
- const createMockLogger = () => ({
10
- debug: vi.fn(),
11
- info: vi.fn(),
12
- warn: vi.fn(),
13
- error: vi.fn(),
14
- createChild: vi.fn().mockReturnThis()
15
- });
9
+ const createMockLogger = () => {
10
+ const logger = {
11
+ debug: vi.fn(),
12
+ silly: vi.fn(),
13
+ info: vi.fn(),
14
+ warn: vi.fn(),
15
+ error: vi.fn(),
16
+ trackException: vi.fn(),
17
+ createChild: vi.fn(() => logger),
18
+ setLevel: vi.fn(),
19
+ getLevel: vi.fn(() => "debug"),
20
+ getLogFilePath: vi.fn(() => null),
21
+ destroy: vi.fn(async () => void 0)
22
+ };
23
+ return logger;
24
+ };
25
+ function createToolContext(logger, overrides = {}) {
26
+ return { logger, ...overrides };
27
+ }
16
28
  describe("write_file tool", () => {
17
29
  let mockLogger;
18
30
  let tempDir;
@@ -44,7 +56,7 @@ describe("write_file tool", () => {
44
56
  });
45
57
  describe("File Modification Detection - Existing Files", () => {
46
58
  it("should succeed when existing file is not modified between preview and execute", async () => {
47
- const tool = createWriteFileTool({ fileSystemService });
59
+ const tool = createWriteFileTool(async () => fileSystemService);
48
60
  const testFile = path.join(tempDir, "test.txt");
49
61
  await fs.writeFile(testFile, "original content");
50
62
  const toolCallId = "test-call-123";
@@ -52,17 +64,24 @@ describe("write_file tool", () => {
52
64
  file_path: testFile,
53
65
  content: "new content"
54
66
  };
55
- const preview = await tool.generatePreview(input, { toolCallId });
67
+ const parsedInput = tool.inputSchema.parse(input);
68
+ const preview = await tool.generatePreview(
69
+ parsedInput,
70
+ createToolContext(mockLogger, { toolCallId })
71
+ );
56
72
  expect(preview).toBeDefined();
57
73
  expect(preview?.type).toBe("diff");
58
- const result = await tool.execute(input, { toolCallId });
74
+ const result = await tool.execute(
75
+ parsedInput,
76
+ createToolContext(mockLogger, { toolCallId })
77
+ );
59
78
  expect(result.success).toBe(true);
60
79
  expect(result.path).toBe(testFile);
61
80
  const content = await fs.readFile(testFile, "utf-8");
62
81
  expect(content).toBe("new content");
63
82
  });
64
83
  it("should fail when existing file is modified between preview and execute", async () => {
65
- const tool = createWriteFileTool({ fileSystemService });
84
+ const tool = createWriteFileTool(async () => fileSystemService);
66
85
  const testFile = path.join(tempDir, "test.txt");
67
86
  await fs.writeFile(testFile, "original content");
68
87
  const toolCallId = "test-call-456";
@@ -70,10 +89,11 @@ describe("write_file tool", () => {
70
89
  file_path: testFile,
71
90
  content: "new content"
72
91
  };
73
- await tool.generatePreview(input, { toolCallId });
92
+ const parsedInput = tool.inputSchema.parse(input);
93
+ await tool.generatePreview(parsedInput, createToolContext(mockLogger, { toolCallId }));
74
94
  await fs.writeFile(testFile, "user modified this");
75
95
  try {
76
- await tool.execute(input, { toolCallId });
96
+ await tool.execute(parsedInput, createToolContext(mockLogger, { toolCallId }));
77
97
  expect.fail("Should have thrown an error");
78
98
  } catch (error) {
79
99
  expect(error).toBeInstanceOf(DextoRuntimeError);
@@ -85,7 +105,7 @@ describe("write_file tool", () => {
85
105
  expect(content).toBe("user modified this");
86
106
  });
87
107
  it("should fail when existing file is deleted between preview and execute", async () => {
88
- const tool = createWriteFileTool({ fileSystemService });
108
+ const tool = createWriteFileTool(async () => fileSystemService);
89
109
  const testFile = path.join(tempDir, "test.txt");
90
110
  await fs.writeFile(testFile, "original content");
91
111
  const toolCallId = "test-call-deleted";
@@ -93,10 +113,11 @@ describe("write_file tool", () => {
93
113
  file_path: testFile,
94
114
  content: "new content"
95
115
  };
96
- await tool.generatePreview(input, { toolCallId });
116
+ const parsedInput = tool.inputSchema.parse(input);
117
+ await tool.generatePreview(parsedInput, createToolContext(mockLogger, { toolCallId }));
97
118
  await fs.unlink(testFile);
98
119
  try {
99
- await tool.execute(input, { toolCallId });
120
+ await tool.execute(parsedInput, createToolContext(mockLogger, { toolCallId }));
100
121
  expect.fail("Should have thrown an error");
101
122
  } catch (error) {
102
123
  expect(error).toBeInstanceOf(DextoRuntimeError);
@@ -108,35 +129,46 @@ describe("write_file tool", () => {
108
129
  });
109
130
  describe("File Modification Detection - New Files", () => {
110
131
  it("should succeed when creating new file that still does not exist", async () => {
111
- const tool = createWriteFileTool({ fileSystemService });
132
+ const tool = createWriteFileTool(async () => fileSystemService);
112
133
  const testFile = path.join(tempDir, "new-file.txt");
113
134
  const toolCallId = "test-call-new";
114
135
  const input = {
115
136
  file_path: testFile,
116
137
  content: "brand new content"
117
138
  };
118
- const preview = await tool.generatePreview(input, { toolCallId });
139
+ const parsedInput = tool.inputSchema.parse(input);
140
+ const preview = await tool.generatePreview(
141
+ parsedInput,
142
+ createToolContext(mockLogger, { toolCallId })
143
+ );
119
144
  expect(preview).toBeDefined();
120
145
  expect(preview?.type).toBe("file");
121
146
  expect(preview.operation).toBe("create");
122
- const result = await tool.execute(input, { toolCallId });
147
+ const result = await tool.execute(
148
+ parsedInput,
149
+ createToolContext(mockLogger, { toolCallId })
150
+ );
123
151
  expect(result.success).toBe(true);
124
152
  const content = await fs.readFile(testFile, "utf-8");
125
153
  expect(content).toBe("brand new content");
126
154
  });
127
155
  it("should fail when file is created by someone else between preview and execute", async () => {
128
- const tool = createWriteFileTool({ fileSystemService });
156
+ const tool = createWriteFileTool(async () => fileSystemService);
129
157
  const testFile = path.join(tempDir, "race-condition.txt");
130
158
  const toolCallId = "test-call-race";
131
159
  const input = {
132
160
  file_path: testFile,
133
161
  content: "agent content"
134
162
  };
135
- const preview = await tool.generatePreview(input, { toolCallId });
163
+ const parsedInput = tool.inputSchema.parse(input);
164
+ const preview = await tool.generatePreview(
165
+ parsedInput,
166
+ createToolContext(mockLogger, { toolCallId })
167
+ );
136
168
  expect(preview?.type).toBe("file");
137
169
  await fs.writeFile(testFile, "someone else created this");
138
170
  try {
139
- await tool.execute(input, { toolCallId });
171
+ await tool.execute(parsedInput, createToolContext(mockLogger, { toolCallId }));
140
172
  expect.fail("Should have thrown an error");
141
173
  } catch (error) {
142
174
  expect(error).toBeInstanceOf(DextoRuntimeError);
@@ -150,7 +182,7 @@ describe("write_file tool", () => {
150
182
  });
151
183
  describe("Cache Cleanup", () => {
152
184
  it("should clean up hash cache after successful execution", async () => {
153
- const tool = createWriteFileTool({ fileSystemService });
185
+ const tool = createWriteFileTool(async () => fileSystemService);
154
186
  const testFile = path.join(tempDir, "test.txt");
155
187
  await fs.writeFile(testFile, "original");
156
188
  const toolCallId = "test-call-cleanup";
@@ -158,20 +190,28 @@ describe("write_file tool", () => {
158
190
  file_path: testFile,
159
191
  content: "first write"
160
192
  };
161
- await tool.generatePreview(input, { toolCallId });
162
- await tool.execute(input, { toolCallId });
193
+ const parsedInput = tool.inputSchema.parse(input);
194
+ await tool.generatePreview(parsedInput, createToolContext(mockLogger, { toolCallId }));
195
+ await tool.execute(parsedInput, createToolContext(mockLogger, { toolCallId }));
163
196
  const input2 = {
164
197
  file_path: testFile,
165
198
  content: "second write"
166
199
  };
167
- await tool.generatePreview(input2, { toolCallId });
168
- const result = await tool.execute(input2, { toolCallId });
200
+ const parsedInput2 = tool.inputSchema.parse(input2);
201
+ await tool.generatePreview(
202
+ parsedInput2,
203
+ createToolContext(mockLogger, { toolCallId })
204
+ );
205
+ const result = await tool.execute(
206
+ parsedInput2,
207
+ createToolContext(mockLogger, { toolCallId })
208
+ );
169
209
  expect(result.success).toBe(true);
170
210
  const content = await fs.readFile(testFile, "utf-8");
171
211
  expect(content).toBe("second write");
172
212
  });
173
213
  it("should clean up hash cache after failed execution", async () => {
174
- const tool = createWriteFileTool({ fileSystemService });
214
+ const tool = createWriteFileTool(async () => fileSystemService);
175
215
  const testFile = path.join(tempDir, "test.txt");
176
216
  await fs.writeFile(testFile, "original");
177
217
  const toolCallId = "test-call-fail";
@@ -179,15 +219,19 @@ describe("write_file tool", () => {
179
219
  file_path: testFile,
180
220
  content: "new content"
181
221
  };
182
- await tool.generatePreview(input, { toolCallId });
222
+ const parsedInput = tool.inputSchema.parse(input);
223
+ await tool.generatePreview(parsedInput, createToolContext(mockLogger, { toolCallId }));
183
224
  await fs.writeFile(testFile, "modified");
184
225
  try {
185
- await tool.execute(input, { toolCallId });
226
+ await tool.execute(parsedInput, createToolContext(mockLogger, { toolCallId }));
186
227
  } catch {
187
228
  }
188
229
  await fs.writeFile(testFile, "reset content");
189
- await tool.generatePreview(input, { toolCallId });
190
- const result = await tool.execute(input, { toolCallId });
230
+ await tool.generatePreview(parsedInput, createToolContext(mockLogger, { toolCallId }));
231
+ const result = await tool.execute(
232
+ parsedInput,
233
+ createToolContext(mockLogger, { toolCallId })
234
+ );
191
235
  expect(result.success).toBe(true);
192
236
  });
193
237
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dexto/tools-filesystem",
3
- "version": "1.5.8",
4
- "description": "FileSystem tools provider for Dexto agents",
3
+ "version": "1.6.0",
4
+ "description": "FileSystem tools factory for Dexto agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -22,7 +22,8 @@
22
22
  "glob": "^11.1.0",
23
23
  "safe-regex": "^2.1.1",
24
24
  "zod": "^3.25.0",
25
- "@dexto/core": "1.5.8"
25
+ "@dexto/agent-config": "1.6.0",
26
+ "@dexto/core": "1.6.0"
26
27
  },
27
28
  "devDependencies": {
28
29
  "@types/diff": "^5.2.3",
@@ -1,123 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
- var tool_provider_exports = {};
20
- __export(tool_provider_exports, {
21
- fileSystemToolsProvider: () => fileSystemToolsProvider
22
- });
23
- module.exports = __toCommonJS(tool_provider_exports);
24
- var import_zod = require("zod");
25
- var import_filesystem_service = require("./filesystem-service.js");
26
- var import_read_file_tool = require("./read-file-tool.js");
27
- var import_write_file_tool = require("./write-file-tool.js");
28
- var import_edit_file_tool = require("./edit-file-tool.js");
29
- var import_glob_files_tool = require("./glob-files-tool.js");
30
- var import_grep_content_tool = require("./grep-content-tool.js");
31
- const DEFAULT_ALLOWED_PATHS = ["."];
32
- const DEFAULT_BLOCKED_PATHS = [".git", "node_modules/.bin", ".env"];
33
- const DEFAULT_BLOCKED_EXTENSIONS = [".exe", ".dll", ".so"];
34
- const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
35
- const DEFAULT_ENABLE_BACKUPS = false;
36
- const DEFAULT_BACKUP_RETENTION_DAYS = 7;
37
- const FILESYSTEM_TOOL_NAMES = [
38
- "read_file",
39
- "write_file",
40
- "edit_file",
41
- "glob_files",
42
- "grep_content"
43
- ];
44
- const FileSystemToolsConfigSchema = import_zod.z.object({
45
- type: import_zod.z.literal("filesystem-tools"),
46
- allowedPaths: import_zod.z.array(import_zod.z.string()).default(DEFAULT_ALLOWED_PATHS).describe("List of allowed base paths for file operations"),
47
- blockedPaths: import_zod.z.array(import_zod.z.string()).default(DEFAULT_BLOCKED_PATHS).describe("List of blocked paths to exclude from operations"),
48
- blockedExtensions: import_zod.z.array(import_zod.z.string()).default(DEFAULT_BLOCKED_EXTENSIONS).describe("List of blocked file extensions"),
49
- maxFileSize: import_zod.z.number().int().positive().default(DEFAULT_MAX_FILE_SIZE).describe(
50
- `Maximum file size in bytes (default: ${DEFAULT_MAX_FILE_SIZE / 1024 / 1024}MB)`
51
- ),
52
- workingDirectory: import_zod.z.string().optional().describe("Working directory for file operations (defaults to process.cwd())"),
53
- enableBackups: import_zod.z.boolean().default(DEFAULT_ENABLE_BACKUPS).describe("Enable automatic backups of modified files"),
54
- backupPath: import_zod.z.string().optional().describe("Absolute path for storing file backups (if enableBackups is true)"),
55
- backupRetentionDays: import_zod.z.number().int().positive().default(DEFAULT_BACKUP_RETENTION_DAYS).describe(
56
- `Number of days to retain backup files (default: ${DEFAULT_BACKUP_RETENTION_DAYS})`
57
- ),
58
- enabledTools: import_zod.z.array(import_zod.z.enum(FILESYSTEM_TOOL_NAMES)).optional().describe(
59
- `Subset of tools to enable. If not specified, all tools are enabled. Available: ${FILESYSTEM_TOOL_NAMES.join(", ")}`
60
- )
61
- }).strict();
62
- const fileSystemToolsProvider = {
63
- type: "filesystem-tools",
64
- configSchema: FileSystemToolsConfigSchema,
65
- create: (config, context) => {
66
- const { logger, services } = context;
67
- logger.debug("Creating FileSystemService for filesystem tools");
68
- const fileSystemService = new import_filesystem_service.FileSystemService(
69
- {
70
- allowedPaths: config.allowedPaths,
71
- blockedPaths: config.blockedPaths,
72
- blockedExtensions: config.blockedExtensions,
73
- maxFileSize: config.maxFileSize,
74
- workingDirectory: config.workingDirectory || process.cwd(),
75
- enableBackups: config.enableBackups,
76
- backupPath: config.backupPath,
77
- backupRetentionDays: config.backupRetentionDays
78
- },
79
- logger
80
- );
81
- fileSystemService.initialize().catch((error) => {
82
- logger.error(`Failed to initialize FileSystemService: ${error.message}`);
83
- });
84
- logger.debug("FileSystemService created - initialization will complete on first tool use");
85
- const approvalManager = services?.approvalManager;
86
- if (approvalManager) {
87
- const approvalChecker = (filePath) => {
88
- return approvalManager.isDirectoryApproved(filePath);
89
- };
90
- fileSystemService.setDirectoryApprovalChecker(approvalChecker);
91
- logger.debug("Directory approval checker configured for FileSystemService");
92
- }
93
- const directoryApproval = approvalManager ? {
94
- isSessionApproved: (filePath) => approvalManager.isDirectorySessionApproved(filePath),
95
- addApproved: (directory, type) => approvalManager.addApprovedDirectory(directory, type)
96
- } : void 0;
97
- const fileToolOptions = {
98
- fileSystemService,
99
- directoryApproval
100
- };
101
- const toolCreators = {
102
- read_file: () => (0, import_read_file_tool.createReadFileTool)(fileToolOptions),
103
- write_file: () => (0, import_write_file_tool.createWriteFileTool)(fileToolOptions),
104
- edit_file: () => (0, import_edit_file_tool.createEditFileTool)(fileToolOptions),
105
- glob_files: () => (0, import_glob_files_tool.createGlobFilesTool)(fileToolOptions),
106
- grep_content: () => (0, import_grep_content_tool.createGrepContentTool)(fileToolOptions)
107
- };
108
- const toolsToCreate = config.enabledTools ?? FILESYSTEM_TOOL_NAMES;
109
- if (config.enabledTools) {
110
- logger.debug(`Creating subset of filesystem tools: ${toolsToCreate.join(", ")}`);
111
- }
112
- return toolsToCreate.map((toolName) => toolCreators[toolName]());
113
- },
114
- metadata: {
115
- displayName: "FileSystem Tools",
116
- description: "File system operations (read, write, edit, glob, grep)",
117
- category: "filesystem"
118
- }
119
- };
120
- // Annotate the CommonJS export names for ESM import in node:
121
- 0 && (module.exports = {
122
- fileSystemToolsProvider
123
- });