@robota-sdk/agent-tools 3.0.0-beta.60 → 3.0.0-beta.62

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.
@@ -1,6 +1,275 @@
1
- // src/registry/tool-registry.ts
2
- import { ValidationError } from "@robota-sdk/agent-core";
3
- import { logger } from "@robota-sdk/agent-core";
1
+ import { readFile, readdir, stat, mkdir, writeFile, chmod, rename, rm } from 'fs/promises';
2
+ import { join, posix, isAbsolute, resolve, dirname, basename } from 'path';
3
+ import { ValidationError, ToolExecutionError, logger } from '@robota-sdk/agent-core';
4
+ import { spawn } from 'child_process';
5
+ import { z } from 'zod';
6
+ import { randomBytes } from 'crypto';
7
+ import fg from 'fast-glob';
8
+
9
+ // src/sandbox/e2b-sandbox-client.ts
10
+ var E2BSandboxClient = class {
11
+ sandbox;
12
+ connectSandbox;
13
+ createSandboxFromSnapshot;
14
+ constructor(options) {
15
+ this.sandbox = options.sandbox;
16
+ this.connectSandbox = options.connectSandbox;
17
+ this.createSandboxFromSnapshot = options.createSandboxFromSnapshot;
18
+ }
19
+ async run(command, options) {
20
+ const result = await this.sandbox.commands.run(command, {
21
+ background: false,
22
+ timeoutMs: options?.timeoutMs,
23
+ cwd: options?.workingDirectory
24
+ });
25
+ return {
26
+ stdout: result.stdout ?? "",
27
+ stderr: result.stderr ?? "",
28
+ exitCode: result.exitCode ?? result.exit_code ?? 0
29
+ };
30
+ }
31
+ async readFile(path) {
32
+ const content = await this.sandbox.files.read(path);
33
+ return typeof content === "string" ? content : Buffer.from(content).toString("utf8");
34
+ }
35
+ async writeFile(path, content) {
36
+ await this.sandbox.files.write(path, content);
37
+ }
38
+ async snapshot() {
39
+ if (this.sandbox.createSnapshot) {
40
+ const snapshot = await this.sandbox.createSnapshot();
41
+ const snapshotId = snapshot.snapshotId ?? snapshot.id;
42
+ if (!snapshotId) {
43
+ throw new Error("E2B createSnapshot() did not return a snapshot id.");
44
+ }
45
+ return snapshotId;
46
+ }
47
+ const sandboxId = this.sandbox.sandboxId;
48
+ if (!sandboxId) {
49
+ throw new Error("E2B sandboxId is required to create a resumable sandbox snapshot.");
50
+ }
51
+ if (!this.sandbox.pause) {
52
+ throw new Error("E2B sandbox adapter does not expose pause().");
53
+ }
54
+ await this.sandbox.pause();
55
+ return sandboxId;
56
+ }
57
+ async restore(snapshotId) {
58
+ if (this.createSandboxFromSnapshot) {
59
+ this.sandbox = await this.createSandboxFromSnapshot(snapshotId);
60
+ return;
61
+ }
62
+ if (this.connectSandbox) {
63
+ this.sandbox = await this.connectSandbox(snapshotId);
64
+ return;
65
+ }
66
+ if (this.sandbox.sandboxId === snapshotId && this.sandbox.connect) {
67
+ this.sandbox = await this.sandbox.connect();
68
+ return;
69
+ }
70
+ throw new Error(
71
+ "E2B sandbox restore requires connectSandbox(snapshotId) or sandbox.connect()."
72
+ );
73
+ }
74
+ };
75
+
76
+ // src/sandbox/in-memory-sandbox-client.ts
77
+ var InMemorySandboxClient = class {
78
+ files = /* @__PURE__ */ new Map();
79
+ snapshots = /* @__PURE__ */ new Map();
80
+ runHandler;
81
+ snapshotSequence = 0;
82
+ constructor(options = {}) {
83
+ for (const [path, content] of Object.entries(options.files ?? {})) {
84
+ this.files.set(path, content);
85
+ }
86
+ this.runHandler = options.runHandler;
87
+ }
88
+ async run(command, options) {
89
+ if (this.runHandler) {
90
+ return this.runHandler(command, options, this.files);
91
+ }
92
+ return { stdout: "", stderr: "", exitCode: 0 };
93
+ }
94
+ async readFile(path) {
95
+ const content = this.files.get(path);
96
+ if (content === void 0) {
97
+ throw new Error(`Sandbox file not found: ${path}`);
98
+ }
99
+ return content;
100
+ }
101
+ async writeFile(path, content) {
102
+ this.files.set(path, content);
103
+ }
104
+ async snapshot() {
105
+ const snapshotId = `snapshot-${++this.snapshotSequence}`;
106
+ this.snapshots.set(snapshotId, new Map(this.files));
107
+ return snapshotId;
108
+ }
109
+ async restore(snapshotId) {
110
+ const snapshot = this.snapshots.get(snapshotId);
111
+ if (!snapshot) {
112
+ throw new Error(`Sandbox snapshot not found: ${snapshotId}`);
113
+ }
114
+ this.files.clear();
115
+ for (const [path, content] of snapshot.entries()) {
116
+ this.files.set(path, content);
117
+ }
118
+ }
119
+ getFile(path) {
120
+ return this.files.get(path);
121
+ }
122
+ };
123
+ var DEFAULT_TARGET_ROOT = "/workspace";
124
+ var WINDOWS_ABSOLUTE_PATH_PATTERN = /^[A-Za-z]:[\\/]/;
125
+ var SHELL_QUOTE_PATTERN = /'/g;
126
+ async function applyWorkspaceManifest(sandboxClient, manifest, options = {}) {
127
+ if (sandboxClient.applyManifest) {
128
+ return sandboxClient.applyManifest(manifest, options);
129
+ }
130
+ const targetRoot = normalizeSandboxRoot(options.targetRoot ?? DEFAULT_TARGET_ROOT);
131
+ const appliedEntries = [];
132
+ for (const [rawPath, entry] of Object.entries(manifest.entries)) {
133
+ const path = validateWorkspaceManifestPath(rawPath);
134
+ const targetPath = joinSandboxPath(targetRoot, path);
135
+ appliedEntries.push(
136
+ await applyManifestEntry(sandboxClient, path, targetPath, targetRoot, entry, options)
137
+ );
138
+ }
139
+ return { entries: appliedEntries };
140
+ }
141
+ function validateWorkspaceManifestPath(path) {
142
+ if (path.length === 0) {
143
+ throw new Error("workspace manifest path must not be empty");
144
+ }
145
+ if (path.includes("\0")) {
146
+ throw new Error("workspace manifest path must not contain NUL bytes");
147
+ }
148
+ if (path.startsWith("/") || path.startsWith("\\") || WINDOWS_ABSOLUTE_PATH_PATTERN.test(path)) {
149
+ throw new Error("workspace manifest path must be workspace-relative");
150
+ }
151
+ const parts = path.replace(/\\/g, "/").split("/").filter(Boolean);
152
+ if (parts.length === 0) {
153
+ throw new Error("workspace manifest path must not resolve to the workspace root");
154
+ }
155
+ if (parts.some((part) => part === "..")) {
156
+ throw new Error("workspace manifest path cannot contain traversal segments");
157
+ }
158
+ const normalizedParts = parts.filter((part) => part !== ".");
159
+ if (normalizedParts.length === 0) {
160
+ throw new Error("workspace manifest path must not resolve to the workspace root");
161
+ }
162
+ return normalizedParts.join("/");
163
+ }
164
+ async function applyManifestEntry(sandboxClient, path, targetPath, targetRoot, entry, options) {
165
+ switch (entry.type) {
166
+ case "file":
167
+ await writeSandboxFile(sandboxClient, targetPath, targetRoot, entry.content);
168
+ return createAppliedEntry(path, entry.type);
169
+ case "dir":
170
+ await createSandboxDirectory(sandboxClient, targetPath);
171
+ return createAppliedEntry(path, entry.type);
172
+ case "localFile":
173
+ await copyLocalFile(sandboxClient, entry.src, targetPath, targetRoot, options);
174
+ return createAppliedEntry(path, entry.type);
175
+ case "localDir":
176
+ await copyLocalDirectory(sandboxClient, entry.src, targetPath, options);
177
+ return createAppliedEntry(path, entry.type);
178
+ case "gitRepo":
179
+ await cloneGitRepository(sandboxClient, entry, targetPath);
180
+ return createAppliedEntry(path, entry.type);
181
+ case "s3Mount":
182
+ case "gcsMount":
183
+ case "r2Mount":
184
+ case "azureBlobMount":
185
+ return {
186
+ path,
187
+ type: entry.type,
188
+ status: "unsupported",
189
+ message: `${entry.type} requires a provider-specific sandbox adapter.`
190
+ };
191
+ default:
192
+ return assertUnreachable(entry);
193
+ }
194
+ }
195
+ function createAppliedEntry(path, type) {
196
+ return { path, type, status: "applied" };
197
+ }
198
+ async function copyLocalFile(sandboxClient, source, targetPath, targetRoot, options) {
199
+ const hostSourcePath = resolveHostSourcePath(source, options.hostRoot);
200
+ const content = await readFile(hostSourcePath, "utf8");
201
+ await writeSandboxFile(sandboxClient, targetPath, targetRoot, content);
202
+ }
203
+ async function copyLocalDirectory(sandboxClient, source, targetPath, options) {
204
+ const hostSourcePath = resolveHostSourcePath(source, options.hostRoot);
205
+ await copyLocalDirectoryRecursive(sandboxClient, hostSourcePath, targetPath);
206
+ }
207
+ async function copyLocalDirectoryRecursive(sandboxClient, sourcePath, targetPath) {
208
+ await createSandboxDirectory(sandboxClient, targetPath);
209
+ const entries = await readdir(sourcePath, { withFileTypes: true });
210
+ for (const entry of entries) {
211
+ const childSourcePath = join(sourcePath, entry.name);
212
+ const childTargetPath = joinSandboxPath(targetPath, entry.name);
213
+ if (entry.isDirectory()) {
214
+ await copyLocalDirectoryRecursive(sandboxClient, childSourcePath, childTargetPath);
215
+ continue;
216
+ }
217
+ if (entry.isFile()) {
218
+ const content = await readFile(childSourcePath, "utf8");
219
+ await sandboxClient.writeFile(childTargetPath, content);
220
+ }
221
+ }
222
+ }
223
+ async function cloneGitRepository(sandboxClient, entry, targetPath) {
224
+ const shallowArgs = entry.shallow === false ? "" : " --depth 1";
225
+ const refArgs = entry.ref ? ` --branch ${quoteShellArg(entry.ref)}` : "";
226
+ await runSandboxCommand(
227
+ sandboxClient,
228
+ `git clone${shallowArgs}${refArgs} ${quoteShellArg(entry.url)} ${quoteShellArg(targetPath)}`
229
+ );
230
+ }
231
+ async function writeSandboxFile(sandboxClient, targetPath, targetRoot, content) {
232
+ const parentPath = posix.dirname(targetPath);
233
+ if (parentPath !== targetRoot) {
234
+ await createSandboxDirectory(sandboxClient, parentPath);
235
+ }
236
+ await sandboxClient.writeFile(targetPath, content);
237
+ }
238
+ async function createSandboxDirectory(sandboxClient, targetPath) {
239
+ await runSandboxCommand(sandboxClient, `mkdir -p ${quoteShellArg(targetPath)}`);
240
+ }
241
+ async function runSandboxCommand(sandboxClient, command) {
242
+ const result = await sandboxClient.run(command);
243
+ if (result.exitCode !== 0) {
244
+ throw new Error(
245
+ `workspace manifest command failed: ${command}
246
+ ${result.stderr ?? result.stdout}`
247
+ );
248
+ }
249
+ }
250
+ function resolveHostSourcePath(source, hostRoot) {
251
+ return isAbsolute(source) ? resolve(source) : resolve(hostRoot ?? process.cwd(), source);
252
+ }
253
+ function normalizeSandboxRoot(root) {
254
+ const normalized = root.replace(/\\/g, "/").replace(/\/+$/, "");
255
+ if (!normalized.startsWith("/")) {
256
+ throw new Error("workspace manifest targetRoot must be an absolute sandbox path");
257
+ }
258
+ return normalized.length === 0 ? "/" : normalized;
259
+ }
260
+ function joinSandboxPath(root, path) {
261
+ const normalizedRoot = normalizeSandboxRoot(root);
262
+ if (normalizedRoot === "/") {
263
+ return `/${path}`;
264
+ }
265
+ return `${normalizedRoot}/${path}`;
266
+ }
267
+ function quoteShellArg(value) {
268
+ return `'${value.replace(SHELL_QUOTE_PATTERN, "'\\''")}'`;
269
+ }
270
+ function assertUnreachable(value) {
271
+ throw new Error(`unsupported workspace manifest entry: ${JSON.stringify(value)}`);
272
+ }
4
273
  var ToolRegistry = class {
5
274
  tools = /* @__PURE__ */ new Map();
6
275
  /**
@@ -140,9 +409,6 @@ var ToolRegistry = class {
140
409
  }
141
410
  };
142
411
 
143
- // src/implementations/function-tool.ts
144
- import { ToolExecutionError, ValidationError as ValidationError2 } from "@robota-sdk/agent-core";
145
-
146
412
  // src/implementations/function-tool/schema-converter.ts
147
413
  function zodToJsonSchema(schema, options = {}) {
148
414
  const properties = {};
@@ -376,14 +642,14 @@ var FunctionTool = class {
376
642
  this.schema.parameters.properties || {},
377
643
  this.schema.parameters.additionalProperties
378
644
  );
379
- throw new ValidationError2(`Invalid parameters for tool "${toolName}": ${errors.join(", ")}`);
645
+ throw new ValidationError(`Invalid parameters for tool "${toolName}": ${errors.join(", ")}`);
380
646
  }
381
647
  const startTime = Date.now();
382
648
  let result;
383
649
  try {
384
650
  result = await this.fn(parameters, context);
385
651
  } catch (error) {
386
- if (error instanceof ToolExecutionError || error instanceof ValidationError2) {
652
+ if (error instanceof ToolExecutionError || error instanceof ValidationError) {
387
653
  throw error;
388
654
  }
389
655
  throw new ToolExecutionError(
@@ -440,13 +706,13 @@ var FunctionTool = class {
440
706
  */
441
707
  validateConstructorInputs() {
442
708
  if (!this.schema) {
443
- throw new ValidationError2("Tool schema is required");
709
+ throw new ValidationError("Tool schema is required");
444
710
  }
445
711
  if (!this.fn || typeof this.fn !== "function") {
446
- throw new ValidationError2("Tool function is required and must be a function");
712
+ throw new ValidationError("Tool function is required and must be a function");
447
713
  }
448
714
  if (!this.schema.name) {
449
- throw new ValidationError2("Tool schema must have a name");
715
+ throw new ValidationError("Tool schema must have a name");
450
716
  }
451
717
  }
452
718
  };
