ai-code-agents 0.1.0-beta.1 → 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,286 +1,16 @@
1
1
  // src/environments/docker-environment.ts
2
2
  import { exec } from "child_process";
3
+ import {
4
+ UnixEnvironmentBase,
5
+ escapeCommandArg
6
+ } from "@ai-code-agents/environment-utils";
3
7
 
4
- // src/util/escape-command-arg.ts
5
- function escapeCommandArg(arg) {
6
- if ("" === arg) {
7
- return "''";
8
- }
9
- return `'${arg.replace(/'/g, "'\\''")}'`;
8
+ // src/util/escape-command.ts
9
+ function escapeCommand(command) {
10
+ const escaped = command.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
11
+ return `"${escaped}"`;
10
12
  }
11
13
 
12
- // src/util/validate-relative-path.ts
13
- import * as path from "path";
14
- function validateRelativePath(filePath) {
15
- if (path.isAbsolute(filePath)) {
16
- throw new Error("Absolute paths are not allowed.");
17
- }
18
- if (filePath.startsWith("~")) {
19
- throw new Error('Paths starting with "~" are not allowed.');
20
- }
21
- if (filePath.includes("\0")) {
22
- throw new Error("Paths must not contain null bytes.");
23
- }
24
- const normalizedPath = path.normalize(filePath);
25
- if (normalizedPath.startsWith("..")) {
26
- throw new Error("Path traversal is not allowed.");
27
- }
28
- }
29
-
30
- // src/environments/filesystem-environment-base.ts
31
- var FilesystemEnvironmentBase = class {
32
- _envConfig;
33
- /**
34
- * Constructs a new environment instance.
35
- *
36
- * @param config - Environment configuration.
37
- */
38
- constructor(config) {
39
- this._envConfig = config;
40
- }
41
- /**
42
- * Reads the content of a file at the specified path.
43
- *
44
- * @param path - The path to the file to read, relative to the project directory.
45
- * @returns A promise that resolves to a ReadFileResult.
46
- */
47
- async readFile(path5) {
48
- validateRelativePath(path5);
49
- if (!await this.fileExists(path5)) {
50
- throw new Error(`File not found: ${path5}`);
51
- }
52
- const content = await this.readFileContent(path5);
53
- return {
54
- path: path5,
55
- content
56
- };
57
- }
58
- /**
59
- * Writes content to a file at the specified path.
60
- *
61
- * If a file is already present at the path, it will be overwritten.
62
- *
63
- * @param path - The path to the file to write, relative to the project directory.
64
- * @param content - The content to write to the file.
65
- * @returns A promise that resolves to a WriteFileResult.
66
- */
67
- async writeFile(path5, content) {
68
- validateRelativePath(path5);
69
- await this.writeFileContent(path5, content);
70
- return {
71
- path: path5,
72
- message: "File written successfully."
73
- };
74
- }
75
- /**
76
- * Deletes a file at the specified path.
77
- *
78
- * @param path - The path to the file to delete, relative to the project directory.
79
- * @returns A promise that resolves to a DeleteFileResult.
80
- */
81
- async deleteFile(path5) {
82
- validateRelativePath(path5);
83
- if (!await this.fileExists(path5)) {
84
- return {
85
- path: path5,
86
- message: "File was already deleted."
87
- };
88
- }
89
- await this.deleteFileContent(path5);
90
- return {
91
- path: path5,
92
- message: "File deleted successfully."
93
- };
94
- }
95
- /**
96
- * Moves a file from a source path to a destination path.
97
- *
98
- * If a file is already present at the destination path, it will be overwritten.
99
- *
100
- * @param sourcePath - The path to the file to move.
101
- * @param destinationPath - The path to move the file to.
102
- * @returns A promise that resolves to a MoveFileResult.
103
- */
104
- async moveFile(sourcePath, destinationPath) {
105
- validateRelativePath(sourcePath);
106
- validateRelativePath(destinationPath);
107
- if (!await this.fileExists(sourcePath)) {
108
- throw new Error(`File not found: ${sourcePath}`);
109
- }
110
- await this.moveFileContent(sourcePath, destinationPath);
111
- return {
112
- sourcePath,
113
- destinationPath,
114
- message: "File moved successfully."
115
- };
116
- }
117
- /**
118
- * Copies a file from a source path to a destination path.
119
- *
120
- * If a file is already present at the destination path, it will be overwritten.
121
- *
122
- * @param sourcePath - The path to the file to copy.
123
- * @param destinationPath - The path to copy the file to.
124
- * @returns A promise that resolves to a CopyFileResult.
125
- */
126
- async copyFile(sourcePath, destinationPath) {
127
- validateRelativePath(sourcePath);
128
- validateRelativePath(destinationPath);
129
- if (!await this.fileExists(sourcePath)) {
130
- throw new Error(`File not found: ${sourcePath}`);
131
- }
132
- await this.copyFileContent(sourcePath, destinationPath);
133
- return {
134
- sourcePath,
135
- destinationPath,
136
- message: "File copied successfully."
137
- };
138
- }
139
- /**
140
- * Moves the content of a file from a source path to a destination path, relative to the project directory.
141
- *
142
- * When this method is called, it is guaranteed that the source file exists.
143
- * This method unconditionally moves the content, even if a file already exists at the destination path.
144
- *
145
- * @param relativeSourcePath - The path to the file to move, relative to the project directory.
146
- * @param relativeDestinationPath - The path to move the file to, relative to the project directory.
147
- */
148
- async moveFileContent(relativeSourcePath, relativeDestinationPath) {
149
- const content = await this.readFileContent(relativeSourcePath);
150
- this.writeFileContent(relativeDestinationPath, content);
151
- this.deleteFileContent(relativeSourcePath);
152
- }
153
- /**
154
- * Copies the content of a file from a source path to a destination path, relative to the project directory.
155
- *
156
- * When this method is called, it is guaranteed that the source file exists.
157
- * This method unconditionally copies the content, even if a file already exists at the destination path.
158
- *
159
- * @param relativeSourcePath - The path to the file to copy, relative to the project directory.
160
- * @param relativeDestinationPath - The path to copy the file to, relative to the project directory.
161
- */
162
- async copyFileContent(relativeSourcePath, relativeDestinationPath) {
163
- const content = await this.readFileContent(relativeSourcePath);
164
- this.writeFileContent(relativeDestinationPath, content);
165
- }
166
- };
167
-
168
- // src/environments/command-line-environment-base.ts
169
- var CommandLineEnvironmentBase = class extends FilesystemEnvironmentBase {
170
- /**
171
- * Runs a CLI command in environment.
172
- *
173
- * @param command - The command to run.
174
- * @returns A promise that resolves to a RunCommandResult.
175
- */
176
- async runCommand(command) {
177
- const [exitCode, stdout, stderr] = await this.executeCommand(command);
178
- return {
179
- command,
180
- exitCode,
181
- stdout,
182
- stderr
183
- };
184
- }
185
- };
186
-
187
- // src/environments/unix-environment-base.ts
188
- var UnixEnvironmentBase = class extends CommandLineEnvironmentBase {
189
- /**
190
- * Checks whether a file exists at the specified path relative to the project directory.
191
- *
192
- * @param relativePath - The path to the file to check, relative to the project directory.
193
- * @returns True if the file exists, false otherwise.
194
- */
195
- async fileExists(relativePath) {
196
- const command = `if [ -e ${escapeCommandArg(relativePath)} ]; then echo "yes"; else echo "no"; fi`;
197
- const { exitCode, stdout } = await this.runCommand(command);
198
- return exitCode === 0 && stdout.trim() === "yes";
199
- }
200
- /**
201
- * Gets the content of a file at the specified path, relative to the project directory.
202
- *
203
- * When this method is called, it is guaranteed that the file exists.
204
- *
205
- * @param relativePath - The path to the file to read, relative to the project directory.
206
- * @returns The content of the file.
207
- */
208
- async readFileContent(relativePath) {
209
- const command = `cat ${escapeCommandArg(relativePath)}`;
210
- const { exitCode, stdout } = await this.runCommand(command);
211
- return exitCode === 0 ? stdout : "";
212
- }
213
- /**
214
- * Writes content to a file at the specified path, relative to the project directory.
215
- *
216
- * This method unconditionally writes the content, even if a file already exists at the path, or if the file is new.
217
- *
218
- * @param relativePath - The path to the file to write, relative to the project directory.
219
- * @param content - The content to write to the file.
220
- */
221
- async writeFileContent(relativePath, content) {
222
- const command = `sh -c "echo ${escapeCommandArg(
223
- content
224
- )} > ${escapeCommandArg(relativePath)}"`;
225
- const { exitCode, stderr } = await this.runCommand(command);
226
- if (exitCode !== 0) {
227
- throw new Error(`Failed to write file: ${stderr || "Unknown error"}`);
228
- }
229
- }
230
- /**
231
- * Deletes a file at the specified path, relative to the project directory.
232
- *
233
- * When this method is called, it is guaranteed that the file exists.
234
- *
235
- * @param relativePath - The path to the file to delete, relative to the project directory.
236
- */
237
- async deleteFileContent(relativePath) {
238
- const command = `rm ${escapeCommandArg(relativePath)}`;
239
- const { exitCode, stderr } = await this.runCommand(command);
240
- if (exitCode !== 0) {
241
- throw new Error(`Failed to delete file: ${stderr || "Unknown error"}`);
242
- }
243
- }
244
- /**
245
- * Moves the content of a file from a source path to a destination path, relative to the project directory.
246
- *
247
- * When this method is called, it is guaranteed that the source file exists.
248
- * This method unconditionally moves the content, even if a file already exists at the destination path.
249
- *
250
- * @param relativeSourcePath - The path to the file to move, relative to the project directory.
251
- * @param relativeDestinationPath - The path to move the file to, relative to the project directory.
252
- */
253
- async moveFileContent(relativeSourcePath, relativeDestinationPath) {
254
- const command = `mv ${escapeCommandArg(relativeSourcePath)} ${escapeCommandArg(
255
- relativeDestinationPath
256
- )}`;
257
- const { exitCode, stderr } = await this.runCommand(command);
258
- if (exitCode !== 0) {
259
- throw new Error(`Failed to move file: ${stderr || "Unknown error"}`);
260
- }
261
- }
262
- /**
263
- * Copies the content of a file from a source path to a destination path, relative to the project directory.
264
- *
265
- * When this method is called, it is guaranteed that the source file exists.
266
- * This method unconditionally copies the content, even if a file already exists at the destination path.
267
- *
268
- * @param relativeSourcePath - The path to the file to copy, relative to the project directory.
269
- * @param relativeDestinationPath - The path to copy the file to, relative to the project directory.
270
- */
271
- async copyFileContent(relativeSourcePath, relativeDestinationPath) {
272
- const command = `cp ${escapeCommandArg(relativeSourcePath)} ${escapeCommandArg(
273
- relativeDestinationPath
274
- )}`;
275
- const result = await this.runCommand(command);
276
- if (result.exitCode !== 0) {
277
- throw new Error(
278
- `Failed to copy file: ${result.stderr || "Unknown error"}`
279
- );
280
- }
281
- }
282
- };
283
-
284
14
  // src/environments/docker-environment.ts
285
15
  var DockerEnvironmentName = "docker";
286
16
  var DockerEnvironment = class extends UnixEnvironmentBase {
@@ -312,7 +42,7 @@ var DockerEnvironment = class extends UnixEnvironmentBase {
312
42
  async executeCommand(command) {
313
43
  return new Promise((resolve) => {
314
44
  exec(
315
- `docker exec ${this._envConfig.containerId} ${this._commandPrefix}${command}`,
45
+ `docker exec ${this._envConfig.containerId} sh -c ${escapeCommand(this._commandPrefix + command)}`,
316
46
  (error, stdout, stderr) => {
317
47
  const exitCode = error ? error.code ?? 1 : 0;
318
48
  resolve([exitCode, stdout, stderr]);
@@ -323,7 +53,8 @@ var DockerEnvironment = class extends UnixEnvironmentBase {
323
53
  };
324
54
 
325
55
  // src/environments/mock-filesystem-environment.ts
326
- import path2 from "path";
56
+ import path from "path";
57
+ import { FilesystemEnvironmentBase } from "@ai-code-agents/environment-utils";
327
58
  var MockFilesystemEnvironmentName = "mock-filesystem";
328
59
  var MockFilesystemEnvironment = class extends FilesystemEnvironmentBase {
329
60
  files;
@@ -336,8 +67,8 @@ var MockFilesystemEnvironment = class extends FilesystemEnvironmentBase {
336
67
  constructor(config = {}) {
337
68
  super(config);
338
69
  const { initialFiles, directoryPath } = this._envConfig;
339
- this.files = initialFiles ?? /* @__PURE__ */ new Map();
340
- 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;
341
72
  }
342
73
  /**
343
74
  * Gets the environment name.
@@ -390,9 +121,10 @@ var MockFilesystemEnvironment = class extends FilesystemEnvironmentBase {
390
121
 
391
122
  // src/environments/node-filesystem-environment.ts
392
123
  import fs from "fs/promises";
393
- import path3 from "path";
124
+ import path2 from "path";
125
+ import { FilesystemEnvironmentBase as FilesystemEnvironmentBase2 } from "@ai-code-agents/environment-utils";
394
126
  var NodeFilesystemEnvironmentName = "node-filesystem";
395
- var NodeFilesystemEnvironment = class extends FilesystemEnvironmentBase {
127
+ var NodeFilesystemEnvironment = class extends FilesystemEnvironmentBase2 {
396
128
  /**
397
129
  * Constructs a new NodeFilesystemEnvironment instance.
398
130
  *
@@ -419,7 +151,7 @@ var NodeFilesystemEnvironment = class extends FilesystemEnvironmentBase {
419
151
  * @returns True if the file exists, false otherwise.
420
152
  */
421
153
  async fileExists(relativePath) {
422
- const absolutePath = path3.join(this._envConfig.directoryPath, relativePath);
154
+ const absolutePath = path2.join(this._envConfig.directoryPath, relativePath);
423
155
  try {
424
156
  await fs.stat(absolutePath);
425
157
  return true;
@@ -436,7 +168,7 @@ var NodeFilesystemEnvironment = class extends FilesystemEnvironmentBase {
436
168
  * @returns The content of the file.
437
169
  */
438
170
  async readFileContent(relativePath) {
439
- const absolutePath = path3.join(this._envConfig.directoryPath, relativePath);
171
+ const absolutePath = path2.join(this._envConfig.directoryPath, relativePath);
440
172
  return fs.readFile(absolutePath, "utf-8");
441
173
  }
442
174
  /**
@@ -448,7 +180,7 @@ var NodeFilesystemEnvironment = class extends FilesystemEnvironmentBase {
448
180
  * @param content - The content to write to the file.
449
181
  */
450
182
  async writeFileContent(relativePath, content) {
451
- const absolutePath = path3.join(this._envConfig.directoryPath, relativePath);
183
+ const absolutePath = path2.join(this._envConfig.directoryPath, relativePath);
452
184
  await fs.writeFile(absolutePath, content, "utf-8");
453
185
  }
454
186
  /**
@@ -459,7 +191,7 @@ var NodeFilesystemEnvironment = class extends FilesystemEnvironmentBase {
459
191
  * @param relativePath - The path to the file to delete, relative to the project directory.
460
192
  */
461
193
  async deleteFileContent(relativePath) {
462
- const absolutePath = path3.join(this._envConfig.directoryPath, relativePath);
194
+ const absolutePath = path2.join(this._envConfig.directoryPath, relativePath);
463
195
  await fs.rm(absolutePath);
464
196
  }
465
197
  /**
@@ -472,11 +204,11 @@ var NodeFilesystemEnvironment = class extends FilesystemEnvironmentBase {
472
204
  * @param relativeDestinationPath - The path to move the file to, relative to the project directory.
473
205
  */
474
206
  async moveFileContent(relativeSourcePath, relativeDestinationPath) {
475
- const sourcePath = path3.join(
207
+ const sourcePath = path2.join(
476
208
  this._envConfig.directoryPath,
477
209
  relativeSourcePath
478
210
  );
479
- const destinationPath = path3.join(
211
+ const destinationPath = path2.join(
480
212
  this._envConfig.directoryPath,
481
213
  relativeDestinationPath
482
214
  );
@@ -486,8 +218,12 @@ var NodeFilesystemEnvironment = class extends FilesystemEnvironmentBase {
486
218
 
487
219
  // src/environments/unsafe-local-environment.ts
488
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";
489
225
  var UnsafeLocalEnvironmentName = "unsafe-local";
490
- var UnsafeLocalEnvironment = class extends UnixEnvironmentBase {
226
+ var UnsafeLocalEnvironment = class extends UnixEnvironmentBase2 {
491
227
  _commandPrefix;
492
228
  /**
493
229
  * Constructs a new environment instance.
@@ -503,7 +239,7 @@ var UnsafeLocalEnvironment = class extends UnixEnvironmentBase {
503
239
  throw new Error('The directory path must be absolute (start with "/")');
504
240
  }
505
241
  super(config);
506
- this._commandPrefix = `cd ${escapeCommandArg(directoryPath)} && `;
242
+ this._commandPrefix = `cd ${escapeCommandArg2(directoryPath)} && `;
507
243
  }
508
244
  /**
509
245
  * Gets the environment name.
@@ -530,180 +266,17 @@ var UnsafeLocalEnvironment = class extends UnixEnvironmentBase {
530
266
  };
531
267
 
532
268
  // src/tools/copy-file-tool.ts
533
- import { z as z2 } from "zod";
534
-
535
- // src/types.ts
536
- import * as z from "zod";
537
- var ReadFileResult = z.object({
538
- path: z.string().meta({
539
- description: "The path to the file that was read."
540
- }),
541
- content: z.string().meta({
542
- description: "The content of the file that was read."
543
- })
544
- });
545
- var WriteFileResult = z.object({
546
- path: z.string().meta({
547
- description: "The path to the file that was written."
548
- }),
549
- message: z.string().meta({
550
- description: "A message indicating the result of the write operation."
551
- })
552
- });
553
- var DeleteFileResult = z.object({
554
- path: z.string().meta({
555
- description: "The path to the file that was deleted."
556
- }),
557
- message: z.string().meta({
558
- description: "A message indicating the result of the delete operation."
559
- })
560
- });
561
- var MoveFileResult = z.object({
562
- sourcePath: z.string().meta({
563
- description: "The original path of the file that was moved."
564
- }),
565
- destinationPath: z.string().meta({
566
- description: "The new path of the file that was moved to."
567
- }),
568
- message: z.string().meta({
569
- description: "A message indicating the result of the move operation."
570
- })
571
- });
572
- var CopyFileResult = z.object({
573
- sourcePath: z.string().meta({
574
- description: "The original path of the file that was copied."
575
- }),
576
- destinationPath: z.string().meta({
577
- description: "The new path of the file that was copied to."
578
- }),
579
- message: z.string().meta({
580
- description: "A message indicating the result of the copy operation."
581
- })
582
- });
583
- var RunCommandResult = z.object({
584
- command: z.string().meta({
585
- description: "The command that was executed."
586
- }),
587
- exitCode: z.number().meta({
588
- description: "The exit code of the command."
589
- }),
590
- stdout: z.string().meta({
591
- description: "The standard output of the command."
592
- }),
593
- stderr: z.string().meta({
594
- description: "The standard error output of the command."
595
- })
596
- });
597
-
598
- // src/tools/tool-base.ts
599
- var ToolBase = class {
600
- _toolConfig;
601
- _name;
602
- _description;
603
- _inputSchema;
604
- _outputSchema;
605
- _needsApproval;
606
- /**
607
- * Constructs a new tool instance.
608
- *
609
- * @param toolConfig - Optional tool config, can be used to override some defaults.
610
- */
611
- constructor(toolConfig) {
612
- const {
613
- name: defaultName,
614
- description: defaultDescription,
615
- inputSchema,
616
- outputSchema,
617
- needsApproval: defaultNeedsApproval
618
- } = this.getMetadata();
619
- this._name = toolConfig?.name || defaultName;
620
- this._description = toolConfig?.description || defaultDescription;
621
- this._inputSchema = inputSchema;
622
- this._outputSchema = outputSchema;
623
- this._needsApproval = toolConfig?.needsApproval !== void 0 ? toolConfig.needsApproval : defaultNeedsApproval;
624
- }
625
- /**
626
- * Gets the tool name.
627
- *
628
- * @returns The tool name.
629
- */
630
- get name() {
631
- return this._name;
632
- }
633
- /**
634
- * Gets the tool description.
635
- *
636
- * @returns The tool description.
637
- */
638
- get description() {
639
- return this._description;
640
- }
641
- /**
642
- * Gets the input schema for the tool.
643
- *
644
- * @returns The input schema.
645
- */
646
- get inputSchema() {
647
- return this._inputSchema;
648
- }
649
- /**
650
- * Gets the input schema for the tool.
651
- *
652
- * @returns The input schema.
653
- */
654
- get outputSchema() {
655
- return this._outputSchema;
656
- }
657
- /**
658
- * Gets whether the tool needs approval before use.
659
- *
660
- * @returns True if the tool needs approval, false otherwise.
661
- */
662
- get needsApproval() {
663
- return this._needsApproval;
664
- }
665
- };
666
-
667
- // src/tools/environment-tool-base.ts
668
- var EnvironmentToolBase = class extends ToolBase {
669
- _environment;
670
- /**
671
- * Constructs a new `EnvironmentToolBase` instance.
672
- *
673
- * @param environment - The execution environment to apply the tool in.
674
- * @param toolConfig - Optional tool config, can be used to override some defaults.
675
- */
676
- constructor(environment, toolConfig) {
677
- super(toolConfig);
678
- this._environment = environment;
679
- }
680
- /**
681
- * Gets the current execution environment for the tool.
682
- *
683
- * @returns The current execution environment.
684
- */
685
- get environment() {
686
- return this._environment;
687
- }
688
- /**
689
- * Executes the tool with the given input.
690
- *
691
- * @param input - The input for the tool.
692
- * @param _options - Options from the tool call.
693
- * @returns A promise that resolves to the tool execution result.
694
- */
695
- execute(input, _options) {
696
- return this.executeForEnvironment(this._environment, input);
697
- }
698
- };
699
-
700
- // src/tools/copy-file-tool.ts
269
+ import { z } from "zod";
270
+ import {
271
+ EnvironmentToolBase,
272
+ CopyFileResult
273
+ } from "@ai-code-agents/environment-utils";
701
274
  var CopyFileToolName = "copy_file";
702
- var CopyFileToolInput = z2.object({
703
- sourcePath: z2.string().meta({
275
+ var CopyFileToolInput = z.object({
276
+ sourcePath: z.string().meta({
704
277
  description: "The path to the file to copy, relative to the project directory."
705
278
  }),
706
- destinationPath: z2.string().meta({
279
+ destinationPath: z.string().meta({
707
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."
708
281
  })
709
282
  });
@@ -738,10 +311,11 @@ var CopyFileTool = class extends EnvironmentToolBase {
738
311
  /**
739
312
  * Converts the tool output to a format suitable for model consumption.
740
313
  *
741
- * @param output - The output from the tool execution.
314
+ * @param options - The tool result, including the output from the tool execution.
742
315
  * @returns The formatted tool result.
743
316
  */
744
- toModelOutput(output) {
317
+ toModelOutput(options) {
318
+ const { output } = options;
745
319
  return {
746
320
  type: "text",
747
321
  value: `File \`${output.sourcePath}\` copied successfully to \`${output.destinationPath}\`.`
@@ -766,15 +340,19 @@ var CopyFileTool = class extends EnvironmentToolBase {
766
340
  };
767
341
 
768
342
  // src/tools/delete-file-tool.ts
769
- 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";
770
348
  var DeleteFileToolName = "delete_file";
771
- var DeleteFileToolInput = z3.object({
772
- path: z3.string().meta({
349
+ var DeleteFileToolInput = z2.object({
350
+ path: z2.string().meta({
773
351
  description: "The path to the file to delete, relative to the project directory."
774
352
  })
775
353
  });
776
354
  var DeleteFileToolOutput = DeleteFileResult;
777
- var DeleteFileTool = class extends EnvironmentToolBase {
355
+ var DeleteFileTool = class extends EnvironmentToolBase2 {
778
356
  /**
779
357
  * Returns the metadata for the tool.
780
358
  *
@@ -804,10 +382,11 @@ var DeleteFileTool = class extends EnvironmentToolBase {
804
382
  /**
805
383
  * Converts the tool output to a format suitable for model consumption.
806
384
  *
807
- * @param output - The output from the tool execution.
385
+ * @param options - The tool result, including the output from the tool execution.
808
386
  * @returns The formatted tool result.
809
387
  */
810
- toModelOutput(output) {
388
+ toModelOutput(options) {
389
+ const { output } = options;
811
390
  return {
812
391
  type: "text",
813
392
  value: `File \`${output.path}\` deleted successfully.`
@@ -831,43 +410,46 @@ var DeleteFileTool = class extends EnvironmentToolBase {
831
410
  };
832
411
 
833
412
  // src/tools/edit-file-tool.ts
834
- 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";
835
417
  var EditFileToolName = "edit_file";
836
- var EditFileToolInput = z4.object({
837
- path: z4.string().meta({
418
+ var EditFileToolInput = z3.object({
419
+ path: z3.string().meta({
838
420
  description: "The path to the file to edit, relative to the project directory."
839
421
  }),
840
- oldString: z4.string().meta({
422
+ oldString: z3.string().meta({
841
423
  description: "The exact string to replace in the file."
842
424
  }),
843
- newString: z4.string().meta({
425
+ newString: z3.string().meta({
844
426
  description: "The string to replace the old string with."
845
427
  }),
846
- replaceAll: z4.boolean().optional().meta({
428
+ replaceAll: z3.boolean().optional().meta({
847
429
  description: "Whether to replace all occurrences of the old string. Defaults to false."
848
430
  })
849
431
  });
850
- var EditFileToolOutput = z4.object({
851
- path: z4.string().meta({
432
+ var EditFileToolOutput = z3.object({
433
+ path: z3.string().meta({
852
434
  description: "The path to the file that was edited."
853
435
  }),
854
- oldString: z4.string().meta({
436
+ oldString: z3.string().meta({
855
437
  description: "The old string that was replaced."
856
438
  }),
857
- newString: z4.string().meta({
439
+ newString: z3.string().meta({
858
440
  description: "The new string that replaced the old string."
859
441
  }),
860
- replacements: z4.number().meta({
442
+ replacements: z3.number().meta({
861
443
  description: "The number of replacements made."
862
444
  }),
863
- message: z4.string().meta({
445
+ message: z3.string().meta({
864
446
  description: "A message indicating the result of the edit operation."
865
447
  })
866
448
  });
867
- function escapeRegExp(string2) {
868
- return string2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
449
+ function escapeRegExp(string) {
450
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
869
451
  }
870
- var EditFileTool = class extends EnvironmentToolBase {
452
+ var EditFileTool = class extends EnvironmentToolBase3 {
871
453
  /**
872
454
  * Returns the metadata for the tool.
873
455
  *
@@ -922,10 +504,11 @@ var EditFileTool = class extends EnvironmentToolBase {
922
504
  /**
923
505
  * Converts the tool output to a format suitable for model consumption.
924
506
  *
925
- * @param output - The output from the tool execution.
507
+ * @param options - The tool result, including the output from the tool execution.
926
508
  * @returns The formatted tool result.
927
509
  */
928
- toModelOutput(output) {
510
+ toModelOutput(options) {
511
+ const { output } = options;
929
512
  return {
930
513
  type: "text",
931
514
  value: `Edited file \`${output.path}\` with ${output.replacements} replacement(s).`
@@ -973,7 +556,11 @@ var EditFileTool = class extends EnvironmentToolBase {
973
556
  };
974
557
 
975
558
  // src/tools/get-project-file-structure-tool.ts
976
- 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";
977
564
 
978
565
  // src/util/build-tree-from-files.ts
979
566
  function renderTree(node, prefix = "") {
@@ -1004,31 +591,118 @@ function buildTreeFromFiles(files) {
1004
591
  if (!current[part]) {
1005
592
  current[part] = {};
1006
593
  }
1007
- current = current[part];
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
+ }
1008
661
  }
662
+ return [...new Set(sanitizedRules)];
663
+ } catch (_error) {
664
+ return [];
1009
665
  }
1010
- return renderTree(tree).trim();
666
+ }
667
+ function matchSegment(pattern, segment) {
668
+ if (pattern === "*") {
669
+ return true;
670
+ }
671
+ if (pattern === segment) {
672
+ return true;
673
+ }
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();
1011
685
  }
1012
686
 
1013
687
  // src/tools/get-project-file-structure-tool.ts
1014
688
  var GetProjectFileStructureToolName = "get_project_file_structure";
1015
- var GetProjectFileStructureToolInput = z5.object({
1016
- path: z5.string().optional().meta({
689
+ var GetProjectFileStructureToolInput = z4.object({
690
+ path: z4.string().optional().meta({
1017
691
  description: 'Root path to list files from, relative to the project directory. Defaults to ".".'
1018
692
  }),
1019
- excludeGitIgnored: z5.boolean().optional().meta({
693
+ excludeGitIgnored: z4.boolean().optional().meta({
1020
694
  description: "Whether to exclude files ignored by Git. Defaults to true."
1021
695
  })
1022
696
  });
1023
- var GetProjectFileStructureToolOutput = z5.object({
1024
- files: z5.array(z5.string()).meta({
697
+ var GetProjectFileStructureToolOutput = z4.object({
698
+ files: z4.array(z4.string()).meta({
1025
699
  description: "List of all file paths found, relative to the root path."
1026
700
  }),
1027
- excludeGitIgnored: z5.boolean().meta({
701
+ excludeGitIgnored: z4.boolean().meta({
1028
702
  description: "Whether files ignored by Git were excluded."
1029
703
  })
1030
704
  });
1031
- var GetProjectFileStructureTool = class extends EnvironmentToolBase {
705
+ var GetProjectFileStructureTool = class extends EnvironmentToolBase4 {
1032
706
  /**
1033
707
  * Returns the metadata for the tool.
1034
708
  *
@@ -1054,21 +728,16 @@ var GetProjectFileStructureTool = class extends EnvironmentToolBase {
1054
728
  */
1055
729
  async executeForEnvironment(env, input) {
1056
730
  const { path: path5 = ".", excludeGitIgnored = true } = input;
1057
- const escapedPath = escapeCommandArg(path5);
731
+ const escapedPath = escapeCommandArg4(path5);
1058
732
  let command = `find ${escapedPath} -type f`;
1059
733
  if (excludeGitIgnored) {
1060
- let gitIgnoredPaths = [];
1061
- try {
1062
- const { content: gitignoreContent } = await env.readFile(".gitignore");
1063
- gitIgnoredPaths = gitignoreContent.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
1064
- } catch (_error) {
1065
- }
734
+ const gitIgnoredPaths = await getGitIgnoredPaths(env);
1066
735
  for (const gitIgnoredPath of gitIgnoredPaths) {
1067
736
  if (!gitIgnoredPath.endsWith("/")) {
1068
- const escapedPath2 = escapeCommandArg(`*/${gitIgnoredPath}/*`);
1069
- command += ` -not -name ${escapeCommandArg(gitIgnoredPath)} -not -path ${escapedPath2}`;
737
+ const escapedPath2 = escapeCommandArg4(`*/${gitIgnoredPath}/*`);
738
+ command += ` -not -name ${escapeCommandArg4(gitIgnoredPath)} -not -path ${escapedPath2}`;
1070
739
  } else {
1071
- const escapedPath2 = escapeCommandArg(`*/${gitIgnoredPath}*`);
740
+ const escapedPath2 = escapeCommandArg4(`*/${gitIgnoredPath}*`);
1072
741
  command += ` -not -path ${escapedPath2}`;
1073
742
  }
1074
743
  }
@@ -1090,10 +759,11 @@ var GetProjectFileStructureTool = class extends EnvironmentToolBase {
1090
759
  /**
1091
760
  * Converts the tool output to a format suitable for model consumption.
1092
761
  *
1093
- * @param output - The output from the tool execution.
762
+ * @param options - The tool result, including the output from the tool execution.
1094
763
  * @returns The formatted tool result.
1095
764
  */
1096
- toModelOutput(output) {
765
+ toModelOutput(options) {
766
+ const { output } = options;
1097
767
  const tree = buildTreeFromFiles(output.files);
1098
768
  if (!tree) {
1099
769
  return {
@@ -1131,7 +801,12 @@ var GetProjectFileStructureTool = class extends EnvironmentToolBase {
1131
801
  };
1132
802
 
1133
803
  // src/tools/glob-tool.ts
1134
- 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";
1135
810
 
1136
811
  // src/util/glob-to-reg-exp.ts
1137
812
  function globToRegExp(glob) {
@@ -1179,32 +854,32 @@ function globToRegExp(glob) {
1179
854
 
1180
855
  // src/tools/glob-tool.ts
1181
856
  var GlobToolName = "glob";
1182
- var GlobToolInput = z6.object({
1183
- searchPattern: z6.string().meta({
857
+ var GlobToolInput = z5.object({
858
+ searchPattern: z5.string().meta({
1184
859
  description: 'The glob pattern to search for, relative to the search path / project directory (e.g. "**/*.ts", "docs/*.md").'
1185
860
  }),
1186
- searchPath: z6.string().optional().meta({
861
+ searchPath: z5.string().optional().meta({
1187
862
  description: "The path to search within, relative to the project directory. Defaults to the project directory."
1188
863
  }),
1189
- excludeGitIgnored: z6.boolean().optional().meta({
864
+ excludeGitIgnored: z5.boolean().optional().meta({
1190
865
  description: "Whether to exclude files ignored by Git. Defaults to true."
1191
866
  })
1192
867
  });
1193
- var GlobToolOutput = z6.object({
1194
- searchPattern: z6.string().meta({
868
+ var GlobToolOutput = z5.object({
869
+ searchPattern: z5.string().meta({
1195
870
  description: "The glob pattern that was searched for."
1196
871
  }),
1197
- searchPath: z6.string().meta({
872
+ searchPath: z5.string().meta({
1198
873
  description: "The path that was searched within."
1199
874
  }),
1200
- excludeGitIgnored: z6.boolean().meta({
875
+ excludeGitIgnored: z5.boolean().meta({
1201
876
  description: "Whether files ignored by Git were excluded."
1202
877
  }),
1203
- matchingPaths: z6.array(z6.string()).meta({
878
+ matchingPaths: z5.array(z5.string()).meta({
1204
879
  description: "The list of file paths that matched the glob search, relative to the project directory."
1205
880
  })
1206
881
  });
1207
- var GlobTool = class extends EnvironmentToolBase {
882
+ var GlobTool = class extends EnvironmentToolBase5 {
1208
883
  /**
1209
884
  * Returns the metadata for the tool.
1210
885
  *
@@ -1239,21 +914,16 @@ var GlobTool = class extends EnvironmentToolBase {
1239
914
  validateRelativePath(searchPath);
1240
915
  }
1241
916
  const untrailingslashedSearchPath = searchPath === "" ? "." : searchPath.replace(/\/+$/, "");
1242
- const escapedSearchPath = escapeCommandArg(untrailingslashedSearchPath);
917
+ const escapedSearchPath = escapeCommandArg5(untrailingslashedSearchPath);
1243
918
  let command = `find ${escapedSearchPath} -type f`;
1244
919
  if (excludeGitIgnored) {
1245
- let gitIgnoredPaths = [];
1246
- try {
1247
- const { content: gitignoreContent } = await env.readFile(".gitignore");
1248
- gitIgnoredPaths = gitignoreContent.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
1249
- } catch (_error) {
1250
- }
920
+ const gitIgnoredPaths = await getGitIgnoredPaths(env);
1251
921
  for (const gitIgnoredPath of gitIgnoredPaths) {
1252
922
  if (!gitIgnoredPath.endsWith("/")) {
1253
- const escapedPath = escapeCommandArg(`*/${gitIgnoredPath}/*`);
1254
- command += ` -not -name ${escapeCommandArg(gitIgnoredPath)} -not -path ${escapedPath}`;
923
+ const escapedPath = escapeCommandArg5(`*/${gitIgnoredPath}/*`);
924
+ command += ` -not -name ${escapeCommandArg5(gitIgnoredPath)} -not -path ${escapedPath}`;
1255
925
  } else {
1256
- const escapedPath = escapeCommandArg(`*/${gitIgnoredPath}*`);
926
+ const escapedPath = escapeCommandArg5(`*/${gitIgnoredPath}*`);
1257
927
  command += ` -not -path ${escapedPath}`;
1258
928
  }
1259
929
  }
@@ -1289,10 +959,11 @@ var GlobTool = class extends EnvironmentToolBase {
1289
959
  /**
1290
960
  * Converts the tool output to a format suitable for model consumption.
1291
961
  *
1292
- * @param output - The output from the tool execution.
962
+ * @param options - The tool result, including the output from the tool execution.
1293
963
  * @returns The formatted tool result.
1294
964
  */
1295
- toModelOutput(output) {
965
+ toModelOutput(options) {
966
+ const { output } = options;
1296
967
  if (output.matchingPaths.length === 0) {
1297
968
  return {
1298
969
  type: "text",
@@ -1341,8 +1012,276 @@ ${bulletPoints}
1341
1012
  }
1342
1013
  };
1343
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
+
1344
1279
  // src/tools/list-directory-tool.ts
1345
1280
  import { z as z7 } from "zod";
1281
+ import {
1282
+ EnvironmentToolBase as EnvironmentToolBase7,
1283
+ escapeCommandArg as escapeCommandArg7
1284
+ } from "@ai-code-agents/environment-utils";
1346
1285
  var ListDirectoryToolName = "list_directory";
1347
1286
  var ListDirectoryToolInput = z7.object({
1348
1287
  path: z7.string().meta({
@@ -1360,7 +1299,7 @@ var ListDirectoryToolOutput = z7.object({
1360
1299
  description: "List of subdirectories in the directory."
1361
1300
  })
1362
1301
  });
1363
- var ListDirectoryTool = class extends EnvironmentToolBase {
1302
+ var ListDirectoryTool = class extends EnvironmentToolBase7 {
1364
1303
  /**
1365
1304
  * Returns the metadata for the tool.
1366
1305
  *
@@ -1385,7 +1324,7 @@ var ListDirectoryTool = class extends EnvironmentToolBase {
1385
1324
  * @returns A promise that resolves to the tool execution result.
1386
1325
  */
1387
1326
  async executeForEnvironment(env, input) {
1388
- const escapedPath = escapeCommandArg(input.path);
1327
+ const escapedPath = escapeCommandArg7(input.path);
1389
1328
  const command = `ls -la ${escapedPath}`;
1390
1329
  const { stdout, stderr, exitCode } = await env.runCommand(command);
1391
1330
  if (exitCode !== 0) {
@@ -1418,10 +1357,11 @@ var ListDirectoryTool = class extends EnvironmentToolBase {
1418
1357
  /**
1419
1358
  * Converts the tool output to a format suitable for model consumption.
1420
1359
  *
1421
- * @param output - The output from the tool execution.
1360
+ * @param options - The tool result, including the output from the tool execution.
1422
1361
  * @returns The formatted tool result.
1423
1362
  */
1424
- toModelOutput(output) {
1363
+ toModelOutput(options) {
1364
+ const { output } = options;
1425
1365
  const formatEntries = (entries, type) => {
1426
1366
  if (entries.length === 0) {
1427
1367
  return `No ${type} found.`;
@@ -1468,6 +1408,10 @@ Directories:
1468
1408
 
1469
1409
  // src/tools/move-file-tool.ts
1470
1410
  import { z as z8 } from "zod";
1411
+ import {
1412
+ EnvironmentToolBase as EnvironmentToolBase8,
1413
+ MoveFileResult
1414
+ } from "@ai-code-agents/environment-utils";
1471
1415
  var MoveFileToolName = "move_file";
1472
1416
  var MoveFileToolInput = z8.object({
1473
1417
  sourcePath: z8.string().meta({
@@ -1478,7 +1422,7 @@ var MoveFileToolInput = z8.object({
1478
1422
  })
1479
1423
  });
1480
1424
  var MoveFileToolOutput = MoveFileResult;
1481
- var MoveFileTool = class extends EnvironmentToolBase {
1425
+ var MoveFileTool = class extends EnvironmentToolBase8 {
1482
1426
  /**
1483
1427
  * Returns the metadata for the tool.
1484
1428
  *
@@ -1508,10 +1452,11 @@ var MoveFileTool = class extends EnvironmentToolBase {
1508
1452
  /**
1509
1453
  * Converts the tool output to a format suitable for model consumption.
1510
1454
  *
1511
- * @param output - The output from the tool execution.
1455
+ * @param options - The tool result, including the output from the tool execution.
1512
1456
  * @returns The formatted tool result.
1513
1457
  */
1514
- toModelOutput(output) {
1458
+ toModelOutput(options) {
1459
+ const { output } = options;
1515
1460
  return {
1516
1461
  type: "text",
1517
1462
  value: `File \`${output.sourcePath}\` moved successfully to \`${output.destinationPath}\`.`
@@ -1537,6 +1482,10 @@ var MoveFileTool = class extends EnvironmentToolBase {
1537
1482
 
1538
1483
  // src/tools/read-file-tool.ts
1539
1484
  import { z as z9 } from "zod";
1485
+ import {
1486
+ EnvironmentToolBase as EnvironmentToolBase9,
1487
+ ReadFileResult
1488
+ } from "@ai-code-agents/environment-utils";
1540
1489
 
1541
1490
  // src/util/get-language-identifier-from-file-path.ts
1542
1491
  import path4 from "path";
@@ -1714,7 +1663,7 @@ ${output.content}
1714
1663
  \`\`\`
1715
1664
  `;
1716
1665
  };
1717
- var ReadFileTool = class extends EnvironmentToolBase {
1666
+ var ReadFileTool = class extends EnvironmentToolBase9 {
1718
1667
  /**
1719
1668
  * Returns the metadata for the tool.
1720
1669
  *
@@ -1744,10 +1693,11 @@ var ReadFileTool = class extends EnvironmentToolBase {
1744
1693
  /**
1745
1694
  * Converts the tool output to a format suitable for model consumption.
1746
1695
  *
1747
- * @param output - The output from the tool execution.
1696
+ * @param options - The tool result, including the output from the tool execution.
1748
1697
  * @returns The formatted tool result.
1749
1698
  */
1750
- toModelOutput(output) {
1699
+ toModelOutput(options) {
1700
+ const { output } = options;
1751
1701
  return {
1752
1702
  type: "text",
1753
1703
  value: formatModelResponse(output)
@@ -1791,13 +1741,17 @@ export function Loader(props: LoaderProps) {
1791
1741
 
1792
1742
  // src/tools/read-many-files-tool.ts
1793
1743
  import { z as z10 } from "zod";
1744
+ import {
1745
+ EnvironmentToolBase as EnvironmentToolBase10,
1746
+ ReadFileResult as ReadFileResult2
1747
+ } from "@ai-code-agents/environment-utils";
1794
1748
  var ReadManyFilesToolName = "read_many_files";
1795
1749
  var ReadManyFilesToolInput = z10.object({
1796
1750
  paths: z10.array(z10.string()).meta({
1797
1751
  description: "The paths to the files to read, relative to the project directory."
1798
1752
  })
1799
1753
  });
1800
- var ReadManyFilesToolOutput = z10.record(z10.string(), ReadFileResult);
1754
+ var ReadManyFilesToolOutput = z10.record(z10.string(), ReadFileResult2);
1801
1755
  var formatModelResponse2 = (output) => {
1802
1756
  const language = getLanguageIdentifierFromFilePath(output.path);
1803
1757
  return `File: \`${output.path}\`
@@ -1807,7 +1761,7 @@ ${output.content}
1807
1761
  \`\`\`
1808
1762
  `;
1809
1763
  };
1810
- var ReadManyFilesTool = class extends EnvironmentToolBase {
1764
+ var ReadManyFilesTool = class extends EnvironmentToolBase10 {
1811
1765
  /**
1812
1766
  * Returns the metadata for the tool.
1813
1767
  *
@@ -1843,10 +1797,11 @@ var ReadManyFilesTool = class extends EnvironmentToolBase {
1843
1797
  /**
1844
1798
  * Converts the tool output to a format suitable for model consumption.
1845
1799
  *
1846
- * @param output - The output from the tool execution.
1800
+ * @param options - The tool result, including the output from the tool execution.
1847
1801
  * @returns The formatted tool result.
1848
1802
  */
1849
- toModelOutput(output) {
1803
+ toModelOutput(options) {
1804
+ const { output } = options;
1850
1805
  const fileContentResponses = Object.values(output).map(
1851
1806
  (fileResult) => formatModelResponse2(fileResult)
1852
1807
  );
@@ -1903,6 +1858,10 @@ export function Loader(props: LoaderProps) {
1903
1858
 
1904
1859
  // src/tools/run-command-tool.ts
1905
1860
  import { z as z11 } from "zod";
1861
+ import {
1862
+ EnvironmentToolBase as EnvironmentToolBase11,
1863
+ RunCommandResult
1864
+ } from "@ai-code-agents/environment-utils";
1906
1865
  var RunCommandToolName = "run_command";
1907
1866
  var RunCommandToolInput = z11.object({
1908
1867
  command: z11.string().meta({ description: "The CLI command to run, including all arguments." })
@@ -1923,7 +1882,7 @@ Output (stdout): ${stdout}
1923
1882
  Error Output (stderr): ${stderr}
1924
1883
  `;
1925
1884
  }
1926
- var RunCommandTool = class extends EnvironmentToolBase {
1885
+ var RunCommandTool = class extends EnvironmentToolBase11 {
1927
1886
  /**
1928
1887
  * Returns the metadata for the tool.
1929
1888
  *
@@ -1953,10 +1912,11 @@ var RunCommandTool = class extends EnvironmentToolBase {
1953
1912
  /**
1954
1913
  * Converts the tool output to a format suitable for model consumption.
1955
1914
  *
1956
- * @param output - The output from the tool execution.
1915
+ * @param options - The tool result, including the output from the tool execution.
1957
1916
  * @returns The formatted tool result.
1958
1917
  */
1959
- toModelOutput(output) {
1918
+ toModelOutput(options) {
1919
+ const { output } = options;
1960
1920
  return {
1961
1921
  type: "text",
1962
1922
  value: formatCommandResultToModelResponse(output)
@@ -2028,6 +1988,10 @@ added 1 package, and changed 1 package in 2s
2028
1988
 
2029
1989
  // src/tools/write-file-tool.ts
2030
1990
  import { z as z12 } from "zod";
1991
+ import {
1992
+ EnvironmentToolBase as EnvironmentToolBase12,
1993
+ WriteFileResult
1994
+ } from "@ai-code-agents/environment-utils";
2031
1995
  var WriteFileToolName = "write_file";
2032
1996
  var WriteFileToolInput = z12.object({
2033
1997
  path: z12.string().meta({
@@ -2038,7 +2002,7 @@ var WriteFileToolInput = z12.object({
2038
2002
  })
2039
2003
  });
2040
2004
  var WriteFileToolOutput = WriteFileResult;
2041
- var WriteFileTool = class extends EnvironmentToolBase {
2005
+ var WriteFileTool = class extends EnvironmentToolBase12 {
2042
2006
  /**
2043
2007
  * Returns the metadata for the tool.
2044
2008
  *
@@ -2068,10 +2032,11 @@ var WriteFileTool = class extends EnvironmentToolBase {
2068
2032
  /**
2069
2033
  * Converts the tool output to a format suitable for model consumption.
2070
2034
  *
2071
- * @param output - The output from the tool execution.
2035
+ * @param options - The tool result, including the output from the tool execution.
2072
2036
  * @returns The formatted tool result.
2073
2037
  */
2074
- toModelOutput(output) {
2038
+ toModelOutput(options) {
2039
+ const { output } = options;
2075
2040
  return {
2076
2041
  type: "text",
2077
2042
  value: `File \`${output.path}\` written successfully.`
@@ -2099,15 +2064,11 @@ var WriteFileTool = class extends EnvironmentToolBase {
2099
2064
  }
2100
2065
  };
2101
2066
 
2102
- // src/agent-creators.ts
2103
- import {
2104
- Experimental_Agent as Agent,
2105
- stepCountIs,
2106
- hasToolCall
2107
- } from "ai";
2108
-
2109
2067
  // src/tools/submit-tool.ts
2110
2068
  import { z as z13 } from "zod";
2069
+ import {
2070
+ ToolBase
2071
+ } from "@ai-code-agents/environment-utils";
2111
2072
  var SubmitToolName = "submit";
2112
2073
  var SubmitToolInput = z13.object({});
2113
2074
  var SubmitToolOutput = z13.object({});
@@ -2132,7 +2093,7 @@ var SubmitTool = class extends ToolBase {
2132
2093
  * Executes the tool with the given input.
2133
2094
  *
2134
2095
  * @param _ - The input for the tool. Unused.
2135
- * @param __ - Options from the tool call. Unused.
2096
+ * @param __ - Options for the tool execution. Unused.
2136
2097
  * @returns A promise that resolves to the tool execution result.
2137
2098
  */
2138
2099
  async execute(_, __) {
@@ -2141,7 +2102,7 @@ var SubmitTool = class extends ToolBase {
2141
2102
  /**
2142
2103
  * Converts the tool output to a format suitable for model consumption.
2143
2104
  *
2144
- * @param _ - The output from the tool execution. Unused.
2105
+ * @param _ - The tool result, including the output from the tool execution. Unused.
2145
2106
  * @returns The formatted tool result.
2146
2107
  */
2147
2108
  toModelOutput(_) {
@@ -2160,13 +2121,23 @@ var SubmitTool = class extends ToolBase {
2160
2121
  }
2161
2122
  };
2162
2123
 
2124
+ // src/agent-creators.ts
2125
+ import {
2126
+ ToolLoopAgent,
2127
+ stepCountIs,
2128
+ hasToolCall
2129
+ } from "ai";
2130
+
2163
2131
  // src/instructions.ts
2164
2132
  function getAdditionalInstructions(config) {
2165
2133
  const { maxSteps, allowSubmit, tools } = config;
2166
- 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
+ ];
2167
2138
  for (const [toolName, tool] of Object.entries(tools)) {
2168
2139
  if ("examples" in tool && Array.isArray(tool.examples) && tool.examples.length > 0) {
2169
- let toolSection = `### Tool: \`${toolName}\`
2140
+ let toolSection = `## Tool: \`${toolName}\`
2170
2141
 
2171
2142
  `;
2172
2143
  for (const example of tool.examples) {
@@ -2175,40 +2146,48 @@ function getAdditionalInstructions(config) {
2175
2146
  exampleSections.push(toolSection.trim());
2176
2147
  }
2177
2148
  }
2178
- const workflowGuidelines = [
2179
- /*
2180
- * If there are examples, the tool information is already mentioned in a separate Tool Examples section.
2181
- * Therefore the line below is only relevant if there are no examples.
2182
- */
2183
- ...!exampleSections.length ? [
2184
- "You have access to several tools to assist you in completing your task."
2185
- ] : [],
2186
- "You must issue tool calls to complete your task. Do not engage with the user directly.",
2187
- ...allowSubmit ? [
2188
- `Once you think you have completed your task, call the \`${SubmitToolName}\` tool to submit your results.`
2189
- ] : [],
2190
- `You have a maximum of ${maxSteps} steps to complete your task.`
2191
- ];
2192
- const importantWorkflowGuidelines = `## Important Workflow Guidelines
2193
-
2194
- ${workflowGuidelines.map((line) => `- ${line}`).join("\n")}
2195
-
2196
- 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}
2197
2155
  `;
2198
- if (exampleSections.length) {
2199
- return `## Tool Examples
2200
-
2201
- You have access to several tools to assist you in completing your task. Here are some examples of how to use them:
2202
-
2203
- ${exampleSections.join("\n\n")}
2204
-
2205
- ` + 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());
2206
2182
  }
2207
- return importantWorkflowGuidelines;
2183
+ const finalReminder = getCodeAgentFinalReminder();
2184
+ return [...exampleSections, ...constraintSections, finalReminder].join(
2185
+ "\n\n"
2186
+ );
2208
2187
  }
2209
2188
  function formatExampleForInstructions(toolName, example) {
2210
- const input = typeof example.input === "undefined" ? "" : typeof example.input === "string" || typeof example.input === "number" ? example.input : JSON.stringify(example.input, null, 2);
2211
- 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);
2212
2191
  if (output === "") {
2213
2192
  return `<example>
2214
2193
  <tool_call>
@@ -2225,8 +2204,42 @@ ${output}
2225
2204
  </tool_response>
2226
2205
  </example>`;
2227
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
+ }
2228
2238
 
2229
2239
  // src/tool-creators.ts
2240
+ import {
2241
+ isCommandLine
2242
+ } from "@ai-code-agents/environment-utils";
2230
2243
  var availableEnvironmentTools = {
2231
2244
  [ReadFileToolName]: ReadFileTool,
2232
2245
  [WriteFileToolName]: WriteFileTool,
@@ -2237,12 +2250,14 @@ var availableEnvironmentTools = {
2237
2250
  [ReadManyFilesToolName]: ReadManyFilesTool,
2238
2251
  [GetProjectFileStructureToolName]: GetProjectFileStructureTool,
2239
2252
  [GlobToolName]: GlobTool,
2253
+ [GrepToolName]: GrepTool,
2240
2254
  [ListDirectoryToolName]: ListDirectoryTool,
2241
2255
  [RunCommandToolName]: RunCommandTool
2242
2256
  };
2243
2257
  var cliOnlyTools = [
2244
2258
  GetProjectFileStructureToolName,
2245
2259
  GlobToolName,
2260
+ GrepToolName,
2246
2261
  ListDirectoryToolName,
2247
2262
  RunCommandToolName
2248
2263
  ];
@@ -2251,6 +2266,7 @@ var readonlyTools = [
2251
2266
  ReadManyFilesToolName,
2252
2267
  GetProjectFileStructureToolName,
2253
2268
  GlobToolName,
2269
+ GrepToolName,
2254
2270
  ListDirectoryToolName
2255
2271
  ];
2256
2272
  var dangerousTools = [
@@ -2278,7 +2294,7 @@ function createEnvironmentTool(toolName, environment, config) {
2278
2294
  }
2279
2295
  function createToolsForEnvironment(environment, toolsDefinition = "all") {
2280
2296
  const sanitizedToolsDefinition = sanitizeToolsDefinition(toolsDefinition);
2281
- const isCliEnvironment = "runCommand" in environment;
2297
+ const isCliEnvironment = isCommandLine(environment);
2282
2298
  const tools = {};
2283
2299
  for (const toolDefinition of sanitizedToolsDefinition) {
2284
2300
  const actualToolName = toolDefinition.toolName;
@@ -2299,7 +2315,7 @@ function createToolsForEnvironment(environment, toolsDefinition = "all") {
2299
2315
  }
2300
2316
  tools[toolNameToUse] = createEnvironmentTool(
2301
2317
  actualToolName,
2302
- isCliEnvironment ? environment : environment,
2318
+ environment,
2303
2319
  toolConfig
2304
2320
  );
2305
2321
  }
@@ -2355,6 +2371,54 @@ function sanitizeToolsDefinition(toolsDefinition) {
2355
2371
  });
2356
2372
  }
2357
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
+
2358
2422
  // src/util/get-step-log.ts
2359
2423
  function getStepLog(stepResult) {
2360
2424
  const { content } = stepResult;
@@ -2368,13 +2432,17 @@ function getStepLog(stepResult) {
2368
2432
  }
2369
2433
  logEntry += ": ";
2370
2434
  if (part.type === "tool-call" && "input" in part) {
2371
- 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
+ );
2372
2438
  } else if (part.type === "tool-result" && "output" in part) {
2373
- 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
+ );
2374
2442
  } else if (part.type === "tool-error" && "error" in part) {
2375
2443
  logEntry += typeof part.error === "object" && part.error !== null && "message" in part.error ? part.error.message : String(part.error);
2376
2444
  } else if (part.type === "text" && "text" in part) {
2377
- logEntry += part.text;
2445
+ logEntry += truncateString(part.text);
2378
2446
  }
2379
2447
  logEntry += "\n";
2380
2448
  });
@@ -2383,6 +2451,9 @@ function getStepLog(stepResult) {
2383
2451
 
2384
2452
  // src/agent-creators.ts
2385
2453
  function createCodeAgent(agentConfig) {
2454
+ return new ToolLoopAgent(createCodeAgentSettings(agentConfig));
2455
+ }
2456
+ function createCodeAgentSettings(agentConfig) {
2386
2457
  const {
2387
2458
  maxSteps,
2388
2459
  allowSubmit,
@@ -2391,46 +2462,29 @@ function createCodeAgent(agentConfig) {
2391
2462
  tools: originalTools,
2392
2463
  stopWhen: originalStopWhen,
2393
2464
  prepareStep: originalPrepareStep,
2394
- system: originalSystemInstruction,
2465
+ instructions: originalSystemInstruction,
2395
2466
  ...remainingConfig
2396
2467
  } = agentConfig;
2397
2468
  let agentSettings;
2398
- let environmentTools;
2469
+ let tools;
2399
2470
  if ("environments" in remainingConfig) {
2400
2471
  const { environments, environmentToolsDefinition, ...agentSettingsInput } = remainingConfig;
2401
2472
  agentSettings = { ...agentSettingsInput };
2402
- environmentTools = {};
2403
- for (const [environmentName, environment] of Object.entries(environments)) {
2404
- if (!(environmentName in environmentToolsDefinition)) {
2405
- throw new Error(
2406
- `No tools definition provided for environment "${environmentName}". Please provide a tools definition for each environment.`
2407
- );
2408
- }
2409
- const environmentTools2 = createToolsForNamedEnvironment(
2410
- environmentName,
2411
- environment,
2412
- environmentToolsDefinition[environmentName]
2413
- );
2414
- for (const [toolName, tool] of Object.entries(environmentTools2)) {
2415
- if (toolName in environmentTools2) {
2416
- throw new Error(
2417
- `Tool name conflict: The tool name "${toolName}" from environment "${environmentName}" is already used by another environment's tools.`
2418
- );
2419
- }
2420
- environmentTools2[toolName] = tool;
2421
- }
2422
- }
2473
+ tools = createCodeAgentTools(
2474
+ { environments, environmentToolsDefinition },
2475
+ originalTools
2476
+ );
2423
2477
  } else if ("environment" in remainingConfig) {
2424
2478
  const { environment, environmentToolsDefinition, ...agentSettingsInput } = remainingConfig;
2425
2479
  agentSettings = { ...agentSettingsInput };
2426
- environmentTools = createToolsForEnvironment(
2427
- environment,
2428
- environmentToolsDefinition
2480
+ tools = createCodeAgentTools(
2481
+ { environment, environmentToolsDefinition },
2482
+ originalTools
2429
2483
  );
2430
2484
  } else {
2431
2485
  agentSettings = { ...remainingConfig };
2486
+ tools = originalTools || {};
2432
2487
  }
2433
- const tools = environmentTools && originalTools ? mergeTools(environmentTools, originalTools) : originalTools || environmentTools || {};
2434
2488
  if (allowSubmit) {
2435
2489
  if (SubmitToolName in tools) {
2436
2490
  throw new Error(
@@ -2458,16 +2512,54 @@ ${stepLog}`, stepCount - 1);
2458
2512
  } : originalPrepareStep;
2459
2513
  const stopWhenCondition = allowSubmit ? [stepCountIs(maxSteps), hasToolCall(SubmitToolName)] : stepCountIs(maxSteps);
2460
2514
  const stopWhen = originalStopWhen ? mergeStopWhen(originalStopWhen, stopWhenCondition) : stopWhenCondition;
2461
- const system = !omitAdditionalInstructions ? mergeSystemInstructions(
2515
+ const instructions = !omitAdditionalInstructions ? mergeSystemInstructions(
2462
2516
  originalSystemInstruction,
2463
2517
  getAdditionalInstructions({ maxSteps, allowSubmit, tools })
2464
2518
  ) : originalSystemInstruction;
2465
- return new Agent({
2519
+ return {
2466
2520
  ...agentSettings,
2467
- system,
2521
+ instructions,
2468
2522
  prepareStep,
2469
2523
  stopWhen
2470
- });
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
+ );
2471
2563
  }
2472
2564
  function mergeTools(baseTools, additionalTools) {
2473
2565
  const tools = { ...baseTools };
@@ -2493,9 +2585,26 @@ function mergeStopWhen(baseStopWhen, additionalStopWhen) {
2493
2585
  }
2494
2586
  return [baseStopWhen, additionalStopWhen];
2495
2587
  }
2496
- function mergeSystemInstructions(baseSystem, additionalInstructions) {
2497
- if (baseSystem) {
2498
- 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()}
2499
2608
 
2500
2609
  ${additionalInstructions}`;
2501
2610
  }
@@ -2520,12 +2629,10 @@ function createEnvironment(environmentName, config) {
2520
2629
  return new EnvironmentClass(config);
2521
2630
  }
2522
2631
  export {
2523
- CopyFileResult,
2524
2632
  CopyFileTool,
2525
2633
  CopyFileToolInput,
2526
2634
  CopyFileToolName,
2527
2635
  CopyFileToolOutput,
2528
- DeleteFileResult,
2529
2636
  DeleteFileTool,
2530
2637
  DeleteFileToolInput,
2531
2638
  DeleteFileToolName,
@@ -2547,20 +2654,22 @@ export {
2547
2654
  GlobToolInput,
2548
2655
  GlobToolName,
2549
2656
  GlobToolOutput,
2657
+ GrepTool,
2658
+ GrepToolInput,
2659
+ GrepToolName,
2660
+ GrepToolOutput,
2550
2661
  ListDirectoryTool,
2551
2662
  ListDirectoryToolInput,
2552
2663
  ListDirectoryToolName,
2553
2664
  ListDirectoryToolOutput,
2554
2665
  MockFilesystemEnvironment,
2555
2666
  MockFilesystemEnvironmentName,
2556
- MoveFileResult,
2557
2667
  MoveFileTool,
2558
2668
  MoveFileToolInput,
2559
2669
  MoveFileToolName,
2560
2670
  MoveFileToolOutput,
2561
2671
  NodeFilesystemEnvironment,
2562
2672
  NodeFilesystemEnvironmentName,
2563
- ReadFileResult,
2564
2673
  ReadFileTool,
2565
2674
  ReadFileToolInput,
2566
2675
  ReadFileToolName,
@@ -2569,19 +2678,23 @@ export {
2569
2678
  ReadManyFilesToolInput,
2570
2679
  ReadManyFilesToolName,
2571
2680
  ReadManyFilesToolOutput,
2572
- RunCommandResult,
2573
2681
  RunCommandTool,
2574
2682
  RunCommandToolInput,
2575
2683
  RunCommandToolName,
2576
2684
  RunCommandToolOutput,
2685
+ SubmitTool,
2686
+ SubmitToolInput,
2687
+ SubmitToolName,
2688
+ SubmitToolOutput,
2577
2689
  UnsafeLocalEnvironment,
2578
2690
  UnsafeLocalEnvironmentName,
2579
- WriteFileResult,
2580
2691
  WriteFileTool,
2581
2692
  WriteFileToolInput,
2582
2693
  WriteFileToolName,
2583
2694
  WriteFileToolOutput,
2584
2695
  createCodeAgent,
2696
+ createCodeAgentSettings,
2697
+ createCodeAgentTools,
2585
2698
  createEnvironment,
2586
2699
  createEnvironmentTool,
2587
2700
  createToolsForEnvironment,