ai-code-agents 0.1.0 → 0.2.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/dist/index.js CHANGED
@@ -1,13 +1,9 @@
1
1
  // src/environments/docker-environment.ts
2
2
  import { exec } from "child_process";
3
-
4
- // src/util/escape-command-arg.ts
5
- function escapeCommandArg(arg) {
6
- if ("" === arg) {
7
- return "''";
8
- }
9
- return `'${arg.replace(/'/g, "'\\''")}'`;
10
- }
3
+ import {
4
+ UnixEnvironmentBase,
5
+ escapeCommandArg
6
+ } from "@ai-code-agents/environment-utils";
11
7
 
12
8
  // src/util/escape-command.ts
13
9
  function escapeCommand(command) {
@@ -15,278 +11,6 @@ function escapeCommand(command) {
15
11
  return `"${escaped}"`;
16
12
  }
17
13
 
18
- // src/util/validate-relative-path.ts
19
- import * as path from "path";
20
- function validateRelativePath(filePath) {
21
- if (path.isAbsolute(filePath)) {
22
- throw new Error("Absolute paths are not allowed.");
23
- }
24
- if (filePath.startsWith("~")) {
25
- throw new Error('Paths starting with "~" are not allowed.');
26
- }
27
- if (filePath.includes("\0")) {
28
- throw new Error("Paths must not contain null bytes.");
29
- }
30
- const normalizedPath = path.normalize(filePath);
31
- if (normalizedPath.startsWith("..")) {
32
- throw new Error("Path traversal is not allowed.");
33
- }
34
- }
35
-
36
- // src/environments/filesystem-environment-base.ts
37
- var FilesystemEnvironmentBase = class {
38
- _envConfig;
39
- /**
40
- * Constructs a new environment instance.
41
- *
42
- * @param config - Environment configuration.
43
- */
44
- constructor(config) {
45
- this._envConfig = config;
46
- }
47
- /**
48
- * Reads the content of a file at the specified path.
49
- *
50
- * @param path - The path to the file to read, relative to the project directory.
51
- * @returns A promise that resolves to a ReadFileResult.
52
- */
53
- async readFile(path5) {
54
- validateRelativePath(path5);
55
- if (!await this.fileExists(path5)) {
56
- throw new Error(`File not found: ${path5}`);
57
- }
58
- const content = await this.readFileContent(path5);
59
- return {
60
- path: path5,
61
- content
62
- };
63
- }
64
- /**
65
- * Writes content to a file at the specified path.
66
- *
67
- * If a file is already present at the path, it will be overwritten.
68
- *
69
- * @param path - The path to the file to write, relative to the project directory.
70
- * @param content - The content to write to the file.
71
- * @returns A promise that resolves to a WriteFileResult.
72
- */
73
- async writeFile(path5, content) {
74
- validateRelativePath(path5);
75
- await this.writeFileContent(path5, content);
76
- return {
77
- path: path5,
78
- message: "File written successfully."
79
- };
80
- }
81
- /**
82
- * Deletes a file at the specified path.
83
- *
84
- * @param path - The path to the file to delete, relative to the project directory.
85
- * @returns A promise that resolves to a DeleteFileResult.
86
- */
87
- async deleteFile(path5) {
88
- validateRelativePath(path5);
89
- if (!await this.fileExists(path5)) {
90
- return {
91
- path: path5,
92
- message: "File was already deleted."
93
- };
94
- }
95
- await this.deleteFileContent(path5);
96
- return {
97
- path: path5,
98
- message: "File deleted successfully."
99
- };
100
- }
101
- /**
102
- * Moves a file from a source path to a destination path.
103
- *
104
- * If a file is already present at the destination path, it will be overwritten.
105
- *
106
- * @param sourcePath - The path to the file to move.
107
- * @param destinationPath - The path to move the file to.
108
- * @returns A promise that resolves to a MoveFileResult.
109
- */
110
- async moveFile(sourcePath, destinationPath) {
111
- validateRelativePath(sourcePath);
112
- validateRelativePath(destinationPath);
113
- if (!await this.fileExists(sourcePath)) {
114
- throw new Error(`File not found: ${sourcePath}`);
115
- }
116
- await this.moveFileContent(sourcePath, destinationPath);
117
- return {
118
- sourcePath,
119
- destinationPath,
120
- message: "File moved successfully."
121
- };
122
- }
123
- /**
124
- * Copies a file from a source path to a destination path.
125
- *
126
- * If a file is already present at the destination path, it will be overwritten.
127
- *
128
- * @param sourcePath - The path to the file to copy.
129
- * @param destinationPath - The path to copy the file to.
130
- * @returns A promise that resolves to a CopyFileResult.
131
- */
132
- async copyFile(sourcePath, destinationPath) {
133
- validateRelativePath(sourcePath);
134
- validateRelativePath(destinationPath);
135
- if (!await this.fileExists(sourcePath)) {
136
- throw new Error(`File not found: ${sourcePath}`);
137
- }
138
- await this.copyFileContent(sourcePath, destinationPath);
139
- return {
140
- sourcePath,
141
- destinationPath,
142
- message: "File copied successfully."
143
- };
144
- }
145
- /**
146
- * Moves the content of a file from a source path to a destination path, relative to the project directory.
147
- *
148
- * When this method is called, it is guaranteed that the source file exists.
149
- * This method unconditionally moves the content, even if a file already exists at the destination path.
150
- *
151
- * @param relativeSourcePath - The path to the file to move, relative to the project directory.
152
- * @param relativeDestinationPath - The path to move the file to, relative to the project directory.
153
- */
154
- async moveFileContent(relativeSourcePath, relativeDestinationPath) {
155
- const content = await this.readFileContent(relativeSourcePath);
156
- this.writeFileContent(relativeDestinationPath, content);
157
- this.deleteFileContent(relativeSourcePath);
158
- }
159
- /**
160
- * Copies the content of a file from a source path to a destination path, relative to the project directory.
161
- *
162
- * When this method is called, it is guaranteed that the source file exists.
163
- * This method unconditionally copies the content, even if a file already exists at the destination path.
164
- *
165
- * @param relativeSourcePath - The path to the file to copy, relative to the project directory.
166
- * @param relativeDestinationPath - The path to copy the file to, relative to the project directory.
167
- */
168
- async copyFileContent(relativeSourcePath, relativeDestinationPath) {
169
- const content = await this.readFileContent(relativeSourcePath);
170
- this.writeFileContent(relativeDestinationPath, content);
171
- }
172
- };
173
-
174
- // src/environments/command-line-environment-base.ts
175
- var CommandLineEnvironmentBase = class extends FilesystemEnvironmentBase {
176
- /**
177
- * Runs a CLI command in environment.
178
- *
179
- * @param command - The command to run.
180
- * @returns A promise that resolves to a RunCommandResult.
181
- */
182
- async runCommand(command) {
183
- const [exitCode, stdout, stderr] = await this.executeCommand(command);
184
- return {
185
- command,
186
- exitCode,
187
- stdout,
188
- stderr
189
- };
190
- }
191
- };
192
-
193
- // src/environments/unix-environment-base.ts
194
- var UnixEnvironmentBase = class extends CommandLineEnvironmentBase {
195
- /**
196
- * Checks whether a file exists at the specified path relative to the project directory.
197
- *
198
- * @param relativePath - The path to the file to check, relative to the project directory.
199
- * @returns True if the file exists, false otherwise.
200
- */
201
- async fileExists(relativePath) {
202
- const command = `if [ -e ${escapeCommandArg(relativePath)} ]; then echo "yes"; else echo "no"; fi`;
203
- const { exitCode, stdout } = await this.runCommand(command);
204
- return exitCode === 0 && stdout.trim() === "yes";
205
- }
206
- /**
207
- * Gets the content of a file at the specified path, relative to the project directory.
208
- *
209
- * When this method is called, it is guaranteed that the file exists.
210
- *
211
- * @param relativePath - The path to the file to read, relative to the project directory.
212
- * @returns The content of the file.
213
- */
214
- async readFileContent(relativePath) {
215
- const command = `cat ${escapeCommandArg(relativePath)}`;
216
- const { exitCode, stdout } = await this.runCommand(command);
217
- return exitCode === 0 ? stdout : "";
218
- }
219
- /**
220
- * Writes content to a file at the specified path, relative to the project directory.
221
- *
222
- * This method unconditionally writes the content, even if a file already exists at the path, or if the file is new.
223
- *
224
- * @param relativePath - The path to the file to write, relative to the project directory.
225
- * @param content - The content to write to the file.
226
- */
227
- async writeFileContent(relativePath, content) {
228
- const command = `sh -c "echo ${escapeCommandArg(
229
- content
230
- )} > ${escapeCommandArg(relativePath)}"`;
231
- const { exitCode, stderr } = await this.runCommand(command);
232
- if (exitCode !== 0) {
233
- throw new Error(`Failed to write file: ${stderr || "Unknown error"}`);
234
- }
235
- }
236
- /**
237
- * Deletes a file at the specified path, relative to the project directory.
238
- *
239
- * When this method is called, it is guaranteed that the file exists.
240
- *
241
- * @param relativePath - The path to the file to delete, relative to the project directory.
242
- */
243
- async deleteFileContent(relativePath) {
244
- const command = `rm ${escapeCommandArg(relativePath)}`;
245
- const { exitCode, stderr } = await this.runCommand(command);
246
- if (exitCode !== 0) {
247
- throw new Error(`Failed to delete file: ${stderr || "Unknown error"}`);
248
- }
249
- }
250
- /**
251
- * Moves the content of a file from a source path to a destination path, relative to the project directory.
252
- *
253
- * When this method is called, it is guaranteed that the source file exists.
254
- * This method unconditionally moves the content, even if a file already exists at the destination path.
255
- *
256
- * @param relativeSourcePath - The path to the file to move, relative to the project directory.
257
- * @param relativeDestinationPath - The path to move the file to, relative to the project directory.
258
- */
259
- async moveFileContent(relativeSourcePath, relativeDestinationPath) {
260
- const command = `mv ${escapeCommandArg(relativeSourcePath)} ${escapeCommandArg(
261
- relativeDestinationPath
262
- )}`;
263
- const { exitCode, stderr } = await this.runCommand(command);
264
- if (exitCode !== 0) {
265
- throw new Error(`Failed to move file: ${stderr || "Unknown error"}`);
266
- }
267
- }
268
- /**
269
- * Copies the content of a file from a source path to a destination path, relative to the project directory.
270
- *
271
- * When this method is called, it is guaranteed that the source file exists.
272
- * This method unconditionally copies the content, even if a file already exists at the destination path.
273
- *
274
- * @param relativeSourcePath - The path to the file to copy, relative to the project directory.
275
- * @param relativeDestinationPath - The path to copy the file to, relative to the project directory.
276
- */
277
- async copyFileContent(relativeSourcePath, relativeDestinationPath) {
278
- const command = `cp ${escapeCommandArg(relativeSourcePath)} ${escapeCommandArg(
279
- relativeDestinationPath
280
- )}`;
281
- const result = await this.runCommand(command);
282
- if (result.exitCode !== 0) {
283
- throw new Error(
284
- `Failed to copy file: ${result.stderr || "Unknown error"}`
285
- );
286
- }
287
- }
288
- };
289
-
290
14
  // src/environments/docker-environment.ts
291
15
  var DockerEnvironmentName = "docker";
292
16
  var DockerEnvironment = class extends UnixEnvironmentBase {
@@ -329,7 +53,8 @@ var DockerEnvironment = class extends UnixEnvironmentBase {
329
53
  };
330
54
 
331
55
  // src/environments/mock-filesystem-environment.ts
332
- import path2 from "path";
56
+ import path from "path";
57
+ import { FilesystemEnvironmentBase } from "@ai-code-agents/environment-utils";
333
58
  var MockFilesystemEnvironmentName = "mock-filesystem";
334
59
  var MockFilesystemEnvironment = class extends FilesystemEnvironmentBase {
335
60
  files;
@@ -342,8 +67,8 @@ var MockFilesystemEnvironment = class extends FilesystemEnvironmentBase {
342
67
  constructor(config = {}) {
343
68
  super(config);
344
69
  const { initialFiles, directoryPath } = this._envConfig;
345
- this.files = initialFiles ?? /* @__PURE__ */ new Map();
346
- this._preparePath = directoryPath ? (filePath) => path2.join(directoryPath, filePath) : (filePath) => filePath;
70
+ this.files = initialFiles ? new Map(Object.entries(initialFiles)) : /* @__PURE__ */ new Map();
71
+ this._preparePath = directoryPath ? (filePath) => path.join(directoryPath, filePath) : (filePath) => filePath;
347
72
  }
348
73
  /**
349
74
  * Gets the environment name.
@@ -396,9 +121,10 @@ var MockFilesystemEnvironment = class extends FilesystemEnvironmentBase {
396
121
 
397
122
  // src/environments/node-filesystem-environment.ts
398
123
  import fs from "fs/promises";
399
- import path3 from "path";
124
+ import path2 from "path";
125
+ import { FilesystemEnvironmentBase as FilesystemEnvironmentBase2 } from "@ai-code-agents/environment-utils";
400
126
  var NodeFilesystemEnvironmentName = "node-filesystem";
401
- var NodeFilesystemEnvironment = class extends FilesystemEnvironmentBase {
127
+ var NodeFilesystemEnvironment = class extends FilesystemEnvironmentBase2 {
402
128
  /**
403
129
  * Constructs a new NodeFilesystemEnvironment instance.
404
130
  *
@@ -425,7 +151,7 @@ var NodeFilesystemEnvironment = class extends FilesystemEnvironmentBase {
425
151
  * @returns True if the file exists, false otherwise.
426
152
  */
427
153
  async fileExists(relativePath) {
428
- const absolutePath = path3.join(this._envConfig.directoryPath, relativePath);
154
+ const absolutePath = path2.join(this._envConfig.directoryPath, relativePath);
429
155
  try {
430
156
  await fs.stat(absolutePath);
431
157
  return true;
@@ -442,7 +168,7 @@ var NodeFilesystemEnvironment = class extends FilesystemEnvironmentBase {
442
168
  * @returns The content of the file.
443
169
  */
444
170
  async readFileContent(relativePath) {
445
- const absolutePath = path3.join(this._envConfig.directoryPath, relativePath);
171
+ const absolutePath = path2.join(this._envConfig.directoryPath, relativePath);
446
172
  return fs.readFile(absolutePath, "utf-8");
447
173
  }
448
174
  /**
@@ -454,7 +180,7 @@ var NodeFilesystemEnvironment = class extends FilesystemEnvironmentBase {
454
180
  * @param content - The content to write to the file.
455
181
  */
456
182
  async writeFileContent(relativePath, content) {
457
- const absolutePath = path3.join(this._envConfig.directoryPath, relativePath);
183
+ const absolutePath = path2.join(this._envConfig.directoryPath, relativePath);
458
184
  await fs.writeFile(absolutePath, content, "utf-8");
459
185
  }
460
186
  /**
@@ -465,7 +191,7 @@ var NodeFilesystemEnvironment = class extends FilesystemEnvironmentBase {
465
191
  * @param relativePath - The path to the file to delete, relative to the project directory.
466
192
  */
467
193
  async deleteFileContent(relativePath) {
468
- const absolutePath = path3.join(this._envConfig.directoryPath, relativePath);
194
+ const absolutePath = path2.join(this._envConfig.directoryPath, relativePath);
469
195
  await fs.rm(absolutePath);
470
196
  }
471
197
  /**
@@ -478,11 +204,11 @@ var NodeFilesystemEnvironment = class extends FilesystemEnvironmentBase {
478
204
  * @param relativeDestinationPath - The path to move the file to, relative to the project directory.
479
205
  */
480
206
  async moveFileContent(relativeSourcePath, relativeDestinationPath) {
481
- const sourcePath = path3.join(
207
+ const sourcePath = path2.join(
482
208
  this._envConfig.directoryPath,
483
209
  relativeSourcePath
484
210
  );
485
- const destinationPath = path3.join(
211
+ const destinationPath = path2.join(
486
212
  this._envConfig.directoryPath,
487
213
  relativeDestinationPath
488
214
  );
@@ -492,8 +218,12 @@ var NodeFilesystemEnvironment = class extends FilesystemEnvironmentBase {
492
218
 
493
219
  // src/environments/unsafe-local-environment.ts
494
220
  import { exec as exec2 } from "child_process";
221
+ import {
222
+ UnixEnvironmentBase as UnixEnvironmentBase2,
223
+ escapeCommandArg as escapeCommandArg2
224
+ } from "@ai-code-agents/environment-utils";
495
225
  var UnsafeLocalEnvironmentName = "unsafe-local";
496
- var UnsafeLocalEnvironment = class extends UnixEnvironmentBase {
226
+ var UnsafeLocalEnvironment = class extends UnixEnvironmentBase2 {
497
227
  _commandPrefix;
498
228
  /**
499
229
  * Constructs a new environment instance.
@@ -509,7 +239,7 @@ var UnsafeLocalEnvironment = class extends UnixEnvironmentBase {
509
239
  throw new Error('The directory path must be absolute (start with "/")');
510
240
  }
511
241
  super(config);
512
- this._commandPrefix = `cd ${escapeCommandArg(directoryPath)} && `;
242
+ this._commandPrefix = `cd ${escapeCommandArg2(directoryPath)} && `;
513
243
  }
514
244
  /**
515
245
  * Gets the environment name.
@@ -536,180 +266,17 @@ var UnsafeLocalEnvironment = class extends UnixEnvironmentBase {
536
266
  };
537
267
 
538
268
  // src/tools/copy-file-tool.ts
539
- import { z as z2 } from "zod";
540
-
541
- // src/types.ts
542
- import * as z from "zod";
543
- var ReadFileResult = z.object({
544
- path: z.string().meta({
545
- description: "The path to the file that was read."
546
- }),
547
- content: z.string().meta({
548
- description: "The content of the file that was read."
549
- })
550
- });
551
- var WriteFileResult = z.object({
552
- path: z.string().meta({
553
- description: "The path to the file that was written."
554
- }),
555
- message: z.string().meta({
556
- description: "A message indicating the result of the write operation."
557
- })
558
- });
559
- var DeleteFileResult = z.object({
560
- path: z.string().meta({
561
- description: "The path to the file that was deleted."
562
- }),
563
- message: z.string().meta({
564
- description: "A message indicating the result of the delete operation."
565
- })
566
- });
567
- var MoveFileResult = z.object({
568
- sourcePath: z.string().meta({
569
- description: "The original path of the file that was moved."
570
- }),
571
- destinationPath: z.string().meta({
572
- description: "The new path of the file that was moved to."
573
- }),
574
- message: z.string().meta({
575
- description: "A message indicating the result of the move operation."
576
- })
577
- });
578
- var CopyFileResult = z.object({
579
- sourcePath: z.string().meta({
580
- description: "The original path of the file that was copied."
581
- }),
582
- destinationPath: z.string().meta({
583
- description: "The new path of the file that was copied to."
584
- }),
585
- message: z.string().meta({
586
- description: "A message indicating the result of the copy operation."
587
- })
588
- });
589
- var RunCommandResult = z.object({
590
- command: z.string().meta({
591
- description: "The command that was executed."
592
- }),
593
- exitCode: z.number().meta({
594
- description: "The exit code of the command."
595
- }),
596
- stdout: z.string().meta({
597
- description: "The standard output of the command."
598
- }),
599
- stderr: z.string().meta({
600
- description: "The standard error output of the command."
601
- })
602
- });
603
-
604
- // src/tools/tool-base.ts
605
- var ToolBase = class {
606
- _toolConfig;
607
- _name;
608
- _description;
609
- _inputSchema;
610
- _outputSchema;
611
- _needsApproval;
612
- /**
613
- * Constructs a new tool instance.
614
- *
615
- * @param toolConfig - Optional tool config, can be used to override some defaults.
616
- */
617
- constructor(toolConfig) {
618
- const {
619
- name: defaultName,
620
- description: defaultDescription,
621
- inputSchema,
622
- outputSchema,
623
- needsApproval: defaultNeedsApproval
624
- } = this.getMetadata();
625
- this._name = toolConfig?.name || defaultName;
626
- this._description = toolConfig?.description || defaultDescription;
627
- this._inputSchema = inputSchema;
628
- this._outputSchema = outputSchema;
629
- this._needsApproval = toolConfig?.needsApproval !== void 0 ? toolConfig.needsApproval : defaultNeedsApproval;
630
- }
631
- /**
632
- * Gets the tool name.
633
- *
634
- * @returns The tool name.
635
- */
636
- get name() {
637
- return this._name;
638
- }
639
- /**
640
- * Gets the tool description.
641
- *
642
- * @returns The tool description.
643
- */
644
- get description() {
645
- return this._description;
646
- }
647
- /**
648
- * Gets the input schema for the tool.
649
- *
650
- * @returns The input schema.
651
- */
652
- get inputSchema() {
653
- return this._inputSchema;
654
- }
655
- /**
656
- * Gets the input schema for the tool.
657
- *
658
- * @returns The input schema.
659
- */
660
- get outputSchema() {
661
- return this._outputSchema;
662
- }
663
- /**
664
- * Gets whether the tool needs approval before use.
665
- *
666
- * @returns True if the tool needs approval, false otherwise.
667
- */
668
- get needsApproval() {
669
- return this._needsApproval;
670
- }
671
- };
672
-
673
- // src/tools/environment-tool-base.ts
674
- var EnvironmentToolBase = class extends ToolBase {
675
- _environment;
676
- /**
677
- * Constructs a new `EnvironmentToolBase` instance.
678
- *
679
- * @param environment - The execution environment to apply the tool in.
680
- * @param toolConfig - Optional tool config, can be used to override some defaults.
681
- */
682
- constructor(environment, toolConfig) {
683
- super(toolConfig);
684
- this._environment = environment;
685
- }
686
- /**
687
- * Gets the current execution environment for the tool.
688
- *
689
- * @returns The current execution environment.
690
- */
691
- get environment() {
692
- return this._environment;
693
- }
694
- /**
695
- * Executes the tool with the given input.
696
- *
697
- * @param input - The input for the tool.
698
- * @param _options - Options from the tool call.
699
- * @returns A promise that resolves to the tool execution result.
700
- */
701
- execute(input, _options) {
702
- return this.executeForEnvironment(this._environment, input);
703
- }
704
- };
705
-
706
- // src/tools/copy-file-tool.ts
269
+ import { z } from "zod";
270
+ import {
271
+ EnvironmentToolBase,
272
+ CopyFileResult
273
+ } from "@ai-code-agents/environment-utils";
707
274
  var CopyFileToolName = "copy_file";
708
- var CopyFileToolInput = z2.object({
709
- sourcePath: z2.string().meta({
275
+ var CopyFileToolInput = z.object({
276
+ sourcePath: z.string().meta({
710
277
  description: "The path to the file to copy, relative to the project directory."
711
278
  }),
712
- destinationPath: z2.string().meta({
279
+ destinationPath: z.string().meta({
713
280
  description: "The path to the destination where the file should be copied, relative to the project directory. If the file already exists, it will be overwritten."
714
281
  })
715
282
  });
@@ -744,10 +311,11 @@ var CopyFileTool = class extends EnvironmentToolBase {
744
311
  /**
745
312
  * Converts the tool output to a format suitable for model consumption.
746
313
  *
747
- * @param output - The output from the tool execution.
314
+ * @param options - The tool result, including the output from the tool execution.
748
315
  * @returns The formatted tool result.
749
316
  */
750
- toModelOutput(output) {
317
+ toModelOutput(options) {
318
+ const { output } = options;
751
319
  return {
752
320
  type: "text",
753
321
  value: `File \`${output.sourcePath}\` copied successfully to \`${output.destinationPath}\`.`
@@ -772,15 +340,19 @@ var CopyFileTool = class extends EnvironmentToolBase {
772
340
  };
773
341
 
774
342
  // src/tools/delete-file-tool.ts
775
- import { z as z3 } from "zod";
343
+ import { z as z2 } from "zod";
344
+ import {
345
+ EnvironmentToolBase as EnvironmentToolBase2,
346
+ DeleteFileResult
347
+ } from "@ai-code-agents/environment-utils";
776
348
  var DeleteFileToolName = "delete_file";
777
- var DeleteFileToolInput = z3.object({
778
- path: z3.string().meta({
349
+ var DeleteFileToolInput = z2.object({
350
+ path: z2.string().meta({
779
351
  description: "The path to the file to delete, relative to the project directory."
780
352
  })
781
353
  });
782
354
  var DeleteFileToolOutput = DeleteFileResult;
783
- var DeleteFileTool = class extends EnvironmentToolBase {
355
+ var DeleteFileTool = class extends EnvironmentToolBase2 {
784
356
  /**
785
357
  * Returns the metadata for the tool.
786
358
  *
@@ -810,10 +382,11 @@ var DeleteFileTool = class extends EnvironmentToolBase {
810
382
  /**
811
383
  * Converts the tool output to a format suitable for model consumption.
812
384
  *
813
- * @param output - The output from the tool execution.
385
+ * @param options - The tool result, including the output from the tool execution.
814
386
  * @returns The formatted tool result.
815
387
  */
816
- toModelOutput(output) {
388
+ toModelOutput(options) {
389
+ const { output } = options;
817
390
  return {
818
391
  type: "text",
819
392
  value: `File \`${output.path}\` deleted successfully.`
@@ -837,43 +410,46 @@ var DeleteFileTool = class extends EnvironmentToolBase {
837
410
  };
838
411
 
839
412
  // src/tools/edit-file-tool.ts
840
- import { z as z4 } from "zod";
413
+ import { z as z3 } from "zod";
414
+ import {
415
+ EnvironmentToolBase as EnvironmentToolBase3
416
+ } from "@ai-code-agents/environment-utils";
841
417
  var EditFileToolName = "edit_file";
842
- var EditFileToolInput = z4.object({
843
- path: z4.string().meta({
418
+ var EditFileToolInput = z3.object({
419
+ path: z3.string().meta({
844
420
  description: "The path to the file to edit, relative to the project directory."
845
421
  }),
846
- oldString: z4.string().meta({
422
+ oldString: z3.string().meta({
847
423
  description: "The exact string to replace in the file."
848
424
  }),
849
- newString: z4.string().meta({
425
+ newString: z3.string().meta({
850
426
  description: "The string to replace the old string with."
851
427
  }),
852
- replaceAll: z4.boolean().optional().meta({
428
+ replaceAll: z3.boolean().optional().meta({
853
429
  description: "Whether to replace all occurrences of the old string. Defaults to false."
854
430
  })
855
431
  });
856
- var EditFileToolOutput = z4.object({
857
- path: z4.string().meta({
432
+ var EditFileToolOutput = z3.object({
433
+ path: z3.string().meta({
858
434
  description: "The path to the file that was edited."
859
435
  }),
860
- oldString: z4.string().meta({
436
+ oldString: z3.string().meta({
861
437
  description: "The old string that was replaced."
862
438
  }),
863
- newString: z4.string().meta({
439
+ newString: z3.string().meta({
864
440
  description: "The new string that replaced the old string."
865
441
  }),
866
- replacements: z4.number().meta({
442
+ replacements: z3.number().meta({
867
443
  description: "The number of replacements made."
868
444
  }),
869
- message: z4.string().meta({
445
+ message: z3.string().meta({
870
446
  description: "A message indicating the result of the edit operation."
871
447
  })
872
448
  });
873
- function escapeRegExp(string2) {
874
- return string2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
449
+ function escapeRegExp(string) {
450
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
875
451
  }
876
- var EditFileTool = class extends EnvironmentToolBase {
452
+ var EditFileTool = class extends EnvironmentToolBase3 {
877
453
  /**
878
454
  * Returns the metadata for the tool.
879
455
  *
@@ -928,10 +504,11 @@ var EditFileTool = class extends EnvironmentToolBase {
928
504
  /**
929
505
  * Converts the tool output to a format suitable for model consumption.
930
506
  *
931
- * @param output - The output from the tool execution.
507
+ * @param options - The tool result, including the output from the tool execution.
932
508
  * @returns The formatted tool result.
933
509
  */
934
- toModelOutput(output) {
510
+ toModelOutput(options) {
511
+ const { output } = options;
935
512
  return {
936
513
  type: "text",
937
514
  value: `Edited file \`${output.path}\` with ${output.replacements} replacement(s).`
@@ -979,7 +556,11 @@ var EditFileTool = class extends EnvironmentToolBase {
979
556
  };
980
557
 
981
558
  // src/tools/get-project-file-structure-tool.ts
982
- import { z as z5 } from "zod";
559
+ import { z as z4 } from "zod";
560
+ import {
561
+ EnvironmentToolBase as EnvironmentToolBase4,
562
+ escapeCommandArg as escapeCommandArg4
563
+ } from "@ai-code-agents/environment-utils";
983
564
 
984
565
  // src/util/build-tree-from-files.ts
985
566
  function renderTree(node, prefix = "") {
@@ -995,46 +576,133 @@ function renderTree(node, prefix = "") {
995
576
  result += renderTree(node[entry], nextPrefix);
996
577
  }
997
578
  }
998
- return result;
579
+ return result;
580
+ }
581
+ function buildTreeFromFiles(files) {
582
+ if (files.length === 0) {
583
+ return "";
584
+ }
585
+ const sortedFiles = [...files].sort();
586
+ const tree = {};
587
+ for (const file of sortedFiles) {
588
+ const parts = file.split("/");
589
+ let current = tree;
590
+ for (const part of parts) {
591
+ if (!current[part]) {
592
+ current[part] = {};
593
+ }
594
+ current = current[part];
595
+ }
596
+ }
597
+ return renderTree(tree).trim();
598
+ }
599
+
600
+ // src/util/get-gitignored-paths.ts
601
+ import path3 from "path";
602
+ import {
603
+ escapeCommandArg as escapeCommandArg3
604
+ } from "@ai-code-agents/environment-utils";
605
+ async function getGitIgnoredPaths(env) {
606
+ const gitignorePath = await getClosestGitIgnorePath(env);
607
+ if (!gitignorePath) {
608
+ return [];
609
+ }
610
+ const { stdout: pwd } = await env.runCommand("pwd");
611
+ const currentDir = pwd.trim();
612
+ const gitignoreDir = path3.dirname(gitignorePath);
613
+ try {
614
+ const { stdout, exitCode } = await env.runCommand(
615
+ `cat ${escapeCommandArg3(gitignorePath)}`
616
+ );
617
+ if (exitCode !== 0) {
618
+ return [];
619
+ }
620
+ const rawRules = stdout.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
621
+ const relPath = path3.relative(gitignoreDir, currentDir);
622
+ if (relPath === "") {
623
+ return rawRules.map(
624
+ (rule) => rule.startsWith("/") ? rule.slice(1) : rule
625
+ );
626
+ }
627
+ const relPathSegments = relPath.split(path3.sep);
628
+ const sanitizedRules = [];
629
+ for (const rule of rawRules) {
630
+ const isAnchored = rule.startsWith("/") || rule.includes("/") && rule.indexOf("/") !== rule.length - 1;
631
+ if (!isAnchored || rule.startsWith("**/")) {
632
+ sanitizedRules.push(rule.startsWith("**/") ? rule.slice(3) : rule);
633
+ continue;
634
+ }
635
+ const normalizedRule = rule.startsWith("/") ? rule.slice(1) : rule;
636
+ const cleanRule = normalizedRule.endsWith("/") ? normalizedRule.slice(0, -1) : normalizedRule;
637
+ const ruleSegments = cleanRule.split("/");
638
+ let matches = true;
639
+ let i = 0;
640
+ for (; i < relPathSegments.length; i++) {
641
+ if (i >= ruleSegments.length) {
642
+ sanitizedRules.push(".");
643
+ matches = false;
644
+ break;
645
+ }
646
+ if (!matchSegment(ruleSegments[i], relPathSegments[i])) {
647
+ matches = false;
648
+ break;
649
+ }
650
+ }
651
+ if (matches) {
652
+ const remaining = ruleSegments.slice(i).join("/");
653
+ if (remaining) {
654
+ sanitizedRules.push(
655
+ normalizedRule.endsWith("/") && !remaining.endsWith("/") ? `${remaining}/` : remaining
656
+ );
657
+ } else {
658
+ sanitizedRules.push(".");
659
+ }
660
+ }
661
+ }
662
+ return [...new Set(sanitizedRules)];
663
+ } catch (_error) {
664
+ return [];
665
+ }
999
666
  }
1000
- function buildTreeFromFiles(files) {
1001
- if (files.length === 0) {
1002
- return "";
667
+ function matchSegment(pattern, segment) {
668
+ if (pattern === "*") {
669
+ return true;
1003
670
  }
1004
- const sortedFiles = [...files].sort();
1005
- const tree = {};
1006
- for (const file of sortedFiles) {
1007
- const parts = file.split("/");
1008
- let current = tree;
1009
- for (const part of parts) {
1010
- if (!current[part]) {
1011
- current[part] = {};
1012
- }
1013
- current = current[part];
1014
- }
671
+ if (pattern === segment) {
672
+ return true;
1015
673
  }
1016
- return renderTree(tree).trim();
674
+ if (pattern.includes("*") || pattern.includes("?")) {
675
+ const regexStr = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
676
+ const regex = new RegExp(`^${regexStr}$`);
677
+ return regex.test(segment);
678
+ }
679
+ return false;
680
+ }
681
+ async function getClosestGitIgnorePath(env) {
682
+ const command = 'd=$PWD; while [ -n "$d" ] && [ ! -f "$d/.gitignore" ]; do d=${d%/*}; done; [ -f "$d/.gitignore" ] && echo "$d/.gitignore"';
683
+ const { stdout } = await env.runCommand(command);
684
+ return stdout.trim();
1017
685
  }
1018
686
 
1019
687
  // src/tools/get-project-file-structure-tool.ts
1020
688
  var GetProjectFileStructureToolName = "get_project_file_structure";
1021
- var GetProjectFileStructureToolInput = z5.object({
1022
- path: z5.string().optional().meta({
689
+ var GetProjectFileStructureToolInput = z4.object({
690
+ path: z4.string().optional().meta({
1023
691
  description: 'Root path to list files from, relative to the project directory. Defaults to ".".'
1024
692
  }),
1025
- excludeGitIgnored: z5.boolean().optional().meta({
693
+ excludeGitIgnored: z4.boolean().optional().meta({
1026
694
  description: "Whether to exclude files ignored by Git. Defaults to true."
1027
695
  })
1028
696
  });
1029
- var GetProjectFileStructureToolOutput = z5.object({
1030
- files: z5.array(z5.string()).meta({
697
+ var GetProjectFileStructureToolOutput = z4.object({
698
+ files: z4.array(z4.string()).meta({
1031
699
  description: "List of all file paths found, relative to the root path."
1032
700
  }),
1033
- excludeGitIgnored: z5.boolean().meta({
701
+ excludeGitIgnored: z4.boolean().meta({
1034
702
  description: "Whether files ignored by Git were excluded."
1035
703
  })
1036
704
  });
1037
- var GetProjectFileStructureTool = class extends EnvironmentToolBase {
705
+ var GetProjectFileStructureTool = class extends EnvironmentToolBase4 {
1038
706
  /**
1039
707
  * Returns the metadata for the tool.
1040
708
  *
@@ -1060,21 +728,16 @@ var GetProjectFileStructureTool = class extends EnvironmentToolBase {
1060
728
  */
1061
729
  async executeForEnvironment(env, input) {
1062
730
  const { path: path5 = ".", excludeGitIgnored = true } = input;
1063
- const escapedPath = escapeCommandArg(path5);
731
+ const escapedPath = escapeCommandArg4(path5);
1064
732
  let command = `find ${escapedPath} -type f`;
1065
733
  if (excludeGitIgnored) {
1066
- let gitIgnoredPaths = [];
1067
- try {
1068
- const { content: gitignoreContent } = await env.readFile(".gitignore");
1069
- gitIgnoredPaths = gitignoreContent.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
1070
- } catch (_error) {
1071
- }
734
+ const gitIgnoredPaths = await getGitIgnoredPaths(env);
1072
735
  for (const gitIgnoredPath of gitIgnoredPaths) {
1073
736
  if (!gitIgnoredPath.endsWith("/")) {
1074
- const escapedPath2 = escapeCommandArg(`*/${gitIgnoredPath}/*`);
1075
- command += ` -not -name ${escapeCommandArg(gitIgnoredPath)} -not -path ${escapedPath2}`;
737
+ const escapedPath2 = escapeCommandArg4(`*/${gitIgnoredPath}/*`);
738
+ command += ` -not -name ${escapeCommandArg4(gitIgnoredPath)} -not -path ${escapedPath2}`;
1076
739
  } else {
1077
- const escapedPath2 = escapeCommandArg(`*/${gitIgnoredPath}*`);
740
+ const escapedPath2 = escapeCommandArg4(`*/${gitIgnoredPath}*`);
1078
741
  command += ` -not -path ${escapedPath2}`;
1079
742
  }
1080
743
  }
@@ -1096,10 +759,11 @@ var GetProjectFileStructureTool = class extends EnvironmentToolBase {
1096
759
  /**
1097
760
  * Converts the tool output to a format suitable for model consumption.
1098
761
  *
1099
- * @param output - The output from the tool execution.
762
+ * @param options - The tool result, including the output from the tool execution.
1100
763
  * @returns The formatted tool result.
1101
764
  */
1102
- toModelOutput(output) {
765
+ toModelOutput(options) {
766
+ const { output } = options;
1103
767
  const tree = buildTreeFromFiles(output.files);
1104
768
  if (!tree) {
1105
769
  return {
@@ -1137,7 +801,12 @@ var GetProjectFileStructureTool = class extends EnvironmentToolBase {
1137
801
  };
1138
802
 
1139
803
  // src/tools/glob-tool.ts
1140
- import { z as z6 } from "zod";
804
+ import { z as z5 } from "zod";
805
+ import {
806
+ EnvironmentToolBase as EnvironmentToolBase5,
807
+ escapeCommandArg as escapeCommandArg5,
808
+ validateRelativePath
809
+ } from "@ai-code-agents/environment-utils";
1141
810
 
1142
811
  // src/util/glob-to-reg-exp.ts
1143
812
  function globToRegExp(glob) {
@@ -1185,32 +854,32 @@ function globToRegExp(glob) {
1185
854
 
1186
855
  // src/tools/glob-tool.ts
1187
856
  var GlobToolName = "glob";
1188
- var GlobToolInput = z6.object({
1189
- searchPattern: z6.string().meta({
857
+ var GlobToolInput = z5.object({
858
+ searchPattern: z5.string().meta({
1190
859
  description: 'The glob pattern to search for, relative to the search path / project directory (e.g. "**/*.ts", "docs/*.md").'
1191
860
  }),
1192
- searchPath: z6.string().optional().meta({
861
+ searchPath: z5.string().optional().meta({
1193
862
  description: "The path to search within, relative to the project directory. Defaults to the project directory."
1194
863
  }),
1195
- excludeGitIgnored: z6.boolean().optional().meta({
864
+ excludeGitIgnored: z5.boolean().optional().meta({
1196
865
  description: "Whether to exclude files ignored by Git. Defaults to true."
1197
866
  })
1198
867
  });
1199
- var GlobToolOutput = z6.object({
1200
- searchPattern: z6.string().meta({
868
+ var GlobToolOutput = z5.object({
869
+ searchPattern: z5.string().meta({
1201
870
  description: "The glob pattern that was searched for."
1202
871
  }),
1203
- searchPath: z6.string().meta({
872
+ searchPath: z5.string().meta({
1204
873
  description: "The path that was searched within."
1205
874
  }),
1206
- excludeGitIgnored: z6.boolean().meta({
875
+ excludeGitIgnored: z5.boolean().meta({
1207
876
  description: "Whether files ignored by Git were excluded."
1208
877
  }),
1209
- matchingPaths: z6.array(z6.string()).meta({
878
+ matchingPaths: z5.array(z5.string()).meta({
1210
879
  description: "The list of file paths that matched the glob search, relative to the project directory."
1211
880
  })
1212
881
  });
1213
- var GlobTool = class extends EnvironmentToolBase {
882
+ var GlobTool = class extends EnvironmentToolBase5 {
1214
883
  /**
1215
884
  * Returns the metadata for the tool.
1216
885
  *
@@ -1245,21 +914,16 @@ var GlobTool = class extends EnvironmentToolBase {
1245
914
  validateRelativePath(searchPath);
1246
915
  }
1247
916
  const untrailingslashedSearchPath = searchPath === "" ? "." : searchPath.replace(/\/+$/, "");
1248
- const escapedSearchPath = escapeCommandArg(untrailingslashedSearchPath);
917
+ const escapedSearchPath = escapeCommandArg5(untrailingslashedSearchPath);
1249
918
  let command = `find ${escapedSearchPath} -type f`;
1250
919
  if (excludeGitIgnored) {
1251
- let gitIgnoredPaths = [];
1252
- try {
1253
- const { content: gitignoreContent } = await env.readFile(".gitignore");
1254
- gitIgnoredPaths = gitignoreContent.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
1255
- } catch (_error) {
1256
- }
920
+ const gitIgnoredPaths = await getGitIgnoredPaths(env);
1257
921
  for (const gitIgnoredPath of gitIgnoredPaths) {
1258
922
  if (!gitIgnoredPath.endsWith("/")) {
1259
- const escapedPath = escapeCommandArg(`*/${gitIgnoredPath}/*`);
1260
- command += ` -not -name ${escapeCommandArg(gitIgnoredPath)} -not -path ${escapedPath}`;
923
+ const escapedPath = escapeCommandArg5(`*/${gitIgnoredPath}/*`);
924
+ command += ` -not -name ${escapeCommandArg5(gitIgnoredPath)} -not -path ${escapedPath}`;
1261
925
  } else {
1262
- const escapedPath = escapeCommandArg(`*/${gitIgnoredPath}*`);
926
+ const escapedPath = escapeCommandArg5(`*/${gitIgnoredPath}*`);
1263
927
  command += ` -not -path ${escapedPath}`;
1264
928
  }
1265
929
  }
@@ -1295,10 +959,11 @@ var GlobTool = class extends EnvironmentToolBase {
1295
959
  /**
1296
960
  * Converts the tool output to a format suitable for model consumption.
1297
961
  *
1298
- * @param output - The output from the tool execution.
962
+ * @param options - The tool result, including the output from the tool execution.
1299
963
  * @returns The formatted tool result.
1300
964
  */
1301
- toModelOutput(output) {
965
+ toModelOutput(options) {
966
+ const { output } = options;
1302
967
  if (output.matchingPaths.length === 0) {
1303
968
  return {
1304
969
  type: "text",
@@ -1347,8 +1012,276 @@ ${bulletPoints}
1347
1012
  }
1348
1013
  };
1349
1014
 
1015
+ // src/tools/grep-tool.ts
1016
+ import { z as z6 } from "zod";
1017
+ import {
1018
+ EnvironmentToolBase as EnvironmentToolBase6,
1019
+ escapeCommandArg as escapeCommandArg6,
1020
+ validateRelativePath as validateRelativePath2
1021
+ } from "@ai-code-agents/environment-utils";
1022
+ var GrepToolName = "grep";
1023
+ var GrepToolInput = z6.object({
1024
+ regexpPattern: z6.string().meta({
1025
+ description: "The regular expression pattern to search for in file contents."
1026
+ }),
1027
+ searchPattern: z6.string().optional().meta({
1028
+ description: 'The glob pattern to filter which files are searched (e.g. "**/*.ts"). If omitted, searches all files.'
1029
+ }),
1030
+ searchPath: z6.string().optional().meta({
1031
+ description: "The path to search within, relative to the project directory. Defaults to the project directory."
1032
+ }),
1033
+ contextLines: z6.number().int().nonnegative().optional().meta({
1034
+ description: "The number of context lines to include before and after each match."
1035
+ })
1036
+ });
1037
+ var GrepMatch = z6.object({
1038
+ path: z6.string().meta({
1039
+ description: "The path to the file containing the match, relative to the project directory."
1040
+ }),
1041
+ lineNumber: z6.number().int().meta({
1042
+ description: "The line number of the match (1-based)."
1043
+ }),
1044
+ line: z6.string().meta({
1045
+ description: "The content of the matching line."
1046
+ }),
1047
+ beforeContext: z6.array(z6.string()).optional().meta({
1048
+ description: "Lines of context before the match."
1049
+ }),
1050
+ afterContext: z6.array(z6.string()).optional().meta({
1051
+ description: "Lines of context after the match."
1052
+ })
1053
+ });
1054
+ var GrepToolOutput = z6.object({
1055
+ regexpPattern: z6.string().meta({
1056
+ description: "The regular expression pattern that was searched for."
1057
+ }),
1058
+ searchPattern: z6.string().optional().meta({
1059
+ description: "The glob pattern used to filter files."
1060
+ }),
1061
+ searchPath: z6.string().optional().meta({
1062
+ description: "The path that was searched within."
1063
+ }),
1064
+ contextLines: z6.number().optional().meta({
1065
+ description: "The number of context lines included."
1066
+ }),
1067
+ matches: z6.array(GrepMatch).meta({
1068
+ description: "The list of matches found."
1069
+ })
1070
+ });
1071
+ var GrepTool = class extends EnvironmentToolBase6 {
1072
+ /**
1073
+ * Returns the metadata for the tool.
1074
+ *
1075
+ * The name, description, and needsApproval properties are defaults which can be overridden in the constructor.
1076
+ *
1077
+ * @returns The tool metadata.
1078
+ */
1079
+ getMetadata() {
1080
+ return {
1081
+ name: GrepToolName,
1082
+ description: "Searches for a regular expression pattern within the content of files in the project.",
1083
+ inputSchema: GrepToolInput,
1084
+ outputSchema: GrepToolOutput,
1085
+ needsApproval: false
1086
+ };
1087
+ }
1088
+ /**
1089
+ * Executes the tool in the given execution environment with the given input.
1090
+ *
1091
+ * @param env - The execution environment to use.
1092
+ * @param input - The input for the tool.
1093
+ * @returns A promise that resolves to the tool execution result.
1094
+ */
1095
+ async executeForEnvironment(env, input) {
1096
+ const {
1097
+ regexpPattern,
1098
+ searchPattern,
1099
+ searchPath = "",
1100
+ contextLines = 0
1101
+ } = input;
1102
+ if (searchPath) {
1103
+ validateRelativePath2(searchPath);
1104
+ }
1105
+ const globTool = new GlobTool(env);
1106
+ const globResult = await globTool.execute(
1107
+ {
1108
+ searchPattern: searchPattern || "**/*",
1109
+ searchPath
1110
+ },
1111
+ {}
1112
+ );
1113
+ const filesToSearch = globResult.matchingPaths;
1114
+ if (filesToSearch.length === 0) {
1115
+ return {
1116
+ regexpPattern,
1117
+ searchPattern,
1118
+ searchPath,
1119
+ contextLines,
1120
+ matches: []
1121
+ };
1122
+ }
1123
+ const BATCH_SIZE = 50;
1124
+ const matches = [];
1125
+ for (let i = 0; i < filesToSearch.length; i += BATCH_SIZE) {
1126
+ const batch = filesToSearch.slice(i, i + BATCH_SIZE);
1127
+ const escapedFilePaths = batch.map(escapeCommandArg6).join(" ");
1128
+ const command = `grep -n -H -I -E ${escapeCommandArg6(regexpPattern)} ${escapedFilePaths}`;
1129
+ const { stdout, exitCode } = await env.runCommand(command);
1130
+ if (exitCode > 1) {
1131
+ throw new Error(`Failed to execute grep command "${command}".`);
1132
+ }
1133
+ if (stdout) {
1134
+ const lines = stdout.split("\n");
1135
+ for (const line of lines) {
1136
+ if (!line.trim()) continue;
1137
+ const firstColonIndex = line.indexOf(":");
1138
+ if (firstColonIndex === -1) continue;
1139
+ const secondColonIndex = line.indexOf(":", firstColonIndex + 1);
1140
+ if (secondColonIndex === -1) continue;
1141
+ const filePath = line.substring(0, firstColonIndex);
1142
+ const lineNumberStr = line.substring(
1143
+ firstColonIndex + 1,
1144
+ secondColonIndex
1145
+ );
1146
+ const content = line.substring(secondColonIndex + 1);
1147
+ const lineNumber = parseInt(lineNumberStr, 10);
1148
+ if (isNaN(lineNumber)) continue;
1149
+ matches.push({
1150
+ path: filePath,
1151
+ lineNumber,
1152
+ line: content
1153
+ });
1154
+ }
1155
+ }
1156
+ }
1157
+ if (contextLines > 0 && matches.length > 0) {
1158
+ const matchesByFile = /* @__PURE__ */ new Map();
1159
+ for (const match of matches) {
1160
+ if (!matchesByFile.has(match.path)) {
1161
+ matchesByFile.set(match.path, []);
1162
+ }
1163
+ matchesByFile.get(match.path).push(match);
1164
+ }
1165
+ for (const [filePath, fileMatches] of matchesByFile) {
1166
+ try {
1167
+ const { content } = await env.readFile(filePath);
1168
+ const lines = content.split("\n");
1169
+ for (const match of fileMatches) {
1170
+ const lineIndex = match.lineNumber - 1;
1171
+ const start = Math.max(0, lineIndex - contextLines);
1172
+ const end = Math.min(lines.length, lineIndex + contextLines + 1);
1173
+ match.beforeContext = lines.slice(start, lineIndex);
1174
+ match.afterContext = lines.slice(lineIndex + 1, end);
1175
+ }
1176
+ } catch (_error) {
1177
+ }
1178
+ }
1179
+ }
1180
+ return {
1181
+ regexpPattern,
1182
+ searchPattern,
1183
+ searchPath,
1184
+ contextLines,
1185
+ matches
1186
+ };
1187
+ }
1188
+ /**
1189
+ * Converts the tool output to a format suitable for model consumption.
1190
+ *
1191
+ * @param options - The tool result, including the output from the tool execution.
1192
+ * @returns The formatted tool result.
1193
+ */
1194
+ toModelOutput(options) {
1195
+ const { output } = options;
1196
+ if (output.matches.length === 0) {
1197
+ return {
1198
+ type: "text",
1199
+ value: "No matches found."
1200
+ };
1201
+ }
1202
+ let result = `Found ${output.matches.length} matches:
1203
+ `;
1204
+ const matchesByFile = /* @__PURE__ */ new Map();
1205
+ for (const match of output.matches) {
1206
+ if (!matchesByFile.has(match.path)) {
1207
+ matchesByFile.set(match.path, []);
1208
+ }
1209
+ matchesByFile.get(match.path).push(match);
1210
+ }
1211
+ for (const [filePath, matches] of matchesByFile) {
1212
+ result += `
1213
+ File: ${filePath}
1214
+ `;
1215
+ for (const match of matches) {
1216
+ if (match.beforeContext && match.beforeContext.length > 0) {
1217
+ match.beforeContext.forEach((line, idx) => {
1218
+ result += ` ${match.lineNumber - match.beforeContext.length + idx}: ${line}
1219
+ `;
1220
+ });
1221
+ }
1222
+ result += `> ${match.lineNumber}: ${match.line}
1223
+ `;
1224
+ if (match.afterContext && match.afterContext.length > 0) {
1225
+ match.afterContext.forEach((line, idx) => {
1226
+ result += ` ${match.lineNumber + 1 + idx}: ${line}
1227
+ `;
1228
+ });
1229
+ }
1230
+ if (output.contextLines && output.contextLines > 0) {
1231
+ result += "---\n";
1232
+ }
1233
+ }
1234
+ }
1235
+ return {
1236
+ type: "text",
1237
+ value: result
1238
+ };
1239
+ }
1240
+ /**
1241
+ * Gets the examples for the tool.
1242
+ *
1243
+ * @returns The tool examples.
1244
+ */
1245
+ get examples() {
1246
+ return [
1247
+ {
1248
+ input: {
1249
+ regexpPattern: "interface.*Tool",
1250
+ searchPattern: "src/**/*.ts"
1251
+ },
1252
+ output: `Found 2 matches:
1253
+
1254
+ File: src/types.ts
1255
+ > 120: export interface ToolInterface<ToolInputType, ToolOutputType> {
1256
+ > 135: export interface EnvironmentToolInterface<
1257
+
1258
+ File: src/tools/tool-base.ts
1259
+ > 10: export abstract class ToolBase<
1260
+ `
1261
+ },
1262
+ {
1263
+ input: {
1264
+ regexpPattern: "TODO",
1265
+ contextLines: 1
1266
+ },
1267
+ output: `Found 1 matches:
1268
+
1269
+ File: src/index.ts
1270
+ 10: // Some code before
1271
+ > 11: // TODO: Implement feature X
1272
+ 12: // Some code after
1273
+ `
1274
+ }
1275
+ ];
1276
+ }
1277
+ };
1278
+
1350
1279
  // src/tools/list-directory-tool.ts
1351
1280
  import { z as z7 } from "zod";
1281
+ import {
1282
+ EnvironmentToolBase as EnvironmentToolBase7,
1283
+ escapeCommandArg as escapeCommandArg7
1284
+ } from "@ai-code-agents/environment-utils";
1352
1285
  var ListDirectoryToolName = "list_directory";
1353
1286
  var ListDirectoryToolInput = z7.object({
1354
1287
  path: z7.string().meta({
@@ -1366,7 +1299,7 @@ var ListDirectoryToolOutput = z7.object({
1366
1299
  description: "List of subdirectories in the directory."
1367
1300
  })
1368
1301
  });
1369
- var ListDirectoryTool = class extends EnvironmentToolBase {
1302
+ var ListDirectoryTool = class extends EnvironmentToolBase7 {
1370
1303
  /**
1371
1304
  * Returns the metadata for the tool.
1372
1305
  *
@@ -1391,7 +1324,7 @@ var ListDirectoryTool = class extends EnvironmentToolBase {
1391
1324
  * @returns A promise that resolves to the tool execution result.
1392
1325
  */
1393
1326
  async executeForEnvironment(env, input) {
1394
- const escapedPath = escapeCommandArg(input.path);
1327
+ const escapedPath = escapeCommandArg7(input.path);
1395
1328
  const command = `ls -la ${escapedPath}`;
1396
1329
  const { stdout, stderr, exitCode } = await env.runCommand(command);
1397
1330
  if (exitCode !== 0) {
@@ -1424,10 +1357,11 @@ var ListDirectoryTool = class extends EnvironmentToolBase {
1424
1357
  /**
1425
1358
  * Converts the tool output to a format suitable for model consumption.
1426
1359
  *
1427
- * @param output - The output from the tool execution.
1360
+ * @param options - The tool result, including the output from the tool execution.
1428
1361
  * @returns The formatted tool result.
1429
1362
  */
1430
- toModelOutput(output) {
1363
+ toModelOutput(options) {
1364
+ const { output } = options;
1431
1365
  const formatEntries = (entries, type) => {
1432
1366
  if (entries.length === 0) {
1433
1367
  return `No ${type} found.`;
@@ -1474,6 +1408,10 @@ Directories:
1474
1408
 
1475
1409
  // src/tools/move-file-tool.ts
1476
1410
  import { z as z8 } from "zod";
1411
+ import {
1412
+ EnvironmentToolBase as EnvironmentToolBase8,
1413
+ MoveFileResult
1414
+ } from "@ai-code-agents/environment-utils";
1477
1415
  var MoveFileToolName = "move_file";
1478
1416
  var MoveFileToolInput = z8.object({
1479
1417
  sourcePath: z8.string().meta({
@@ -1484,7 +1422,7 @@ var MoveFileToolInput = z8.object({
1484
1422
  })
1485
1423
  });
1486
1424
  var MoveFileToolOutput = MoveFileResult;
1487
- var MoveFileTool = class extends EnvironmentToolBase {
1425
+ var MoveFileTool = class extends EnvironmentToolBase8 {
1488
1426
  /**
1489
1427
  * Returns the metadata for the tool.
1490
1428
  *
@@ -1514,10 +1452,11 @@ var MoveFileTool = class extends EnvironmentToolBase {
1514
1452
  /**
1515
1453
  * Converts the tool output to a format suitable for model consumption.
1516
1454
  *
1517
- * @param output - The output from the tool execution.
1455
+ * @param options - The tool result, including the output from the tool execution.
1518
1456
  * @returns The formatted tool result.
1519
1457
  */
1520
- toModelOutput(output) {
1458
+ toModelOutput(options) {
1459
+ const { output } = options;
1521
1460
  return {
1522
1461
  type: "text",
1523
1462
  value: `File \`${output.sourcePath}\` moved successfully to \`${output.destinationPath}\`.`
@@ -1543,6 +1482,10 @@ var MoveFileTool = class extends EnvironmentToolBase {
1543
1482
 
1544
1483
  // src/tools/read-file-tool.ts
1545
1484
  import { z as z9 } from "zod";
1485
+ import {
1486
+ EnvironmentToolBase as EnvironmentToolBase9,
1487
+ ReadFileResult
1488
+ } from "@ai-code-agents/environment-utils";
1546
1489
 
1547
1490
  // src/util/get-language-identifier-from-file-path.ts
1548
1491
  import path4 from "path";
@@ -1720,7 +1663,7 @@ ${output.content}
1720
1663
  \`\`\`
1721
1664
  `;
1722
1665
  };
1723
- var ReadFileTool = class extends EnvironmentToolBase {
1666
+ var ReadFileTool = class extends EnvironmentToolBase9 {
1724
1667
  /**
1725
1668
  * Returns the metadata for the tool.
1726
1669
  *
@@ -1750,10 +1693,11 @@ var ReadFileTool = class extends EnvironmentToolBase {
1750
1693
  /**
1751
1694
  * Converts the tool output to a format suitable for model consumption.
1752
1695
  *
1753
- * @param output - The output from the tool execution.
1696
+ * @param options - The tool result, including the output from the tool execution.
1754
1697
  * @returns The formatted tool result.
1755
1698
  */
1756
- toModelOutput(output) {
1699
+ toModelOutput(options) {
1700
+ const { output } = options;
1757
1701
  return {
1758
1702
  type: "text",
1759
1703
  value: formatModelResponse(output)
@@ -1797,13 +1741,17 @@ export function Loader(props: LoaderProps) {
1797
1741
 
1798
1742
  // src/tools/read-many-files-tool.ts
1799
1743
  import { z as z10 } from "zod";
1744
+ import {
1745
+ EnvironmentToolBase as EnvironmentToolBase10,
1746
+ ReadFileResult as ReadFileResult2
1747
+ } from "@ai-code-agents/environment-utils";
1800
1748
  var ReadManyFilesToolName = "read_many_files";
1801
1749
  var ReadManyFilesToolInput = z10.object({
1802
1750
  paths: z10.array(z10.string()).meta({
1803
1751
  description: "The paths to the files to read, relative to the project directory."
1804
1752
  })
1805
1753
  });
1806
- var ReadManyFilesToolOutput = z10.record(z10.string(), ReadFileResult);
1754
+ var ReadManyFilesToolOutput = z10.record(z10.string(), ReadFileResult2);
1807
1755
  var formatModelResponse2 = (output) => {
1808
1756
  const language = getLanguageIdentifierFromFilePath(output.path);
1809
1757
  return `File: \`${output.path}\`
@@ -1813,7 +1761,7 @@ ${output.content}
1813
1761
  \`\`\`
1814
1762
  `;
1815
1763
  };
1816
- var ReadManyFilesTool = class extends EnvironmentToolBase {
1764
+ var ReadManyFilesTool = class extends EnvironmentToolBase10 {
1817
1765
  /**
1818
1766
  * Returns the metadata for the tool.
1819
1767
  *
@@ -1849,10 +1797,11 @@ var ReadManyFilesTool = class extends EnvironmentToolBase {
1849
1797
  /**
1850
1798
  * Converts the tool output to a format suitable for model consumption.
1851
1799
  *
1852
- * @param output - The output from the tool execution.
1800
+ * @param options - The tool result, including the output from the tool execution.
1853
1801
  * @returns The formatted tool result.
1854
1802
  */
1855
- toModelOutput(output) {
1803
+ toModelOutput(options) {
1804
+ const { output } = options;
1856
1805
  const fileContentResponses = Object.values(output).map(
1857
1806
  (fileResult) => formatModelResponse2(fileResult)
1858
1807
  );
@@ -1909,6 +1858,10 @@ export function Loader(props: LoaderProps) {
1909
1858
 
1910
1859
  // src/tools/run-command-tool.ts
1911
1860
  import { z as z11 } from "zod";
1861
+ import {
1862
+ EnvironmentToolBase as EnvironmentToolBase11,
1863
+ RunCommandResult
1864
+ } from "@ai-code-agents/environment-utils";
1912
1865
  var RunCommandToolName = "run_command";
1913
1866
  var RunCommandToolInput = z11.object({
1914
1867
  command: z11.string().meta({ description: "The CLI command to run, including all arguments." })
@@ -1929,7 +1882,7 @@ Output (stdout): ${stdout}
1929
1882
  Error Output (stderr): ${stderr}
1930
1883
  `;
1931
1884
  }
1932
- var RunCommandTool = class extends EnvironmentToolBase {
1885
+ var RunCommandTool = class extends EnvironmentToolBase11 {
1933
1886
  /**
1934
1887
  * Returns the metadata for the tool.
1935
1888
  *
@@ -1959,10 +1912,11 @@ var RunCommandTool = class extends EnvironmentToolBase {
1959
1912
  /**
1960
1913
  * Converts the tool output to a format suitable for model consumption.
1961
1914
  *
1962
- * @param output - The output from the tool execution.
1915
+ * @param options - The tool result, including the output from the tool execution.
1963
1916
  * @returns The formatted tool result.
1964
1917
  */
1965
- toModelOutput(output) {
1918
+ toModelOutput(options) {
1919
+ const { output } = options;
1966
1920
  return {
1967
1921
  type: "text",
1968
1922
  value: formatCommandResultToModelResponse(output)
@@ -2034,6 +1988,10 @@ added 1 package, and changed 1 package in 2s
2034
1988
 
2035
1989
  // src/tools/write-file-tool.ts
2036
1990
  import { z as z12 } from "zod";
1991
+ import {
1992
+ EnvironmentToolBase as EnvironmentToolBase12,
1993
+ WriteFileResult
1994
+ } from "@ai-code-agents/environment-utils";
2037
1995
  var WriteFileToolName = "write_file";
2038
1996
  var WriteFileToolInput = z12.object({
2039
1997
  path: z12.string().meta({
@@ -2044,7 +2002,7 @@ var WriteFileToolInput = z12.object({
2044
2002
  })
2045
2003
  });
2046
2004
  var WriteFileToolOutput = WriteFileResult;
2047
- var WriteFileTool = class extends EnvironmentToolBase {
2005
+ var WriteFileTool = class extends EnvironmentToolBase12 {
2048
2006
  /**
2049
2007
  * Returns the metadata for the tool.
2050
2008
  *
@@ -2074,10 +2032,11 @@ var WriteFileTool = class extends EnvironmentToolBase {
2074
2032
  /**
2075
2033
  * Converts the tool output to a format suitable for model consumption.
2076
2034
  *
2077
- * @param output - The output from the tool execution.
2035
+ * @param options - The tool result, including the output from the tool execution.
2078
2036
  * @returns The formatted tool result.
2079
2037
  */
2080
- toModelOutput(output) {
2038
+ toModelOutput(options) {
2039
+ const { output } = options;
2081
2040
  return {
2082
2041
  type: "text",
2083
2042
  value: `File \`${output.path}\` written successfully.`
@@ -2107,6 +2066,9 @@ var WriteFileTool = class extends EnvironmentToolBase {
2107
2066
 
2108
2067
  // src/tools/submit-tool.ts
2109
2068
  import { z as z13 } from "zod";
2069
+ import {
2070
+ ToolBase
2071
+ } from "@ai-code-agents/environment-utils";
2110
2072
  var SubmitToolName = "submit";
2111
2073
  var SubmitToolInput = z13.object({});
2112
2074
  var SubmitToolOutput = z13.object({});
@@ -2131,7 +2093,7 @@ var SubmitTool = class extends ToolBase {
2131
2093
  * Executes the tool with the given input.
2132
2094
  *
2133
2095
  * @param _ - The input for the tool. Unused.
2134
- * @param __ - Options from the tool call. Unused.
2096
+ * @param __ - Options for the tool execution. Unused.
2135
2097
  * @returns A promise that resolves to the tool execution result.
2136
2098
  */
2137
2099
  async execute(_, __) {
@@ -2140,7 +2102,7 @@ var SubmitTool = class extends ToolBase {
2140
2102
  /**
2141
2103
  * Converts the tool output to a format suitable for model consumption.
2142
2104
  *
2143
- * @param _ - The output from the tool execution. Unused.
2105
+ * @param _ - The tool result, including the output from the tool execution. Unused.
2144
2106
  * @returns The formatted tool result.
2145
2107
  */
2146
2108
  toModelOutput(_) {
@@ -2161,7 +2123,7 @@ var SubmitTool = class extends ToolBase {
2161
2123
 
2162
2124
  // src/agent-creators.ts
2163
2125
  import {
2164
- Experimental_Agent as Agent,
2126
+ ToolLoopAgent,
2165
2127
  stepCountIs,
2166
2128
  hasToolCall
2167
2129
  } from "ai";
@@ -2169,10 +2131,13 @@ import {
2169
2131
  // src/instructions.ts
2170
2132
  function getAdditionalInstructions(config) {
2171
2133
  const { maxSteps, allowSubmit, tools } = config;
2172
- const exampleSections = [];
2134
+ const exampleSections = [
2135
+ "# Tool Examples",
2136
+ "You have access to several tools to assist you in completing your task. Here are some examples of how to use them:"
2137
+ ];
2173
2138
  for (const [toolName, tool] of Object.entries(tools)) {
2174
2139
  if ("examples" in tool && Array.isArray(tool.examples) && tool.examples.length > 0) {
2175
- let toolSection = `### Tool: \`${toolName}\`
2140
+ let toolSection = `## Tool: \`${toolName}\`
2176
2141
 
2177
2142
  `;
2178
2143
  for (const example of tool.examples) {
@@ -2181,40 +2146,48 @@ function getAdditionalInstructions(config) {
2181
2146
  exampleSections.push(toolSection.trim());
2182
2147
  }
2183
2148
  }
2184
- const workflowGuidelines = [
2185
- /*
2186
- * If there are examples, the tool information is already mentioned in a separate Tool Examples section.
2187
- * Therefore the line below is only relevant if there are no examples.
2188
- */
2189
- ...!exampleSections.length ? [
2190
- "You have access to several tools to assist you in completing your task."
2191
- ] : [],
2192
- "You must issue tool calls to complete your task. Do not engage with the user directly.",
2193
- ...allowSubmit ? [
2194
- `Once you think you have completed your task, call the \`${SubmitToolName}\` tool to submit your results.`
2195
- ] : [],
2196
- `You have a maximum of ${maxSteps} steps to complete your task.`
2197
- ];
2198
- const importantWorkflowGuidelines = `## Important Workflow Guidelines
2199
-
2200
- ${workflowGuidelines.map((line) => `- ${line}`).join("\n")}
2201
-
2202
- Remember, you don't get to ask the user any clarifying questions, just use the tools available to complete your task. You're on your own now.
2149
+ const constraintSections = ["# Behavioral Guidelines"];
2150
+ const constraintsByType = getCodeAgentConstraints({ maxSteps, allowSubmit });
2151
+ if (constraintsByType.must && constraintsByType.must.length > 0) {
2152
+ let constraintSection = "## You MUST:\n\n";
2153
+ for (const constraint of constraintsByType.must) {
2154
+ constraintSection += `- ${constraint}
2203
2155
  `;
2204
- if (exampleSections.length) {
2205
- return `## Tool Examples
2206
-
2207
- You have access to several tools to assist you in completing your task. Here are some examples of how to use them:
2208
-
2209
- ${exampleSections.join("\n\n")}
2210
-
2211
- ` + importantWorkflowGuidelines;
2156
+ }
2157
+ constraintSections.push(constraintSection.trim());
2158
+ }
2159
+ if (constraintsByType.must_not && constraintsByType.must_not.length > 0) {
2160
+ let constraintSection = "## You MUST NOT:\n\n";
2161
+ for (const constraint of constraintsByType.must_not) {
2162
+ constraintSection += `- ${constraint}
2163
+ `;
2164
+ }
2165
+ constraintSections.push(constraintSection.trim());
2166
+ }
2167
+ if (constraintsByType.should && constraintsByType.should.length > 0) {
2168
+ let constraintSection = "## You SHOULD:\n\n";
2169
+ for (const constraint of constraintsByType.should) {
2170
+ constraintSection += `- ${constraint}
2171
+ `;
2172
+ }
2173
+ constraintSections.push(constraintSection.trim());
2174
+ }
2175
+ if (constraintsByType.should_not && constraintsByType.should_not.length > 0) {
2176
+ let constraintSection = "## You SHOULD NOT:\n\n";
2177
+ for (const constraint of constraintsByType.should_not) {
2178
+ constraintSection += `- ${constraint}
2179
+ `;
2180
+ }
2181
+ constraintSections.push(constraintSection.trim());
2212
2182
  }
2213
- return importantWorkflowGuidelines;
2183
+ const finalReminder = getCodeAgentFinalReminder();
2184
+ return [...exampleSections, ...constraintSections, finalReminder].join(
2185
+ "\n\n"
2186
+ );
2214
2187
  }
2215
2188
  function formatExampleForInstructions(toolName, example) {
2216
- const input = typeof example.input === "undefined" ? "" : typeof example.input === "string" || typeof example.input === "number" ? example.input : JSON.stringify(example.input, null, 2);
2217
- const output = typeof example.output === "undefined" ? "" : typeof example.output === "string" || typeof example.output === "number" ? example.output : JSON.stringify(example.output, null, 2);
2189
+ const input = formatValueForExample(example.input);
2190
+ const output = formatValueForExample(example.output);
2218
2191
  if (output === "") {
2219
2192
  return `<example>
2220
2193
  <tool_call>
@@ -2231,8 +2204,42 @@ ${output}
2231
2204
  </tool_response>
2232
2205
  </example>`;
2233
2206
  }
2207
+ function formatValueForExample(value) {
2208
+ if (typeof value === "undefined") {
2209
+ return "";
2210
+ }
2211
+ if (typeof value === "string") {
2212
+ return `"${value}"`;
2213
+ }
2214
+ if (typeof value === "number") {
2215
+ return value.toString();
2216
+ }
2217
+ if (typeof value === "boolean") {
2218
+ return value ? "true" : "false";
2219
+ }
2220
+ return JSON.stringify(value, null, 2);
2221
+ }
2222
+ function getCodeAgentConstraints(config) {
2223
+ const { maxSteps, allowSubmit } = config;
2224
+ return {
2225
+ must: [
2226
+ "Always issue tool calls to complete your task",
2227
+ ...allowSubmit ? [
2228
+ `Call the \`${SubmitToolName}\` tool once you think you have completed your task, to submit your results`
2229
+ ] : [],
2230
+ `Complete your task within ${maxSteps} steps`
2231
+ ],
2232
+ must_not: ["Engage with the user directly"]
2233
+ };
2234
+ }
2235
+ function getCodeAgentFinalReminder() {
2236
+ return "Remember, you don't get to ask the user any clarifying questions, just use the tools available to complete your task. You're on your own now.";
2237
+ }
2234
2238
 
2235
2239
  // src/tool-creators.ts
2240
+ import {
2241
+ isCommandLine
2242
+ } from "@ai-code-agents/environment-utils";
2236
2243
  var availableEnvironmentTools = {
2237
2244
  [ReadFileToolName]: ReadFileTool,
2238
2245
  [WriteFileToolName]: WriteFileTool,
@@ -2243,12 +2250,14 @@ var availableEnvironmentTools = {
2243
2250
  [ReadManyFilesToolName]: ReadManyFilesTool,
2244
2251
  [GetProjectFileStructureToolName]: GetProjectFileStructureTool,
2245
2252
  [GlobToolName]: GlobTool,
2253
+ [GrepToolName]: GrepTool,
2246
2254
  [ListDirectoryToolName]: ListDirectoryTool,
2247
2255
  [RunCommandToolName]: RunCommandTool
2248
2256
  };
2249
2257
  var cliOnlyTools = [
2250
2258
  GetProjectFileStructureToolName,
2251
2259
  GlobToolName,
2260
+ GrepToolName,
2252
2261
  ListDirectoryToolName,
2253
2262
  RunCommandToolName
2254
2263
  ];
@@ -2257,6 +2266,7 @@ var readonlyTools = [
2257
2266
  ReadManyFilesToolName,
2258
2267
  GetProjectFileStructureToolName,
2259
2268
  GlobToolName,
2269
+ GrepToolName,
2260
2270
  ListDirectoryToolName
2261
2271
  ];
2262
2272
  var dangerousTools = [
@@ -2284,7 +2294,7 @@ function createEnvironmentTool(toolName, environment, config) {
2284
2294
  }
2285
2295
  function createToolsForEnvironment(environment, toolsDefinition = "all") {
2286
2296
  const sanitizedToolsDefinition = sanitizeToolsDefinition(toolsDefinition);
2287
- const isCliEnvironment = "runCommand" in environment;
2297
+ const isCliEnvironment = isCommandLine(environment);
2288
2298
  const tools = {};
2289
2299
  for (const toolDefinition of sanitizedToolsDefinition) {
2290
2300
  const actualToolName = toolDefinition.toolName;
@@ -2305,7 +2315,7 @@ function createToolsForEnvironment(environment, toolsDefinition = "all") {
2305
2315
  }
2306
2316
  tools[toolNameToUse] = createEnvironmentTool(
2307
2317
  actualToolName,
2308
- isCliEnvironment ? environment : environment,
2318
+ environment,
2309
2319
  toolConfig
2310
2320
  );
2311
2321
  }
@@ -2361,6 +2371,54 @@ function sanitizeToolsDefinition(toolsDefinition) {
2361
2371
  });
2362
2372
  }
2363
2373
 
2374
+ // src/util/truncate.ts
2375
+ function truncateString(str) {
2376
+ const lines = str.split("\n");
2377
+ while (lines.length > 0 && lines[lines.length - 1] === "") {
2378
+ lines.pop();
2379
+ }
2380
+ const isMultiline = lines.length > 1;
2381
+ if (isMultiline) {
2382
+ if (lines.length > 5) {
2383
+ const truncatedLines = lines.slice(0, 5).join("\n");
2384
+ const moreLines = lines.length - 5;
2385
+ const lineSuffix = moreLines === 1 ? "line" : "lines";
2386
+ return `${truncatedLines}
2387
+ ...(${moreLines} more ${lineSuffix})`;
2388
+ }
2389
+ return lines.join("\n");
2390
+ }
2391
+ const singleLine = lines[0] || "";
2392
+ if (singleLine.length > 300) {
2393
+ const moreChars = singleLine.length - 300;
2394
+ return `${singleLine.slice(0, 300)}...(${moreChars} more characters)`;
2395
+ }
2396
+ return singleLine;
2397
+ }
2398
+ function truncateObject(obj) {
2399
+ const result = {};
2400
+ for (const [key, value] of Object.entries(obj)) {
2401
+ if (typeof value === "string") {
2402
+ result[key] = truncateString(value);
2403
+ } else if (Array.isArray(value)) {
2404
+ result[key] = value.map((item) => {
2405
+ if (typeof item === "string") {
2406
+ return truncateString(item);
2407
+ }
2408
+ if (typeof item === "object" && item !== null) {
2409
+ return truncateObject(item);
2410
+ }
2411
+ return item;
2412
+ });
2413
+ } else if (typeof value === "object" && value !== null) {
2414
+ result[key] = truncateObject(value);
2415
+ } else {
2416
+ result[key] = value;
2417
+ }
2418
+ }
2419
+ return result;
2420
+ }
2421
+
2364
2422
  // src/util/get-step-log.ts
2365
2423
  function getStepLog(stepResult) {
2366
2424
  const { content } = stepResult;
@@ -2374,13 +2432,17 @@ function getStepLog(stepResult) {
2374
2432
  }
2375
2433
  logEntry += ": ";
2376
2434
  if (part.type === "tool-call" && "input" in part) {
2377
- logEntry += typeof part.input === "string" ? part.input : JSON.stringify(part.input);
2435
+ logEntry += typeof part.input === "string" ? truncateString(part.input) : part.input === null || part.input === void 0 ? String(part.input) : JSON.stringify(
2436
+ truncateObject(part.input)
2437
+ );
2378
2438
  } else if (part.type === "tool-result" && "output" in part) {
2379
- logEntry += typeof part.output === "string" ? part.output : JSON.stringify(part.output);
2439
+ logEntry += typeof part.output === "string" ? truncateString(part.output) : part.output === null || part.output === void 0 ? String(part.output) : JSON.stringify(
2440
+ truncateObject(part.output)
2441
+ );
2380
2442
  } else if (part.type === "tool-error" && "error" in part) {
2381
2443
  logEntry += typeof part.error === "object" && part.error !== null && "message" in part.error ? part.error.message : String(part.error);
2382
2444
  } else if (part.type === "text" && "text" in part) {
2383
- logEntry += part.text;
2445
+ logEntry += truncateString(part.text);
2384
2446
  }
2385
2447
  logEntry += "\n";
2386
2448
  });
@@ -2389,6 +2451,9 @@ function getStepLog(stepResult) {
2389
2451
 
2390
2452
  // src/agent-creators.ts
2391
2453
  function createCodeAgent(agentConfig) {
2454
+ return new ToolLoopAgent(createCodeAgentSettings(agentConfig));
2455
+ }
2456
+ function createCodeAgentSettings(agentConfig) {
2392
2457
  const {
2393
2458
  maxSteps,
2394
2459
  allowSubmit,
@@ -2397,46 +2462,29 @@ function createCodeAgent(agentConfig) {
2397
2462
  tools: originalTools,
2398
2463
  stopWhen: originalStopWhen,
2399
2464
  prepareStep: originalPrepareStep,
2400
- system: originalSystemInstruction,
2465
+ instructions: originalSystemInstruction,
2401
2466
  ...remainingConfig
2402
2467
  } = agentConfig;
2403
2468
  let agentSettings;
2404
- let environmentTools;
2469
+ let tools;
2405
2470
  if ("environments" in remainingConfig) {
2406
2471
  const { environments, environmentToolsDefinition, ...agentSettingsInput } = remainingConfig;
2407
2472
  agentSettings = { ...agentSettingsInput };
2408
- environmentTools = {};
2409
- for (const [environmentName, environment] of Object.entries(environments)) {
2410
- if (!(environmentName in environmentToolsDefinition)) {
2411
- throw new Error(
2412
- `No tools definition provided for environment "${environmentName}". Please provide a tools definition for each environment.`
2413
- );
2414
- }
2415
- const envTools = createToolsForNamedEnvironment(
2416
- environmentName,
2417
- environment,
2418
- environmentToolsDefinition[environmentName]
2419
- );
2420
- for (const [toolName, tool] of Object.entries(envTools)) {
2421
- if (toolName in environmentTools) {
2422
- throw new Error(
2423
- `Tool name conflict: The tool name "${toolName}" from environment "${environmentName}" is already used by another environment's tools.`
2424
- );
2425
- }
2426
- environmentTools[toolName] = tool;
2427
- }
2428
- }
2473
+ tools = createCodeAgentTools(
2474
+ { environments, environmentToolsDefinition },
2475
+ originalTools
2476
+ );
2429
2477
  } else if ("environment" in remainingConfig) {
2430
2478
  const { environment, environmentToolsDefinition, ...agentSettingsInput } = remainingConfig;
2431
2479
  agentSettings = { ...agentSettingsInput };
2432
- environmentTools = createToolsForEnvironment(
2433
- environment,
2434
- environmentToolsDefinition
2480
+ tools = createCodeAgentTools(
2481
+ { environment, environmentToolsDefinition },
2482
+ originalTools
2435
2483
  );
2436
2484
  } else {
2437
2485
  agentSettings = { ...remainingConfig };
2486
+ tools = originalTools || {};
2438
2487
  }
2439
- const tools = environmentTools && originalTools ? mergeTools(environmentTools, originalTools) : originalTools || environmentTools || {};
2440
2488
  if (allowSubmit) {
2441
2489
  if (SubmitToolName in tools) {
2442
2490
  throw new Error(
@@ -2464,16 +2512,54 @@ ${stepLog}`, stepCount - 1);
2464
2512
  } : originalPrepareStep;
2465
2513
  const stopWhenCondition = allowSubmit ? [stepCountIs(maxSteps), hasToolCall(SubmitToolName)] : stepCountIs(maxSteps);
2466
2514
  const stopWhen = originalStopWhen ? mergeStopWhen(originalStopWhen, stopWhenCondition) : stopWhenCondition;
2467
- const system = !omitAdditionalInstructions ? mergeSystemInstructions(
2515
+ const instructions = !omitAdditionalInstructions ? mergeSystemInstructions(
2468
2516
  originalSystemInstruction,
2469
2517
  getAdditionalInstructions({ maxSteps, allowSubmit, tools })
2470
2518
  ) : originalSystemInstruction;
2471
- return new Agent({
2519
+ return {
2472
2520
  ...agentSettings,
2473
- system,
2521
+ instructions,
2474
2522
  prepareStep,
2475
2523
  stopWhen
2476
- });
2524
+ };
2525
+ }
2526
+ function createCodeAgentTools(agentToolsConfig, originalTools) {
2527
+ if ("environments" in agentToolsConfig) {
2528
+ const { environments, environmentToolsDefinition } = agentToolsConfig;
2529
+ const environmentTools = {};
2530
+ for (const [environmentName, environment] of Object.entries(environments)) {
2531
+ if (!(environmentName in environmentToolsDefinition)) {
2532
+ throw new Error(
2533
+ `No tools definition provided for environment "${environmentName}". Please provide a tools definition for each environment.`
2534
+ );
2535
+ }
2536
+ const envTools = createToolsForNamedEnvironment(
2537
+ environmentName,
2538
+ environment,
2539
+ environmentToolsDefinition[environmentName]
2540
+ );
2541
+ for (const [toolName, tool] of Object.entries(envTools)) {
2542
+ if (toolName in environmentTools) {
2543
+ throw new Error(
2544
+ `Tool name conflict: The tool name "${toolName}" from environment "${environmentName}" is already used by another environment's tools.`
2545
+ );
2546
+ }
2547
+ environmentTools[toolName] = tool;
2548
+ }
2549
+ }
2550
+ return originalTools ? mergeTools(environmentTools, originalTools) : environmentTools;
2551
+ }
2552
+ if ("environment" in agentToolsConfig) {
2553
+ const { environment, environmentToolsDefinition } = agentToolsConfig;
2554
+ const environmentTools = createToolsForEnvironment(
2555
+ environment,
2556
+ environmentToolsDefinition
2557
+ );
2558
+ return originalTools ? mergeTools(environmentTools, originalTools) : environmentTools;
2559
+ }
2560
+ throw new Error(
2561
+ 'No environments provided in agent tools configuration. Please provide either "environment" or "environments".'
2562
+ );
2477
2563
  }
2478
2564
  function mergeTools(baseTools, additionalTools) {
2479
2565
  const tools = { ...baseTools };
@@ -2499,9 +2585,26 @@ function mergeStopWhen(baseStopWhen, additionalStopWhen) {
2499
2585
  }
2500
2586
  return [baseStopWhen, additionalStopWhen];
2501
2587
  }
2502
- function mergeSystemInstructions(baseSystem, additionalInstructions) {
2503
- if (baseSystem) {
2504
- return `${baseSystem.trimEnd()}
2588
+ function mergeSystemInstructions(baseInstructions, additionalInstructions) {
2589
+ if (baseInstructions) {
2590
+ if (Array.isArray(baseInstructions)) {
2591
+ return [
2592
+ ...baseInstructions,
2593
+ {
2594
+ role: "system",
2595
+ content: additionalInstructions
2596
+ }
2597
+ ];
2598
+ }
2599
+ if (typeof baseInstructions === "object") {
2600
+ return {
2601
+ ...baseInstructions,
2602
+ content: `${baseInstructions.content.trimEnd()}
2603
+
2604
+ ${additionalInstructions}`
2605
+ };
2606
+ }
2607
+ return `${baseInstructions.trimEnd()}
2505
2608
 
2506
2609
  ${additionalInstructions}`;
2507
2610
  }
@@ -2526,12 +2629,10 @@ function createEnvironment(environmentName, config) {
2526
2629
  return new EnvironmentClass(config);
2527
2630
  }
2528
2631
  export {
2529
- CopyFileResult,
2530
2632
  CopyFileTool,
2531
2633
  CopyFileToolInput,
2532
2634
  CopyFileToolName,
2533
2635
  CopyFileToolOutput,
2534
- DeleteFileResult,
2535
2636
  DeleteFileTool,
2536
2637
  DeleteFileToolInput,
2537
2638
  DeleteFileToolName,
@@ -2553,20 +2654,22 @@ export {
2553
2654
  GlobToolInput,
2554
2655
  GlobToolName,
2555
2656
  GlobToolOutput,
2657
+ GrepTool,
2658
+ GrepToolInput,
2659
+ GrepToolName,
2660
+ GrepToolOutput,
2556
2661
  ListDirectoryTool,
2557
2662
  ListDirectoryToolInput,
2558
2663
  ListDirectoryToolName,
2559
2664
  ListDirectoryToolOutput,
2560
2665
  MockFilesystemEnvironment,
2561
2666
  MockFilesystemEnvironmentName,
2562
- MoveFileResult,
2563
2667
  MoveFileTool,
2564
2668
  MoveFileToolInput,
2565
2669
  MoveFileToolName,
2566
2670
  MoveFileToolOutput,
2567
2671
  NodeFilesystemEnvironment,
2568
2672
  NodeFilesystemEnvironmentName,
2569
- ReadFileResult,
2570
2673
  ReadFileTool,
2571
2674
  ReadFileToolInput,
2572
2675
  ReadFileToolName,
@@ -2575,7 +2678,6 @@ export {
2575
2678
  ReadManyFilesToolInput,
2576
2679
  ReadManyFilesToolName,
2577
2680
  ReadManyFilesToolOutput,
2578
- RunCommandResult,
2579
2681
  RunCommandTool,
2580
2682
  RunCommandToolInput,
2581
2683
  RunCommandToolName,
@@ -2586,12 +2688,13 @@ export {
2586
2688
  SubmitToolOutput,
2587
2689
  UnsafeLocalEnvironment,
2588
2690
  UnsafeLocalEnvironmentName,
2589
- WriteFileResult,
2590
2691
  WriteFileTool,
2591
2692
  WriteFileToolInput,
2592
2693
  WriteFileToolName,
2593
2694
  WriteFileToolOutput,
2594
2695
  createCodeAgent,
2696
+ createCodeAgentSettings,
2697
+ createCodeAgentTools,
2595
2698
  createEnvironment,
2596
2699
  createEnvironmentTool,
2597
2700
  createToolsForEnvironment,