@@ -468,7 +734,7 @@ function createZodFunctionTool(name, description, zodSchema, fn) {
468
734
  const wrappedFn = async (parameters2, context) => {
469
735
  const parseResult = zodSchema.safeParse(parameters2);
470
736
  if (!parseResult.success) {
471
- throw new ValidationError2(`Zod validation failed: ${parseResult.error}`);
737
+ throw new ValidationError(`Zod validation failed: ${parseResult.error}`);
472
738
  }
473
739
  const result = await fn(parseResult.data || parameters2, context);
474
740
  return typeof result === "string" ? result : JSON.stringify(result);
@@ -476,9 +742,6 @@ function createZodFunctionTool(name, description, zodSchema, fn) {
476
742
  return new FunctionTool(schema, wrappedFn);
477
743
  }
478
744
 
479
- // src/implementations/openapi-tool.ts
480
- import { ToolExecutionError as ToolExecutionError2, ValidationError as ValidationError3 } from "@robota-sdk/agent-core";
481
-
482
745
  // src/implementations/openapi-schema-converter.ts
483
746
  var HTTP_METHODS = [
484
747
  "get",
@@ -630,7 +893,7 @@ var OpenAPITool = class {
630
893
  const toolName = this.schema.name;
631
894
  const validation = this.validateParameters(parameters);
632
895
  if (!validation.isValid) {
633
- throw new ValidationError3(
896
+ throw new ValidationError(
634
897
  `Invalid parameters for OpenAPI tool "${toolName}": ${validation.errors.join(", ")}`
635
898
  );
636
899
  }
@@ -649,11 +912,11 @@ var OpenAPITool = class {
649
912
  }
650
913
  };
651
914
  } catch (error) {
652
- if (error instanceof ToolExecutionError2 || error instanceof ValidationError3) {
915
+ if (error instanceof ToolExecutionError || error instanceof ValidationError) {
653
916
  throw error;
654
917
  }
655
918
  const safeError = error instanceof Error ? error : new Error(String(error));
656
- throw new ToolExecutionError2(
919
+ throw new ToolExecutionError(
657
920
  `OpenAPI tool execution failed: ${safeError.message}`,
658
921
  toolName,
659
922
  safeError,
@@ -800,19 +1063,39 @@ var OpenAPITool = class {
800
1063
  function createOpenAPITool(config) {
801
1064
  return new OpenAPITool(config);
802
1065
  }
803
-
804
- // src/builtins/bash-tool.ts
805
- import { spawn } from "child_process";
806
- import { z } from "zod";
807
1066
  var DEFAULT_TIMEOUT_MS = 12e4;
808
1067
  var BashSchema = z.object({
809
1068
  command: z.string().describe("The bash command to execute"),
810
1069
  timeout: z.number().optional().describe("Optional timeout in milliseconds (max 600000). Default is 120000 (2 minutes)"),
811
1070
  workingDirectory: z.string().optional().describe("Working directory for the command. Defaults to the current working directory")
812
1071
  });
813
- async function runBash(args) {
1072
+ async function runBash(args, options = {}) {
814
1073
  const { command, timeout = DEFAULT_TIMEOUT_MS, workingDirectory } = args;
815
- return new Promise((resolve3) => {
1074
+ if (options.sandboxClient) {
1075
+ try {
1076
+ const sandboxResult = await options.sandboxClient.run(command, {
1077
+ timeoutMs: timeout,
1078
+ workingDirectory
1079
+ });
1080
+ const output = sandboxResult.stderr ? `${sandboxResult.stdout}
1081
+ stderr:
1082
+ ${sandboxResult.stderr}` : sandboxResult.stdout;
1083
+ const result = {
1084
+ success: true,
1085
+ output,
1086
+ exitCode: sandboxResult.exitCode
1087
+ };
1088
+ return JSON.stringify(result);
1089
+ } catch (err) {
1090
+ const result = {
1091
+ success: false,
1092
+ output: "",
1093
+ error: err instanceof Error ? err.message : String(err)
1094
+ };
1095
+ return JSON.stringify(result);
1096
+ }
1097
+ }
1098
+ return new Promise((resolve4) => {
816
1099
  const stdoutChunks = [];
817
1100
  const stderrChunks = [];
818
1101
  let timedOut = false;
@@ -841,7 +1124,7 @@ async function runBash(args) {
841
1124
  if (settled) return;
842
1125
  settled = true;
843
1126
  clearTimeout(timer);
844
- resolve3(JSON.stringify(result));
1127
+ resolve4(JSON.stringify(result));
845
1128
  }
846
1129
  child.on("error", (err) => {
847
1130
  settle({
@@ -874,25 +1157,24 @@ ${stderr}` : stdout;
874
1157
  });
875
1158
  });
876
1159
  }
877
- var bashTool = createZodFunctionTool(
878
- "Bash",
879
- "Executes a given bash command and returns its output.\n\nThe working directory persists between commands, but shell state does not.\n\nIMPORTANT: Avoid using this tool to run `find`, `grep`, `cat`, `head`, `tail`, `sed`, `awk`, or `echo` commands. Instead, use the appropriate dedicated tool:\n - File search: Use Glob (NOT find or ls)\n - Content search: Use Grep (NOT grep or rg)\n - Read files: Use Read (NOT cat/head/tail)\n - Edit files: Use Edit (NOT sed/awk)\n\nFor simple commands, keep the description brief (5-10 words). For complex commands, include enough context to clarify what the command does.\n\nOutput is limited to 30,000 characters. Longer output will be middle-truncated.",
880
- BashSchema,
881
- async (params) => {
882
- return runBash(params);
883
- }
884
- );
885
-
886
- // src/builtins/read-tool.ts
887
- import { readFile, stat } from "fs/promises";
888
- import { z as z2 } from "zod";
1160
+ function createBashTool(options = {}) {
1161
+ return createZodFunctionTool(
1162
+ "Bash",
1163
+ "Executes a given bash command and returns its output.\n\nThe working directory persists between commands, but shell state does not.\n\nIMPORTANT: Avoid using this tool to run `find`, `grep`, `cat`, `head`, `tail`, `sed`, `awk`, or `echo` commands. Instead, use the appropriate dedicated tool:\n - File search: Use Glob (NOT find or ls)\n - Content search: Use Grep (NOT grep or rg)\n - Read files: Use Read (NOT cat/head/tail)\n - Edit files: Use Edit (NOT sed/awk)\n\nFor simple commands, keep the description brief (5-10 words). For complex commands, include enough context to clarify what the command does.\n\nOutput is limited to 30,000 characters. Longer output will be middle-truncated.",
1164
+ BashSchema,
1165
+ async (params) => {
1166
+ return runBash(params, options);
1167
+ }
1168
+ );
1169
+ }
1170
+ var bashTool = createBashTool();
889
1171
  var DEFAULT_LIMIT = 2e3;
890
- var ReadSchema = z2.object({
891
- filePath: z2.string().describe("The absolute path to the file to read"),
892
- offset: z2.number().optional().describe(
1172
+ var ReadSchema = z.object({
1173
+ filePath: z.string().describe("The absolute path to the file to read"),
1174
+ offset: z.number().optional().describe(
893
1175
  "The line number to start reading from (1-based). Only provide if the file is too large to read at once"
894
1176
  ),
895
- limit: z2.number().optional().describe(
1177
+ limit: z.number().optional().describe(
896
1178
  `The number of lines to read (default: ${DEFAULT_LIMIT}). Only provide if the file is too large to read at once`
897
1179
  )
898
1180
  });
@@ -911,82 +1193,93 @@ function formatWithLineNumbers(lines, startLine) {
911
1193
  return `${lineNum} ${line}`;
912
1194
  }).join("\n");
913
1195
  }
914
- async function readFileTool(args) {
1196
+ function formatReadResult(filePath, content, startLine, limit) {
1197
+ const allLines = content.split("\n");
1198
+ if (allLines[allLines.length - 1] === "") {
1199
+ allLines.pop();
1200
+ }
1201
+ const zeroBasedStart = startLine - 1;
1202
+ const selectedLines = allLines.slice(zeroBasedStart, zeroBasedStart + limit);
1203
+ const output = formatWithLineNumbers(selectedLines, startLine);
1204
+ const totalLines = allLines.length;
1205
+ const returnedLines = selectedLines.length;
1206
+ const header = returnedLines < totalLines ? `[File: ${filePath} (lines ${startLine}-${startLine + returnedLines - 1} of ${totalLines})]
1207
+ ` : `[File: ${filePath} (${totalLines} lines)]
1208
+ `;
1209
+ const result = {
1210
+ success: true,
1211
+ output: header + output
1212
+ };
1213
+ return JSON.stringify(result);
1214
+ }
1215
+ async function readFileTool(args, options = {}) {
915
1216
  const { filePath, offset, limit = DEFAULT_LIMIT } = args;
916
1217
  const startLine = offset !== void 0 && offset > 0 ? offset : 1;
1218
+ if (options.sandboxClient) {
1219
+ try {
1220
+ const content2 = await options.sandboxClient.readFile(filePath);
1221
+ return formatReadResult(filePath, content2, startLine, limit);
1222
+ } catch (err) {
1223
+ const result = {
1224
+ success: false,
1225
+ output: "",
1226
+ error: err instanceof Error ? err.message : String(err)
1227
+ };
1228
+ return JSON.stringify(result);
1229
+ }
1230
+ }
917
1231
  let fileStats;
918
1232
  try {
919
1233
  fileStats = await stat(filePath);
920
1234
  } catch (err) {
921
- const result2 = {
1235
+ const result = {
922
1236
  success: false,
923
1237
  output: "",
924
1238
  error: `File not found: ${filePath}`
925
1239
  };
926
- return JSON.stringify(result2);
1240
+ return JSON.stringify(result);
927
1241
  }
928
1242
  if (!fileStats.isFile()) {
929
- const result2 = {
1243
+ const result = {
930
1244
  success: false,
931
1245
  output: "",
932
1246
  error: `Path is not a file: ${filePath}`
933
1247
  };
934
- return JSON.stringify(result2);
1248
+ return JSON.stringify(result);
935
1249
  }
936
1250
  let buffer;
937
1251
  try {
938
1252
  buffer = await readFile(filePath);
939
1253
  } catch (err) {
940
- const result2 = {
1254
+ const result = {
941
1255
  success: false,
942
1256
  output: "",
943
1257
  error: err instanceof Error ? err.message : String(err)
944
1258
  };
945
- return JSON.stringify(result2);
1259
+ return JSON.stringify(result);
946
1260
  }
947
1261
  if (isBinary(buffer)) {
948
- const result2 = {
1262
+ const result = {
949
1263
  success: false,
950
1264
  output: "",
951
1265
  error: `Binary file not supported: ${filePath}`
952
1266
  };
953
- return JSON.stringify(result2);
1267
+ return JSON.stringify(result);
954
1268
  }
955
1269
  const content = buffer.toString("utf8");
956
- const allLines = content.split("\n");
957
- if (allLines[allLines.length - 1] === "") {
958
- allLines.pop();
959
- }
960
- const zeroBasedStart = startLine - 1;
961
- const selectedLines = allLines.slice(zeroBasedStart, zeroBasedStart + limit);
962
- const output = formatWithLineNumbers(selectedLines, startLine);
963
- const totalLines = allLines.length;
964
- const returnedLines = selectedLines.length;
965
- const header = returnedLines < totalLines ? `[File: ${filePath} (lines ${startLine}-${startLine + returnedLines - 1} of ${totalLines})]
966
- ` : `[File: ${filePath} (${totalLines} lines)]
967
- `;
968
- const result = {
969
- success: true,
970
- output: header + output
971
- };
972
- return JSON.stringify(result);
1270
+ return formatReadResult(filePath, content, startLine, limit);
973
1271
  }
974
- var readTool = createZodFunctionTool(
975
- "Read",
976
- "Reads a file from the local filesystem.\n\nBy default, reads up to 2000 lines from the beginning of the file. You can optionally specify offset and limit for partial reads.\n\nResults are returned using cat -n format, with line numbers starting at 1.\n\nThe file_path parameter must be an absolute path, not a relative path.",
977
- ReadSchema,
978
- async (params) => {
979
- return readFileTool(params);
980
- }
981
- );
982
-
983
- // src/builtins/write-tool.ts
984
- import { z as z3 } from "zod";
985
-
986
- // src/builtins/atomic-file-write.ts
987
- import { randomBytes } from "crypto";
988
- import { chmod, mkdir, rename, rm, stat as stat2, writeFile } from "fs/promises";
989
- import { basename, dirname, join } from "path";
1272
+ function createReadTool(options = {}) {
1273
+ return createZodFunctionTool(
1274
+ "Read",
1275
+ "Reads a file from the local filesystem.\n\nBy default, reads up to 2000 lines from the beginning of the file. You can optionally specify offset and limit for partial reads.\n\nResults are returned using cat -n format, with line numbers starting at 1.\n\nThe file_path parameter must be an absolute path, not a relative path.",
1276
+ ReadSchema,
1277
+ async (params) => {
1278
+ return readFileTool(params, options);
1279
+ }
1280
+ );
1281
+ }
1282
+ var readTool = createReadTool();
990
1283
  var TEMP_RANDOM_BYTES = 6;
991
1284
  var PRESERVED_MODE_BITS = 4095;
992
1285
  var MISSING_FILE_ERROR_CODE = "ENOENT";
@@ -998,7 +1291,7 @@ function createTempFilePath(filePath) {
998
1291
  }
999
1292
  async function readExistingMode(filePath) {
1000
1293
  try {
1001
- const fileStats = await stat2(filePath);
1294
+ const fileStats = await stat(filePath);
1002
1295
  return fileStats.mode & PRESERVED_MODE_BITS;
1003
1296
  } catch (error) {
1004
1297
  if (error instanceof Error && hasErrorCode(error, MISSING_FILE_ERROR_CODE)) return void 0;
@@ -1026,14 +1319,18 @@ async function atomicWriteUtf8File(filePath, content) {
1026
1319
  }
1027
1320
 
1028
1321
  // src/builtins/write-tool.ts
1029
- var WriteSchema = z3.object({
1030
- filePath: z3.string().describe("The absolute path to the file to write"),
1031
- content: z3.string().describe("The content to write to the file")
1322
+ var WriteSchema = z.object({
1323
+ filePath: z.string().describe("The absolute path to the file to write"),
1324
+ content: z.string().describe("The content to write to the file")
1032
1325
  });
1033
- async function writeFileTool(args) {
1326
+ async function writeFileTool(args, options = {}) {
1034
1327
  const { filePath, content } = args;
1035
1328
  try {
1036
- await atomicWriteUtf8File(filePath, content);
1329
+ if (options.sandboxClient) {
1330
+ await options.sandboxClient.writeFile(filePath, content);
1331
+ } else {
1332
+ await atomicWriteUtf8File(filePath, content);
1333
+ }
1037
1334
  const result = {
1038
1335
  success: true,
1039
1336
  output: `Written ${Buffer.byteLength(content, "utf8")} bytes to ${filePath}`
@@ -1048,31 +1345,30 @@ async function writeFileTool(args) {
1048
1345
  return JSON.stringify(result);
1049
1346
  }
1050
1347
  }
1051
- var writeTool = createZodFunctionTool(
1052
- "Write",
1053
- "Writes a file to the local filesystem. This will overwrite an existing file if one exists.\n\nALWAYS prefer the Edit tool for modifying existing files \u2014 it only sends the diff. Only use this tool to create new files or for complete rewrites.\n\nNEVER create documentation files (*.md) or README files unless explicitly requested by the user.",
1054
- WriteSchema,
1055
- async (params) => {
1056
- return writeFileTool(params);
1057
- }
1058
- );
1059
-
1060
- // src/builtins/edit-tool.ts
1061
- import { readFile as readFile2 } from "fs/promises";
1062
- import { z as z4 } from "zod";
1063
- var EditSchema = z4.object({
1064
- filePath: z4.string().describe("The absolute path to the file to modify"),
1065
- oldString: z4.string().describe("The text to replace (must be an exact match of existing content)"),
1066
- newString: z4.string().describe("The text to replace it with (must be different from old_string)"),
1067
- replaceAll: z4.boolean().optional().describe(
1348
+ function createWriteTool(options = {}) {
1349
+ return createZodFunctionTool(
1350
+ "Write",
1351
+ "Writes a file to the local filesystem. This will overwrite an existing file if one exists.\n\nALWAYS prefer the Edit tool for modifying existing files \u2014 it only sends the diff. Only use this tool to create new files or for complete rewrites.\n\nNEVER create documentation files (*.md) or README files unless explicitly requested by the user.",
1352
+ WriteSchema,
1353
+ async (params) => {
1354
+ return writeFileTool(params, options);
1355
+ }
1356
+ );
1357
+ }
1358
+ var writeTool = createWriteTool();
1359
+ var EditSchema = z.object({
1360
+ filePath: z.string().describe("The absolute path to the file to modify"),
1361
+ oldString: z.string().describe("The text to replace (must be an exact match of existing content)"),
1362
+ newString: z.string().describe("The text to replace it with (must be different from old_string)"),
1363
+ replaceAll: z.boolean().optional().describe(
1068
1364
  "Replace all occurrences of old_string (default: false). Useful for renaming variables"
1069
1365
  )
1070
1366
  });
1071
- async function editFileTool(args) {
1367
+ async function editFileTool(args, options = {}) {
1072
1368
  const { filePath, oldString, newString, replaceAll = false } = args;
1073
1369
  let content;
1074
1370
  try {
1075
- content = await readFile2(filePath, "utf8");
1371
+ content = options.sandboxClient ? await options.sandboxClient.readFile(filePath) : await readFile(filePath, "utf8");
1076
1372
  } catch (err) {
1077
1373
  const result2 = {
1078
1374
  success: false,
@@ -1104,7 +1400,11 @@ async function editFileTool(args) {
1104
1400
  }
1105
1401
  const updated = replaceAll ? content.split(oldString).join(newString) : content.slice(0, content.indexOf(oldString)) + newString + content.slice(content.indexOf(oldString) + oldString.length);
1106
1402
  try {
1107
- await atomicWriteUtf8File(filePath, updated);
1403
+ if (options.sandboxClient) {
1404
+ await options.sandboxClient.writeFile(filePath, updated);
1405
+ } else {
1406
+ await atomicWriteUtf8File(filePath, updated);
1407
+ }
1108
1408
  } catch (err) {
1109
1409
  const result2 = {
1110
1410
  success: false,
@@ -1123,27 +1423,24 @@ async function editFileTool(args) {
1123
1423
  };
1124
1424
  return JSON.stringify(result);
1125
1425
  }
1126
- var editTool = createZodFunctionTool(
1127
- "Edit",
1128
- "Performs exact string replacements in files.\n\nYou must use the Read tool at least once before editing. When editing text from Read output, preserve the exact indentation.\n\nThe edit will FAIL if old_string is not unique in the file. Either provide more surrounding context to make it unique, or use replace_all to change every instance.\n\nALWAYS prefer editing existing files over creating new ones.",
1129
- EditSchema,
1130
- async (params) => {
1131
- return editFileTool(params);
1132
- }
1133
- );
1134
-
1135
- // src/builtins/glob-tool.ts
1136
- import { stat as stat3 } from "fs/promises";
1137
- import { resolve } from "path";
1138
- import fg from "fast-glob";
1139
- import { z as z5 } from "zod";
1426
+ function createEditTool(options = {}) {
1427
+ return createZodFunctionTool(
1428
+ "Edit",
1429
+ "Performs exact string replacements in files.\n\nYou must use the Read tool at least once before editing. When editing text from Read output, preserve the exact indentation.\n\nThe edit will FAIL if old_string is not unique in the file. Either provide more surrounding context to make it unique, or use replace_all to change every instance.\n\nALWAYS prefer editing existing files over creating new ones.",
1430
+ EditSchema,
1431
+ async (params) => {
1432
+ return editFileTool(params, options);
1433
+ }
1434
+ );
1435
+ }
1436
+ var editTool = createEditTool();
1140
1437
  var DEFAULT_MAX_RESULTS = 1e3;
1141
- var GlobSchema = z5.object({
1142
- pattern: z5.string().describe('The glob pattern to match files against (e.g. "**/*.ts", "src/**/*.tsx")'),
1143
- path: z5.string().optional().describe(
1438
+ var GlobSchema = z.object({
1439
+ pattern: z.string().describe('The glob pattern to match files against (e.g. "**/*.ts", "src/**/*.tsx")'),
1440
+ path: z.string().optional().describe(
1144
1441
  "The directory to search in. Defaults to the current working directory. Must be a valid directory path if provided"
1145
1442
  ),
1146
- limit: z5.number().optional().describe(
1443
+ limit: z.number().optional().describe(
1147
1444
  "Maximum number of results to return (default: 1000). Use a smaller limit to save context space"
1148
1445
  )
1149
1446
  });
@@ -1170,7 +1467,7 @@ async function globFileTool(args) {
1170
1467
  matches.map(async (p) => {
1171
1468
  const absPath = resolve(cwd, p);
1172
1469
  try {
1173
- const s = await stat3(absPath);
1470
+ const s = await stat(absPath);
1174
1471
  return { path: p, mtime: s.mtimeMs };
1175
1472
  } catch {
1176
1473
  return { path: p, mtime: 0 };
@@ -1203,21 +1500,16 @@ var globTool = createZodFunctionTool(
1203
1500
  return globFileTool(params);
1204
1501
  }
1205
1502
  );
1206
-
1207
- // src/builtins/grep-tool.ts
1208
- import { readFile as readFile3, readdir, stat as stat4 } from "fs/promises";
1209
- import { join as join2, resolve as resolve2 } from "path";
1210
- import { z as z6 } from "zod";
1211
- var GrepSchema = z6.object({
1212
- pattern: z6.string().describe("The regular expression pattern to search for in file contents"),
1213
- path: z6.string().optional().describe("File or directory to search in. Defaults to the current working directory"),
1214
- glob: z6.string().optional().describe(
1503
+ var GrepSchema = z.object({
1504
+ pattern: z.string().describe("The regular expression pattern to search for in file contents"),
1505
+ path: z.string().optional().describe("File or directory to search in. Defaults to the current working directory"),
1506
+ glob: z.string().optional().describe(
1215
1507
  'Glob pattern to filter files (e.g. "*.ts", "*.{ts,tsx}"). Only files matching this pattern will be searched'
1216
1508
  ),
1217
- contextLines: z6.number().optional().describe(
1509
+ contextLines: z.number().optional().describe(
1218
1510
  'Number of context lines to show before and after each match. Only applies when outputMode is "content". Default: 0'
1219
1511
  ),
1220
- outputMode: z6.enum(["files_with_matches", "content"]).optional().describe(
1512
+ outputMode: z.enum(["files_with_matches", "content"]).optional().describe(
1221
1513
  'Output mode: "files_with_matches" shows only file paths (default), "content" shows matching lines with context'
1222
1514
  )
1223
1515
  });
@@ -1240,10 +1532,10 @@ async function collectFiles(dirPath, glob) {
1240
1532
  }
1241
1533
  for (const name of entryNames) {
1242
1534
  if (name === "node_modules" || name === ".git") continue;
1243
- const fullPath = join2(current, name);
1535
+ const fullPath = join(current, name);
1244
1536
  let fileStat;
1245
1537
  try {
1246
- fileStat = await stat4(fullPath);
1538
+ fileStat = await stat(fullPath);
1247
1539
  } catch {
1248
1540
  continue;
1249
1541
  }
@@ -1299,7 +1591,7 @@ async function grepFileTool(args) {
1299
1591
  contextLines = 0,
1300
1592
  outputMode = "files_with_matches"
1301
1593
  } = args;
1302
- const targetPath = searchPath ? resolve2(searchPath) : process.cwd();
1594
+ const targetPath = searchPath ? resolve(searchPath) : process.cwd();
1303
1595
  let regex;
1304
1596
  try {
1305
1597
  regex = new RegExp(pattern);
@@ -1313,7 +1605,7 @@ async function grepFileTool(args) {
1313
1605
  }
1314
1606
  let targetStat;
1315
1607
  try {
1316
- targetStat = await stat4(targetPath);
1608
+ targetStat = await stat(targetPath);
1317
1609
  } catch {
1318
1610
  const result2 = {
1319
1611
  success: false,
@@ -1332,7 +1624,7 @@ async function grepFileTool(args) {
1332
1624
  for (const filePath of files) {
1333
1625
  let content;
1334
1626
  try {
1335
- const buffer = await readFile3(filePath);
1627
+ const buffer = await readFile(filePath);
1336
1628
  const checkLen = Math.min(buffer.length, 8192);
1337
1629
  let hasBinary = false;
1338
1630
  for (let i = 0; i < checkLen; i++) {
@@ -1363,14 +1655,11 @@ var grepTool = createZodFunctionTool(
1363
1655
  return grepFileTool(params);
1364
1656
  }
1365
1657
  );
1366
-
1367
- // src/builtins/web-fetch-tool.ts
1368
- import { z as z7 } from "zod";
1369
1658
  var DEFAULT_TIMEOUT_MS2 = 3e4;
1370
1659
  var MAX_RESPONSE_BYTES = 5e6;
1371
- var WebFetchSchema = z7.object({
1372
- url: z7.string().describe("The URL to fetch"),
1373
- headers: z7.record(z7.string()).optional().describe("Optional HTTP headers as key-value pairs")
1660
+ var WebFetchSchema = z.object({
1661
+ url: z.string().describe("The URL to fetch"),
1662
+ headers: z.record(z.string()).optional().describe("Optional HTTP headers as key-value pairs")
1374
1663
  });
1375
1664
  function htmlToText(html) {
1376
1665
  return html.replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&nbsp;/g, " ").replace(/\s+/g, " ").trim();
@@ -1431,14 +1720,11 @@ var webFetchTool = createZodFunctionTool(
1431
1720
  WebFetchSchema,
1432
1721
  async (params) => runWebFetch(params)
1433
1722
  );
1434
-
1435
- // src/builtins/web-search-tool.ts
1436
- import { z as z8 } from "zod";
1437
1723
  var DEFAULT_LIMIT2 = 10;
1438
1724
  var DEFAULT_TIMEOUT_MS3 = 15e3;
1439
- var WebSearchSchema = z8.object({
1440
- query: z8.string().describe("The search query"),
1441
- limit: z8.number().optional().describe(`Maximum number of results to return (default: ${DEFAULT_LIMIT2})`)
1725
+ var WebSearchSchema = z.object({
1726
+ query: z.string().describe("The search query"),
1727
+ limit: z.number().optional().describe(`Maximum number of results to return (default: ${DEFAULT_LIMIT2})`)
1442
1728
  });
1443
1729
  async function runWebSearch(args) {
1444
1730
  const { query, limit = DEFAULT_LIMIT2 } = args;
@@ -1495,20 +1781,5 @@ var webSearchTool = createZodFunctionTool(
1495
1781
  WebSearchSchema,
1496
1782
  async (params) => runWebSearch(params)
1497
1783
  );
1498
- export {
1499
- FunctionTool,
1500
- OpenAPITool,
1501
- ToolRegistry,
1502
- bashTool,
1503
- createFunctionTool,
1504
- createOpenAPITool,
1505
- createZodFunctionTool,
1506
- editTool,
1507
- globTool,
1508
- grepTool,
1509
- readTool,
1510
- webFetchTool,
1511
- webSearchTool,
1512
- writeTool,
1513
- zodToJsonSchema
1514
- };
1784
+
1785
+ export { E2BSandboxClient, FunctionTool, InMemorySandboxClient, OpenAPITool, ToolRegistry, applyWorkspaceManifest, bashTool, createBashTool, createEditTool, createFunctionTool, createOpenAPITool, createReadTool, createWriteTool, createZodFunctionTool, editTool, globTool, grepTool, readTool, validateWorkspaceManifestPath, webFetchTool, webSearchTool, writeTool, zodToJsonSchema };