@robota-sdk/agent-tools 3.0.0-beta.6 → 3.0.0-beta.61

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,3 +1,272 @@
1
+ // src/sandbox/e2b-sandbox-client.ts
2
+ var E2BSandboxClient = class {
3
+ sandbox;
4
+ connectSandbox;
5
+ createSandboxFromSnapshot;
6
+ constructor(options) {
7
+ this.sandbox = options.sandbox;
8
+ this.connectSandbox = options.connectSandbox;
9
+ this.createSandboxFromSnapshot = options.createSandboxFromSnapshot;
10
+ }
11
+ async run(command, options) {
12
+ const result = await this.sandbox.commands.run(command, {
13
+ background: false,
14
+ timeoutMs: options?.timeoutMs,
15
+ cwd: options?.workingDirectory
16
+ });
17
+ return {
18
+ stdout: result.stdout ?? "",
19
+ stderr: result.stderr ?? "",
20
+ exitCode: result.exitCode ?? result.exit_code ?? 0
21
+ };
22
+ }
23
+ async readFile(path) {
24
+ const content = await this.sandbox.files.read(path);
25
+ return typeof content === "string" ? content : Buffer.from(content).toString("utf8");
26
+ }
27
+ async writeFile(path, content) {
28
+ await this.sandbox.files.write(path, content);
29
+ }
30
+ async snapshot() {
31
+ if (this.sandbox.createSnapshot) {
32
+ const snapshot = await this.sandbox.createSnapshot();
33
+ const snapshotId = snapshot.snapshotId ?? snapshot.id;
34
+ if (!snapshotId) {
35
+ throw new Error("E2B createSnapshot() did not return a snapshot id.");
36
+ }
37
+ return snapshotId;
38
+ }
39
+ const sandboxId = this.sandbox.sandboxId;
40
+ if (!sandboxId) {
41
+ throw new Error("E2B sandboxId is required to create a resumable sandbox snapshot.");
42
+ }
43
+ if (!this.sandbox.pause) {
44
+ throw new Error("E2B sandbox adapter does not expose pause().");
45
+ }
46
+ await this.sandbox.pause();
47
+ return sandboxId;
48
+ }
49
+ async restore(snapshotId) {
50
+ if (this.createSandboxFromSnapshot) {
51
+ this.sandbox = await this.createSandboxFromSnapshot(snapshotId);
52
+ return;
53
+ }
54
+ if (this.connectSandbox) {
55
+ this.sandbox = await this.connectSandbox(snapshotId);
56
+ return;
57
+ }
58
+ if (this.sandbox.sandboxId === snapshotId && this.sandbox.connect) {
59
+ this.sandbox = await this.sandbox.connect();
60
+ return;
61
+ }
62
+ throw new Error(
63
+ "E2B sandbox restore requires connectSandbox(snapshotId) or sandbox.connect()."
64
+ );
65
+ }
66
+ };
67
+
68
+ // src/sandbox/in-memory-sandbox-client.ts
69
+ var InMemorySandboxClient = class {
70
+ files = /* @__PURE__ */ new Map();
71
+ snapshots = /* @__PURE__ */ new Map();
72
+ runHandler;
73
+ snapshotSequence = 0;
74
+ constructor(options = {}) {
75
+ for (const [path, content] of Object.entries(options.files ?? {})) {
76
+ this.files.set(path, content);
77
+ }
78
+ this.runHandler = options.runHandler;
79
+ }
80
+ async run(command, options) {
81
+ if (this.runHandler) {
82
+ return this.runHandler(command, options, this.files);
83
+ }
84
+ return { stdout: "", stderr: "", exitCode: 0 };
85
+ }
86
+ async readFile(path) {
87
+ const content = this.files.get(path);
88
+ if (content === void 0) {
89
+ throw new Error(`Sandbox file not found: ${path}`);
90
+ }
91
+ return content;
92
+ }
93
+ async writeFile(path, content) {
94
+ this.files.set(path, content);
95
+ }
96
+ async snapshot() {
97
+ const snapshotId = `snapshot-${++this.snapshotSequence}`;
98
+ this.snapshots.set(snapshotId, new Map(this.files));
99
+ return snapshotId;
100
+ }
101
+ async restore(snapshotId) {
102
+ const snapshot = this.snapshots.get(snapshotId);
103
+ if (!snapshot) {
104
+ throw new Error(`Sandbox snapshot not found: ${snapshotId}`);
105
+ }
106
+ this.files.clear();
107
+ for (const [path, content] of snapshot.entries()) {
108
+ this.files.set(path, content);
109
+ }
110
+ }
111
+ getFile(path) {
112
+ return this.files.get(path);
113
+ }
114
+ };
115
+
116
+ // src/sandbox/workspace-manifest.ts
117
+ import { readdir, readFile } from "fs/promises";
118
+ import { isAbsolute, join, posix, resolve } from "path";
119
+ var DEFAULT_TARGET_ROOT = "/workspace";
120
+ var WINDOWS_ABSOLUTE_PATH_PATTERN = /^[A-Za-z]:[\\/]/;
121
+ var SHELL_QUOTE_PATTERN = /'/g;
122
+ async function applyWorkspaceManifest(sandboxClient, manifest, options = {}) {
123
+ if (sandboxClient.applyManifest) {
124
+ return sandboxClient.applyManifest(manifest, options);
125
+ }
126
+ const targetRoot = normalizeSandboxRoot(options.targetRoot ?? DEFAULT_TARGET_ROOT);
127
+ const appliedEntries = [];
128
+ for (const [rawPath, entry] of Object.entries(manifest.entries)) {
129
+ const path = validateWorkspaceManifestPath(rawPath);
130
+ const targetPath = joinSandboxPath(targetRoot, path);
131
+ appliedEntries.push(
132
+ await applyManifestEntry(sandboxClient, path, targetPath, targetRoot, entry, options)
133
+ );
134
+ }
135
+ return { entries: appliedEntries };
136
+ }
137
+ function validateWorkspaceManifestPath(path) {
138
+ if (path.length === 0) {
139
+ throw new Error("workspace manifest path must not be empty");
140
+ }
141
+ if (path.includes("\0")) {
142
+ throw new Error("workspace manifest path must not contain NUL bytes");
143
+ }
144
+ if (path.startsWith("/") || path.startsWith("\\") || WINDOWS_ABSOLUTE_PATH_PATTERN.test(path)) {
145
+ throw new Error("workspace manifest path must be workspace-relative");
146
+ }
147
+ const parts = path.replace(/\\/g, "/").split("/").filter(Boolean);
148
+ if (parts.length === 0) {
149
+ throw new Error("workspace manifest path must not resolve to the workspace root");
150
+ }
151
+ if (parts.some((part) => part === "..")) {
152
+ throw new Error("workspace manifest path cannot contain traversal segments");
153
+ }
154
+ const normalizedParts = parts.filter((part) => part !== ".");
155
+ if (normalizedParts.length === 0) {
156
+ throw new Error("workspace manifest path must not resolve to the workspace root");
157
+ }
158
+ return normalizedParts.join("/");
159
+ }
160
+ async function applyManifestEntry(sandboxClient, path, targetPath, targetRoot, entry, options) {
161
+ switch (entry.type) {
162
+ case "file":
163
+ await writeSandboxFile(sandboxClient, targetPath, targetRoot, entry.content);
164
+ return createAppliedEntry(path, entry.type);
165
+ case "dir":
166
+ await createSandboxDirectory(sandboxClient, targetPath);
167
+ return createAppliedEntry(path, entry.type);
168
+ case "localFile":
169
+ await copyLocalFile(sandboxClient, entry.src, targetPath, targetRoot, options);
170
+ return createAppliedEntry(path, entry.type);
171
+ case "localDir":
172
+ await copyLocalDirectory(sandboxClient, entry.src, targetPath, options);
173
+ return createAppliedEntry(path, entry.type);
174
+ case "gitRepo":
175
+ await cloneGitRepository(sandboxClient, entry, targetPath);
176
+ return createAppliedEntry(path, entry.type);
177
+ case "s3Mount":
178
+ case "gcsMount":
179
+ case "r2Mount":
180
+ case "azureBlobMount":
181
+ return {
182
+ path,
183
+ type: entry.type,
184
+ status: "unsupported",
185
+ message: `${entry.type} requires a provider-specific sandbox adapter.`
186
+ };
187
+ default:
188
+ return assertUnreachable(entry);
189
+ }
190
+ }
191
+ function createAppliedEntry(path, type) {
192
+ return { path, type, status: "applied" };
193
+ }
194
+ async function copyLocalFile(sandboxClient, source, targetPath, targetRoot, options) {
195
+ const hostSourcePath = resolveHostSourcePath(source, options.hostRoot);
196
+ const content = await readFile(hostSourcePath, "utf8");
197
+ await writeSandboxFile(sandboxClient, targetPath, targetRoot, content);
198
+ }
199
+ async function copyLocalDirectory(sandboxClient, source, targetPath, options) {
200
+ const hostSourcePath = resolveHostSourcePath(source, options.hostRoot);
201
+ await copyLocalDirectoryRecursive(sandboxClient, hostSourcePath, targetPath);
202
+ }
203
+ async function copyLocalDirectoryRecursive(sandboxClient, sourcePath, targetPath) {
204
+ await createSandboxDirectory(sandboxClient, targetPath);
205
+ const entries = await readdir(sourcePath, { withFileTypes: true });
206
+ for (const entry of entries) {
207
+ const childSourcePath = join(sourcePath, entry.name);
208
+ const childTargetPath = joinSandboxPath(targetPath, entry.name);
209
+ if (entry.isDirectory()) {
210
+ await copyLocalDirectoryRecursive(sandboxClient, childSourcePath, childTargetPath);
211
+ continue;
212
+ }
213
+ if (entry.isFile()) {
214
+ const content = await readFile(childSourcePath, "utf8");
215
+ await sandboxClient.writeFile(childTargetPath, content);
216
+ }
217
+ }
218
+ }
219
+ async function cloneGitRepository(sandboxClient, entry, targetPath) {
220
+ const shallowArgs = entry.shallow === false ? "" : " --depth 1";
221
+ const refArgs = entry.ref ? ` --branch ${quoteShellArg(entry.ref)}` : "";
222
+ await runSandboxCommand(
223
+ sandboxClient,
224
+ `git clone${shallowArgs}${refArgs} ${quoteShellArg(entry.url)} ${quoteShellArg(targetPath)}`
225
+ );
226
+ }
227
+ async function writeSandboxFile(sandboxClient, targetPath, targetRoot, content) {
228
+ const parentPath = posix.dirname(targetPath);
229
+ if (parentPath !== targetRoot) {
230
+ await createSandboxDirectory(sandboxClient, parentPath);
231
+ }
232
+ await sandboxClient.writeFile(targetPath, content);
233
+ }
234
+ async function createSandboxDirectory(sandboxClient, targetPath) {
235
+ await runSandboxCommand(sandboxClient, `mkdir -p ${quoteShellArg(targetPath)}`);
236
+ }
237
+ async function runSandboxCommand(sandboxClient, command) {
238
+ const result = await sandboxClient.run(command);
239
+ if (result.exitCode !== 0) {
240
+ throw new Error(
241
+ `workspace manifest command failed: ${command}
242
+ ${result.stderr ?? result.stdout}`
243
+ );
244
+ }
245
+ }
246
+ function resolveHostSourcePath(source, hostRoot) {
247
+ return isAbsolute(source) ? resolve(source) : resolve(hostRoot ?? process.cwd(), source);
248
+ }
249
+ function normalizeSandboxRoot(root) {
250
+ const normalized = root.replace(/\\/g, "/").replace(/\/+$/, "");
251
+ if (!normalized.startsWith("/")) {
252
+ throw new Error("workspace manifest targetRoot must be an absolute sandbox path");
253
+ }
254
+ return normalized.length === 0 ? "/" : normalized;
255
+ }
256
+ function joinSandboxPath(root, path) {
257
+ const normalizedRoot = normalizeSandboxRoot(root);
258
+ if (normalizedRoot === "/") {
259
+ return `/${path}`;
260
+ }
261
+ return `${normalizedRoot}/${path}`;
262
+ }
263
+ function quoteShellArg(value) {
264
+ return `'${value.replace(SHELL_QUOTE_PATTERN, "'\\''")}'`;
265
+ }
266
+ function assertUnreachable(value) {
267
+ throw new Error(`unsupported workspace manifest entry: ${JSON.stringify(value)}`);
268
+ }
269
+
1
270
  // src/registry/tool-registry.ts
2
271
  import { ValidationError } from "@robota-sdk/agent-core";
3
272
  import { logger } from "@robota-sdk/agent-core";
@@ -165,7 +434,9 @@ function zodToJsonSchema(schema, options = {}) {
165
434
  type: "object",
166
435
  properties,
167
436
  required,
168
- ...options.allowAdditionalProperties && { additionalProperties: true }
437
+ ...(options.allowAdditionalProperties || schemaDef.unknownKeys === "passthrough") && {
438
+ additionalProperties: true
439
+ }
169
440
  };
170
441
  }
171
442
  function convertZodTypeToProperty(typeObj) {
@@ -244,6 +515,100 @@ function isRequiredField(typeObj) {
244
515
  return typeDef.typeName !== "ZodOptional" && typeDef.typeName !== "ZodNullable" && typeDef.typeName !== "ZodDefault";
245
516
  }
246
517
 
518
+ // src/implementations/function-tool/parameter-validator.ts
519
+ function validateParameterType(key, value, schema) {
520
+ const expectedType = schema["type"];
521
+ switch (expectedType) {
522
+ case "string":
523
+ if (typeof value !== "string") {
524
+ return `Parameter "${key}" must be a string, got ${typeof value}`;
525
+ }
526
+ break;
527
+ case "number":
528
+ if (typeof value !== "number" || isNaN(value)) {
529
+ return `Parameter "${key}" must be a number, got ${typeof value}`;
530
+ }
531
+ break;
532
+ case "boolean":
533
+ if (typeof value !== "boolean") {
534
+ return `Parameter "${key}" must be a boolean, got ${typeof value}`;
535
+ }
536
+ break;
537
+ case "array":
538
+ if (!Array.isArray(value)) {
539
+ return `Parameter "${key}" must be an array, got ${typeof value}`;
540
+ }
541
+ if (schema.items) {
542
+ for (let i = 0; i < value.length; i++) {
543
+ const itemError = validateParameterType(`${key}[${i}]`, value[i], schema.items);
544
+ if (itemError) {
545
+ return itemError;
546
+ }
547
+ }
548
+ }
549
+ break;
550
+ case "object":
551
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
552
+ return `Parameter "${key}" must be an object, got ${typeof value}`;
553
+ }
554
+ break;
555
+ }
556
+ if (schema.enum && schema.enum.length > 0) {
557
+ const enumValues = schema.enum;
558
+ let isValidEnum = false;
559
+ for (const enumValue of enumValues) {
560
+ if (value === enumValue) {
561
+ isValidEnum = true;
562
+ break;
563
+ }
564
+ }
565
+ if (!isValidEnum) {
566
+ return `Parameter "${key}" must be one of: ${enumValues.join(", ")}, got ${value}`;
567
+ }
568
+ }
569
+ return void 0;
570
+ }
571
+ function getValidationErrors(parameters, schemaRequired, schemaProperties, additionalProperties) {
572
+ const errors = [];
573
+ for (const field of schemaRequired) {
574
+ if (!(field in parameters)) {
575
+ errors.push(`Missing required parameter: ${field}`);
576
+ }
577
+ }
578
+ for (const [key, value] of Object.entries(parameters)) {
579
+ const paramSchema = schemaProperties[key];
580
+ if (!paramSchema) {
581
+ if (additionalProperties === true) {
582
+ continue;
583
+ }
584
+ if (additionalProperties && typeof additionalProperties === "object") {
585
+ const additionalTypeError = validateParameterType(key, value, additionalProperties);
586
+ if (additionalTypeError) errors.push(additionalTypeError);
587
+ continue;
588
+ }
589
+ errors.push(`Unknown parameter: ${key}`);
590
+ continue;
591
+ }
592
+ const typeError = validateParameterType(key, value, paramSchema);
593
+ if (typeError) {
594
+ errors.push(typeError);
595
+ }
596
+ }
597
+ return errors;
598
+ }
599
+ function validateToolParameters(parameters, schemaRequired, schemaProperties, additionalProperties) {
600
+ const errors = getValidationErrors(
601
+ parameters,
602
+ schemaRequired,
603
+ schemaProperties,
604
+ additionalProperties
605
+ );
606
+ return {
607
+ isValid: errors.length === 0,
608
+ errors
609
+ };
610
+ }
611
+
247
612
  // src/implementations/function-tool.ts
248
613
  var FunctionTool = class {
249
614
  schema;
@@ -274,7 +639,12 @@ var FunctionTool = class {
274
639
  async execute(parameters, context) {
275
640
  const toolName = this.schema.name;
276
641
  if (!this.validate(parameters)) {
277
- const errors = this.getValidationErrors(parameters);
642
+ const errors = getValidationErrors(
643
+ parameters,
644
+ this.schema.parameters.required || [],
645
+ this.schema.parameters.properties || {},
646
+ this.schema.parameters.additionalProperties
647
+ );
278
648
  throw new ValidationError2(`Invalid parameters for tool "${toolName}": ${errors.join(", ")}`);
279
649
  }
280
650
  const startTime = Date.now();
@@ -310,17 +680,23 @@ var FunctionTool = class {
310
680
  * Validate parameters (simple boolean result)
311
681
  */
312
682
  validate(parameters) {
313
- return this.getValidationErrors(parameters).length === 0;
683
+ return getValidationErrors(
684
+ parameters,
685
+ this.schema.parameters.required || [],
686
+ this.schema.parameters.properties || {},
687
+ this.schema.parameters.additionalProperties
688
+ ).length === 0;
314
689
  }
315
690
  /**
316
691
  * Validate tool parameters with detailed result
317
692
  */
318
693
  validateParameters(parameters) {
319
- const errors = this.getValidationErrors(parameters);
320
- return {
321
- isValid: errors.length === 0,
322
- errors
323
- };
694
+ return validateToolParameters(
695
+ parameters,
696
+ this.schema.parameters.required || [],
697
+ this.schema.parameters.properties || {},
698
+ this.schema.parameters.additionalProperties
699
+ );
324
700
  }
325
701
  /**
326
702
  * Get tool description
@@ -328,86 +704,6 @@ var FunctionTool = class {
328
704
  getDescription() {
329
705
  return this.schema.description;
330
706
  }
331
- /**
332
- * Get detailed validation errors
333
- */
334
- getValidationErrors(parameters) {
335
- const errors = [];
336
- const required = this.schema.parameters.required || [];
337
- const properties = this.schema.parameters.properties || {};
338
- for (const field of required) {
339
- if (!(field in parameters)) {
340
- errors.push(`Missing required parameter: ${field}`);
341
- }
342
- }
343
- for (const [key, value] of Object.entries(parameters)) {
344
- const paramSchema = properties[key];
345
- if (!paramSchema) {
346
- errors.push(`Unknown parameter: ${key}`);
347
- continue;
348
- }
349
- const typeError = this.validateParameterType(key, value, paramSchema);
350
- if (typeError) {
351
- errors.push(typeError);
352
- }
353
- }
354
- return errors;
355
- }
356
- /**
357
- * Validate individual parameter type
358
- */
359
- validateParameterType(key, value, schema) {
360
- const expectedType = schema["type"];
361
- switch (expectedType) {
362
- case "string":
363
- if (typeof value !== "string") {
364
- return `Parameter "${key}" must be a string, got ${typeof value}`;
365
- }
366
- break;
367
- case "number":
368
- if (typeof value !== "number" || isNaN(value)) {
369
- return `Parameter "${key}" must be a number, got ${typeof value}`;
370
- }
371
- break;
372
- case "boolean":
373
- if (typeof value !== "boolean") {
374
- return `Parameter "${key}" must be a boolean, got ${typeof value}`;
375
- }
376
- break;
377
- case "array":
378
- if (!Array.isArray(value)) {
379
- return `Parameter "${key}" must be an array, got ${typeof value}`;
380
- }
381
- if (schema.items) {
382
- for (let i = 0; i < value.length; i++) {
383
- const itemError = this.validateParameterType(`${key}[${i}]`, value[i], schema.items);
384
- if (itemError) {
385
- return itemError;
386
- }
387
- }
388
- }
389
- break;
390
- case "object":
391
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
392
- return `Parameter "${key}" must be an object, got ${typeof value}`;
393
- }
394
- break;
395
- }
396
- if (schema.enum && schema.enum.length > 0) {
397
- const enumValues = schema.enum;
398
- let isValidEnum = false;
399
- for (const enumValue of enumValues) {
400
- if (value === enumValue) {
401
- isValidEnum = true;
402
- break;
403
- }
404
- }
405
- if (!isValidEnum) {
406
- return `Parameter "${key}" must be one of: ${enumValues.join(", ")}, got ${value}`;
407
- }
408
- }
409
- return void 0;
410
- }
411
707
  /**
412
708
  * Validate constructor inputs
413
709
  */
@@ -451,6 +747,132 @@ function createZodFunctionTool(name, description, zodSchema, fn) {
451
747
 
452
748
  // src/implementations/openapi-tool.ts
453
749
  import { ToolExecutionError as ToolExecutionError2, ValidationError as ValidationError3 } from "@robota-sdk/agent-core";
750
+
751
+ // src/implementations/openapi-schema-converter.ts
752
+ var HTTP_METHODS = [
753
+ "get",
754
+ "post",
755
+ "put",
756
+ "delete",
757
+ "patch",
758
+ "head",
759
+ "options"
760
+ ];
761
+ function findOperation(apiSpec, operationId) {
762
+ for (const [path, pathItem] of Object.entries(apiSpec.paths || {})) {
763
+ if (!pathItem) continue;
764
+ for (const method of HTTP_METHODS) {
765
+ const operation = pathItem[method];
766
+ if (operation?.operationId === operationId) {
767
+ return { method, path, operation };
768
+ }
769
+ }
770
+ }
771
+ return void 0;
772
+ }
773
+ function mapOpenAPIType(type) {
774
+ switch (type) {
775
+ case "string":
776
+ return "string";
777
+ case "number":
778
+ return "number";
779
+ case "integer":
780
+ return "integer";
781
+ case "boolean":
782
+ return "boolean";
783
+ case "array":
784
+ return "array";
785
+ case "object":
786
+ return "object";
787
+ default:
788
+ return "string";
789
+ }
790
+ }
791
+ function convertOpenAPISchemaToParameterSchema(schema) {
792
+ if ("$ref" in schema) {
793
+ return { type: "object" };
794
+ }
795
+ const result = {
796
+ type: mapOpenAPIType(schema.type)
797
+ };
798
+ if (schema.description) {
799
+ result.description = schema.description;
800
+ }
801
+ if (schema.enum) {
802
+ result.enum = schema.enum;
803
+ }
804
+ if (schema.minimum !== void 0) {
805
+ result.minimum = schema.minimum;
806
+ }
807
+ if (schema.maximum !== void 0) {
808
+ result.maximum = schema.maximum;
809
+ }
810
+ if (schema.pattern) {
811
+ result.pattern = schema.pattern;
812
+ }
813
+ if (schema.format) {
814
+ result.format = schema.format;
815
+ }
816
+ if (schema.default !== void 0) {
817
+ result.default = schema.default;
818
+ }
819
+ if (schema.type === "array" && schema.items) {
820
+ result.items = convertOpenAPISchemaToParameterSchema(schema.items);
821
+ }
822
+ if (schema.type === "object" && schema.properties) {
823
+ result.properties = {};
824
+ for (const [propName, propSchema] of Object.entries(schema.properties)) {
825
+ result.properties[propName] = convertOpenAPISchemaToParameterSchema(propSchema);
826
+ }
827
+ if (schema.required && schema.required.length > 0) {
828
+ result.required = schema.required;
829
+ }
830
+ }
831
+ return result;
832
+ }
833
+ function convertOpenAPIParamToSchema(param) {
834
+ const schema = param.schema;
835
+ return convertOpenAPISchemaToParameterSchema(schema);
836
+ }
837
+ function createSchemaFromOperation(operationId, opSpec) {
838
+ const properties = {};
839
+ const required = [];
840
+ const params = opSpec.parameters || [];
841
+ for (const param of params) {
842
+ properties[param.name] = convertOpenAPIParamToSchema(param);
843
+ if (param.required) {
844
+ required.push(param.name);
845
+ }
846
+ }
847
+ if (opSpec.requestBody) {
848
+ const requestBody = opSpec.requestBody;
849
+ const jsonContent = requestBody.content?.["application/json"];
850
+ if (jsonContent?.schema) {
851
+ const bodySchema = convertOpenAPISchemaToParameterSchema(jsonContent.schema);
852
+ if (bodySchema.type === "object" && bodySchema.properties) {
853
+ Object.assign(properties, bodySchema.properties);
854
+ const schemaWithRequired = bodySchema;
855
+ if (schemaWithRequired.required) {
856
+ required.push(...schemaWithRequired.required);
857
+ }
858
+ }
859
+ }
860
+ }
861
+ const schemaParams = {
862
+ type: "object",
863
+ properties
864
+ };
865
+ if (required.length > 0) {
866
+ schemaParams.required = required;
867
+ }
868
+ return {
869
+ name: operationId,
870
+ description: opSpec.summary || opSpec.description || `OpenAPI operation: ${operationId}`,
871
+ parameters: schemaParams
872
+ };
873
+ }
874
+
875
+ // src/implementations/openapi-tool.ts
454
876
  var OpenAPITool = class {
455
877
  schema;
456
878
  apiSpec;
@@ -557,36 +979,13 @@ var OpenAPITool = class {
557
979
  * @private
558
980
  */
559
981
  async executeAPICall(parameters, _context) {
560
- const operation = this.findOperation();
982
+ const operation = findOperation(this.apiSpec, this.operationId);
561
983
  if (!operation) {
562
984
  throw new Error(`Operation ${this.operationId} not found in OpenAPI spec`);
563
985
  }
564
- const requestConfig = this.buildRequestConfig(operation, parameters);
986
+ this.buildRequestConfig(operation, parameters);
565
987
  throw new Error("Not implemented: actual API execution is not yet available");
566
988
  }
567
- /**
568
- * Find the operation in the OpenAPI specification
569
- */
570
- findOperation() {
571
- for (const [path, pathItem] of Object.entries(this.apiSpec.paths || {})) {
572
- if (!pathItem) continue;
573
- for (const method of [
574
- "get",
575
- "post",
576
- "put",
577
- "delete",
578
- "patch",
579
- "head",
580
- "options"
581
- ]) {
582
- const operation = pathItem[method];
583
- if (operation?.operationId === this.operationId) {
584
- return { method, path, operation };
585
- }
586
- }
587
- }
588
- return void 0;
589
- }
590
989
  /**
591
990
  * Build HTTP request configuration from OpenAPI operation and parameters
592
991
  */
@@ -658,121 +1057,13 @@ var OpenAPITool = class {
658
1057
  * Create tool schema from OpenAPI operation specification
659
1058
  */
660
1059
  createSchemaFromOpenAPI() {
661
- const operation = this.findOperation();
1060
+ const operation = findOperation(this.apiSpec, this.operationId);
662
1061
  if (!operation) {
663
1062
  throw new Error(
664
1063
  `[STRICT-POLICY][EMITTER-CONTRACT] OpenAPI operation not found: ${this.operationId}. Emitter contract must provide a valid operationId present in the OpenAPI document.`
665
1064
  );
666
1065
  }
667
- const { operation: opSpec } = operation;
668
- const properties = {};
669
- const required = [];
670
- const params = opSpec.parameters || [];
671
- for (const param of params) {
672
- properties[param.name] = this.convertOpenAPIParamToSchema(param);
673
- if (param.required) {
674
- required.push(param.name);
675
- }
676
- }
677
- if (opSpec.requestBody) {
678
- const requestBody = opSpec.requestBody;
679
- const jsonContent = requestBody.content?.["application/json"];
680
- if (jsonContent?.schema) {
681
- const bodySchema = this.convertOpenAPISchemaToParameterSchema(jsonContent.schema);
682
- if (bodySchema.type === "object" && bodySchema.properties) {
683
- Object.assign(properties, bodySchema.properties);
684
- const schemaWithRequired = bodySchema;
685
- if (schemaWithRequired.required) {
686
- required.push(...schemaWithRequired.required);
687
- }
688
- }
689
- }
690
- }
691
- const schemaParams = {
692
- type: "object",
693
- properties
694
- };
695
- if (required.length > 0) {
696
- schemaParams.required = required;
697
- }
698
- return {
699
- name: this.operationId,
700
- description: opSpec.summary || opSpec.description || `OpenAPI operation: ${this.operationId}`,
701
- parameters: schemaParams
702
- };
703
- }
704
- /**
705
- * Convert OpenAPI parameter to tool parameter schema
706
- */
707
- convertOpenAPIParamToSchema(param) {
708
- const schema = param.schema;
709
- return this.convertOpenAPISchemaToParameterSchema(schema);
710
- }
711
- /**
712
- * Convert OpenAPI schema to parameter schema
713
- */
714
- convertOpenAPISchemaToParameterSchema(schema) {
715
- if ("$ref" in schema) {
716
- return { type: "object" };
717
- }
718
- const result = {
719
- type: this.mapOpenAPIType(schema.type)
720
- };
721
- if (schema.description) {
722
- result.description = schema.description;
723
- }
724
- if (schema.enum) {
725
- result.enum = schema.enum;
726
- }
727
- if (schema.minimum !== void 0) {
728
- result.minimum = schema.minimum;
729
- }
730
- if (schema.maximum !== void 0) {
731
- result.maximum = schema.maximum;
732
- }
733
- if (schema.pattern) {
734
- result.pattern = schema.pattern;
735
- }
736
- if (schema.format) {
737
- result.format = schema.format;
738
- }
739
- if (schema.default !== void 0) {
740
- result.default = schema.default;
741
- }
742
- if (schema.type === "array" && schema.items) {
743
- result.items = this.convertOpenAPISchemaToParameterSchema(schema.items);
744
- }
745
- if (schema.type === "object" && schema.properties) {
746
- result.properties = {};
747
- for (const [propName, propSchema] of Object.entries(schema.properties)) {
748
- result.properties[propName] = this.convertOpenAPISchemaToParameterSchema(propSchema);
749
- }
750
- if (schema.required && schema.required.length > 0) {
751
- result.required = schema.required;
752
- }
753
- }
754
- return result;
755
- }
756
- /**
757
- * Map OpenAPI type to JSON schema type
758
- */
759
- mapOpenAPIType(type) {
760
- switch (type) {
761
- case "string":
762
- return "string";
763
- case "number":
764
- return "number";
765
- case "integer":
766
- return "integer";
767
- case "boolean":
768
- return "boolean";
769
- case "array":
770
- return "array";
771
- case "object":
772
- return "object";
773
- default:
774
- return "string";
775
- }
1066
+ return createSchemaFromOperation(this.operationId, operation.operation);
776
1067
  }
777
1068
  };
778
1069
  function createOpenAPITool(config) {
@@ -788,9 +1079,33 @@ var BashSchema = z.object({
788
1079
  timeout: z.number().optional().describe("Optional timeout in milliseconds (max 600000). Default is 120000 (2 minutes)"),
789
1080
  workingDirectory: z.string().optional().describe("Working directory for the command. Defaults to the current working directory")
790
1081
  });
791
- async function runBash(args) {
1082
+ async function runBash(args, options = {}) {
792
1083
  const { command, timeout = DEFAULT_TIMEOUT_MS, workingDirectory } = args;
793
- return new Promise((resolve3) => {
1084
+ if (options.sandboxClient) {
1085
+ try {
1086
+ const sandboxResult = await options.sandboxClient.run(command, {
1087
+ timeoutMs: timeout,
1088
+ workingDirectory
1089
+ });
1090
+ const output = sandboxResult.stderr ? `${sandboxResult.stdout}
1091
+ stderr:
1092
+ ${sandboxResult.stderr}` : sandboxResult.stdout;
1093
+ const result = {
1094
+ success: true,
1095
+ output,
1096
+ exitCode: sandboxResult.exitCode
1097
+ };
1098
+ return JSON.stringify(result);
1099
+ } catch (err) {
1100
+ const result = {
1101
+ success: false,
1102
+ output: "",
1103
+ error: err instanceof Error ? err.message : String(err)
1104
+ };
1105
+ return JSON.stringify(result);
1106
+ }
1107
+ }
1108
+ return new Promise((resolve4) => {
794
1109
  const stdoutChunks = [];
795
1110
  const stderrChunks = [];
796
1111
  let timedOut = false;
@@ -809,12 +1124,17 @@ async function runBash(args) {
809
1124
  const timer = setTimeout(() => {
810
1125
  timedOut = true;
811
1126
  child.kill("SIGTERM");
1127
+ settle({
1128
+ success: false,
1129
+ output: Buffer.concat(stdoutChunks).toString("utf8"),
1130
+ error: `Command timed out after ${timeout}ms`
1131
+ });
812
1132
  }, timeout);
813
1133
  function settle(result) {
814
1134
  if (settled) return;
815
1135
  settled = true;
816
1136
  clearTimeout(timer);
817
- resolve3(JSON.stringify(result));
1137
+ resolve4(JSON.stringify(result));
818
1138
  }
819
1139
  child.on("error", (err) => {
820
1140
  settle({
@@ -847,17 +1167,20 @@ ${stderr}` : stdout;
847
1167
  });
848
1168
  });
849
1169
  }
850
- var bashTool = createZodFunctionTool(
851
- "Bash",
852
- "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.",
853
- BashSchema,
854
- async (params) => {
855
- return runBash(params);
856
- }
857
- );
1170
+ function createBashTool(options = {}) {
1171
+ return createZodFunctionTool(
1172
+ "Bash",
1173
+ "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.",
1174
+ BashSchema,
1175
+ async (params) => {
1176
+ return runBash(params, options);
1177
+ }
1178
+ );
1179
+ }
1180
+ var bashTool = createBashTool();
858
1181
 
859
1182
  // src/builtins/read-tool.ts
860
- import { readFile, stat } from "fs/promises";
1183
+ import { readFile as readFile2, stat } from "fs/promises";
861
1184
  import { z as z2 } from "zod";
862
1185
  var DEFAULT_LIMIT = 2e3;
863
1186
  var ReadSchema = z2.object({
@@ -884,88 +1207,152 @@ function formatWithLineNumbers(lines, startLine) {
884
1207
  return `${lineNum} ${line}`;
885
1208
  }).join("\n");
886
1209
  }
887
- async function readFileTool(args) {
1210
+ function formatReadResult(filePath, content, startLine, limit) {
1211
+ const allLines = content.split("\n");
1212
+ if (allLines[allLines.length - 1] === "") {
1213
+ allLines.pop();
1214
+ }
1215
+ const zeroBasedStart = startLine - 1;
1216
+ const selectedLines = allLines.slice(zeroBasedStart, zeroBasedStart + limit);
1217
+ const output = formatWithLineNumbers(selectedLines, startLine);
1218
+ const totalLines = allLines.length;
1219
+ const returnedLines = selectedLines.length;
1220
+ const header = returnedLines < totalLines ? `[File: ${filePath} (lines ${startLine}-${startLine + returnedLines - 1} of ${totalLines})]
1221
+ ` : `[File: ${filePath} (${totalLines} lines)]
1222
+ `;
1223
+ const result = {
1224
+ success: true,
1225
+ output: header + output
1226
+ };
1227
+ return JSON.stringify(result);
1228
+ }
1229
+ async function readFileTool(args, options = {}) {
888
1230
  const { filePath, offset, limit = DEFAULT_LIMIT } = args;
889
1231
  const startLine = offset !== void 0 && offset > 0 ? offset : 1;
1232
+ if (options.sandboxClient) {
1233
+ try {
1234
+ const content2 = await options.sandboxClient.readFile(filePath);
1235
+ return formatReadResult(filePath, content2, startLine, limit);
1236
+ } catch (err) {
1237
+ const result = {
1238
+ success: false,
1239
+ output: "",
1240
+ error: err instanceof Error ? err.message : String(err)
1241
+ };
1242
+ return JSON.stringify(result);
1243
+ }
1244
+ }
890
1245
  let fileStats;
891
1246
  try {
892
1247
  fileStats = await stat(filePath);
893
1248
  } catch (err) {
894
- const result2 = {
1249
+ const result = {
895
1250
  success: false,
896
1251
  output: "",
897
1252
  error: `File not found: ${filePath}`
898
1253
  };
899
- return JSON.stringify(result2);
1254
+ return JSON.stringify(result);
900
1255
  }
901
1256
  if (!fileStats.isFile()) {
902
- const result2 = {
1257
+ const result = {
903
1258
  success: false,
904
1259
  output: "",
905
1260
  error: `Path is not a file: ${filePath}`
906
1261
  };
907
- return JSON.stringify(result2);
1262
+ return JSON.stringify(result);
908
1263
  }
909
1264
  let buffer;
910
1265
  try {
911
- buffer = await readFile(filePath);
1266
+ buffer = await readFile2(filePath);
912
1267
  } catch (err) {
913
- const result2 = {
1268
+ const result = {
914
1269
  success: false,
915
1270
  output: "",
916
1271
  error: err instanceof Error ? err.message : String(err)
917
1272
  };
918
- return JSON.stringify(result2);
1273
+ return JSON.stringify(result);
919
1274
  }
920
1275
  if (isBinary(buffer)) {
921
- const result2 = {
1276
+ const result = {
922
1277
  success: false,
923
1278
  output: "",
924
1279
  error: `Binary file not supported: ${filePath}`
925
1280
  };
926
- return JSON.stringify(result2);
1281
+ return JSON.stringify(result);
927
1282
  }
928
1283
  const content = buffer.toString("utf8");
929
- const allLines = content.split("\n");
930
- if (allLines[allLines.length - 1] === "") {
931
- allLines.pop();
1284
+ return formatReadResult(filePath, content, startLine, limit);
1285
+ }
1286
+ function createReadTool(options = {}) {
1287
+ return createZodFunctionTool(
1288
+ "Read",
1289
+ "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.",
1290
+ ReadSchema,
1291
+ async (params) => {
1292
+ return readFileTool(params, options);
1293
+ }
1294
+ );
1295
+ }
1296
+ var readTool = createReadTool();
1297
+
1298
+ // src/builtins/write-tool.ts
1299
+ import { z as z3 } from "zod";
1300
+
1301
+ // src/builtins/atomic-file-write.ts
1302
+ import { randomBytes } from "crypto";
1303
+ import { chmod, mkdir, rename, rm, stat as stat2, writeFile } from "fs/promises";
1304
+ import { basename, dirname, join as join2 } from "path";
1305
+ var TEMP_RANDOM_BYTES = 6;
1306
+ var PRESERVED_MODE_BITS = 4095;
1307
+ var MISSING_FILE_ERROR_CODE = "ENOENT";
1308
+ function createTempFilePath(filePath) {
1309
+ const dir = dirname(filePath);
1310
+ const name = basename(filePath);
1311
+ const suffix = randomBytes(TEMP_RANDOM_BYTES).toString("hex");
1312
+ return join2(dir, `.${name}.robota-tmp-${process.pid}-${Date.now()}-${suffix}`);
1313
+ }
1314
+ async function readExistingMode(filePath) {
1315
+ try {
1316
+ const fileStats = await stat2(filePath);
1317
+ return fileStats.mode & PRESERVED_MODE_BITS;
1318
+ } catch (error) {
1319
+ if (error instanceof Error && hasErrorCode(error, MISSING_FILE_ERROR_CODE)) return void 0;
1320
+ throw error;
932
1321
  }
933
- const zeroBasedStart = startLine - 1;
934
- const selectedLines = allLines.slice(zeroBasedStart, zeroBasedStart + limit);
935
- const output = formatWithLineNumbers(selectedLines, startLine);
936
- const totalLines = allLines.length;
937
- const returnedLines = selectedLines.length;
938
- const header = returnedLines < totalLines ? `[File: ${filePath} (lines ${startLine}-${startLine + returnedLines - 1} of ${totalLines})]
939
- ` : `[File: ${filePath} (${totalLines} lines)]
940
- `;
941
- const result = {
942
- success: true,
943
- output: header + output
944
- };
945
- return JSON.stringify(result);
946
1322
  }
947
- var readTool = createZodFunctionTool(
948
- "Read",
949
- "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.",
950
- ReadSchema,
951
- async (params) => {
952
- return readFileTool(params);
1323
+ function hasErrorCode(error, code) {
1324
+ return "code" in error && error.code === code;
1325
+ }
1326
+ async function atomicWriteUtf8File(filePath, content) {
1327
+ const dir = dirname(filePath);
1328
+ await mkdir(dir, { recursive: true });
1329
+ const existingMode = await readExistingMode(filePath);
1330
+ const tempFilePath = createTempFilePath(filePath);
1331
+ try {
1332
+ await writeFile(tempFilePath, content, "utf8");
1333
+ if (existingMode !== void 0) {
1334
+ await chmod(tempFilePath, existingMode);
1335
+ }
1336
+ await rename(tempFilePath, filePath);
1337
+ } catch (error) {
1338
+ await rm(tempFilePath, { force: true }).catch(() => void 0);
1339
+ throw error;
953
1340
  }
954
- );
1341
+ }
955
1342
 
956
1343
  // src/builtins/write-tool.ts
957
- import { writeFile, mkdir } from "fs/promises";
958
- import { dirname } from "path";
959
- import { z as z3 } from "zod";
960
1344
  var WriteSchema = z3.object({
961
1345
  filePath: z3.string().describe("The absolute path to the file to write"),
962
1346
  content: z3.string().describe("The content to write to the file")
963
1347
  });
964
- async function writeFileTool(args) {
1348
+ async function writeFileTool(args, options = {}) {
965
1349
  const { filePath, content } = args;
966
1350
  try {
967
- await mkdir(dirname(filePath), { recursive: true });
968
- await writeFile(filePath, content, "utf8");
1351
+ if (options.sandboxClient) {
1352
+ await options.sandboxClient.writeFile(filePath, content);
1353
+ } else {
1354
+ await atomicWriteUtf8File(filePath, content);
1355
+ }
969
1356
  const result = {
970
1357
  success: true,
971
1358
  output: `Written ${Buffer.byteLength(content, "utf8")} bytes to ${filePath}`
@@ -980,17 +1367,20 @@ async function writeFileTool(args) {
980
1367
  return JSON.stringify(result);
981
1368
  }
982
1369
  }
983
- var writeTool = createZodFunctionTool(
984
- "Write",
985
- "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.",
986
- WriteSchema,
987
- async (params) => {
988
- return writeFileTool(params);
989
- }
990
- );
1370
+ function createWriteTool(options = {}) {
1371
+ return createZodFunctionTool(
1372
+ "Write",
1373
+ "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.",
1374
+ WriteSchema,
1375
+ async (params) => {
1376
+ return writeFileTool(params, options);
1377
+ }
1378
+ );
1379
+ }
1380
+ var writeTool = createWriteTool();
991
1381
 
992
1382
  // src/builtins/edit-tool.ts
993
- import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
1383
+ import { readFile as readFile3 } from "fs/promises";
994
1384
  import { z as z4 } from "zod";
995
1385
  var EditSchema = z4.object({
996
1386
  filePath: z4.string().describe("The absolute path to the file to modify"),
@@ -1000,11 +1390,11 @@ var EditSchema = z4.object({
1000
1390
  "Replace all occurrences of old_string (default: false). Useful for renaming variables"
1001
1391
  )
1002
1392
  });
1003
- async function editFileTool(args) {
1393
+ async function editFileTool(args, options = {}) {
1004
1394
  const { filePath, oldString, newString, replaceAll = false } = args;
1005
1395
  let content;
1006
1396
  try {
1007
- content = await readFile2(filePath, "utf8");
1397
+ content = options.sandboxClient ? await options.sandboxClient.readFile(filePath) : await readFile3(filePath, "utf8");
1008
1398
  } catch (err) {
1009
1399
  const result2 = {
1010
1400
  success: false,
@@ -1036,7 +1426,11 @@ async function editFileTool(args) {
1036
1426
  }
1037
1427
  const updated = replaceAll ? content.split(oldString).join(newString) : content.slice(0, content.indexOf(oldString)) + newString + content.slice(content.indexOf(oldString) + oldString.length);
1038
1428
  try {
1039
- await writeFile2(filePath, updated, "utf8");
1429
+ if (options.sandboxClient) {
1430
+ await options.sandboxClient.writeFile(filePath, updated);
1431
+ } else {
1432
+ await atomicWriteUtf8File(filePath, updated);
1433
+ }
1040
1434
  } catch (err) {
1041
1435
  const result2 = {
1042
1436
  success: false,
@@ -1046,24 +1440,30 @@ async function editFileTool(args) {
1046
1440
  return JSON.stringify(result2);
1047
1441
  }
1048
1442
  const count = replaceAll ? content.split(oldString).length - 1 : 1;
1443
+ const matchIdx = content.indexOf(oldString);
1444
+ const startLine = matchIdx >= 0 ? content.substring(0, matchIdx).split("\n").length : 1;
1049
1445
  const result = {
1050
1446
  success: true,
1051
- output: `Replaced ${count} occurrence(s) in ${filePath}`
1447
+ output: `Replaced ${count} occurrence(s) in ${filePath}`,
1448
+ startLine
1052
1449
  };
1053
1450
  return JSON.stringify(result);
1054
1451
  }
1055
- var editTool = createZodFunctionTool(
1056
- "Edit",
1057
- "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.",
1058
- EditSchema,
1059
- async (params) => {
1060
- return editFileTool(params);
1061
- }
1062
- );
1452
+ function createEditTool(options = {}) {
1453
+ return createZodFunctionTool(
1454
+ "Edit",
1455
+ "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.",
1456
+ EditSchema,
1457
+ async (params) => {
1458
+ return editFileTool(params, options);
1459
+ }
1460
+ );
1461
+ }
1462
+ var editTool = createEditTool();
1063
1463
 
1064
1464
  // src/builtins/glob-tool.ts
1065
- import { stat as stat2 } from "fs/promises";
1066
- import { resolve } from "path";
1465
+ import { stat as stat3 } from "fs/promises";
1466
+ import { resolve as resolve2 } from "path";
1067
1467
  import fg from "fast-glob";
1068
1468
  import { z as z5 } from "zod";
1069
1469
  var DEFAULT_MAX_RESULTS = 1e3;
@@ -1078,7 +1478,7 @@ var GlobSchema = z5.object({
1078
1478
  });
1079
1479
  async function globFileTool(args) {
1080
1480
  const { pattern, path: basePath } = args;
1081
- const cwd = basePath ? resolve(basePath) : process.cwd();
1481
+ const cwd = basePath ? resolve2(basePath) : process.cwd();
1082
1482
  let matches;
1083
1483
  try {
1084
1484
  matches = await fg(pattern, {
@@ -1097,9 +1497,9 @@ async function globFileTool(args) {
1097
1497
  }
1098
1498
  const withMtime = await Promise.all(
1099
1499
  matches.map(async (p) => {
1100
- const absPath = resolve(cwd, p);
1500
+ const absPath = resolve2(cwd, p);
1101
1501
  try {
1102
- const s = await stat2(absPath);
1502
+ const s = await stat3(absPath);
1103
1503
  return { path: p, mtime: s.mtimeMs };
1104
1504
  } catch {
1105
1505
  return { path: p, mtime: 0 };
@@ -1134,8 +1534,8 @@ var globTool = createZodFunctionTool(
1134
1534
  );
1135
1535
 
1136
1536
  // src/builtins/grep-tool.ts
1137
- import { readFile as readFile3, readdir, stat as stat3 } from "fs/promises";
1138
- import { join, resolve as resolve2 } from "path";
1537
+ import { readFile as readFile4, readdir as readdir2, stat as stat4 } from "fs/promises";
1538
+ import { join as join3, resolve as resolve3 } from "path";
1139
1539
  import { z as z6 } from "zod";
1140
1540
  var GrepSchema = z6.object({
1141
1541
  pattern: z6.string().describe("The regular expression pattern to search for in file contents"),
@@ -1163,16 +1563,16 @@ async function collectFiles(dirPath, glob) {
1163
1563
  async function walk(current) {
1164
1564
  let entryNames;
1165
1565
  try {
1166
- entryNames = await readdir(current);
1566
+ entryNames = await readdir2(current);
1167
1567
  } catch {
1168
1568
  return;
1169
1569
  }
1170
1570
  for (const name of entryNames) {
1171
1571
  if (name === "node_modules" || name === ".git") continue;
1172
- const fullPath = join(current, name);
1572
+ const fullPath = join3(current, name);
1173
1573
  let fileStat;
1174
1574
  try {
1175
- fileStat = await stat3(fullPath);
1575
+ fileStat = await stat4(fullPath);
1176
1576
  } catch {
1177
1577
  continue;
1178
1578
  }
@@ -1228,7 +1628,7 @@ async function grepFileTool(args) {
1228
1628
  contextLines = 0,
1229
1629
  outputMode = "files_with_matches"
1230
1630
  } = args;
1231
- const targetPath = searchPath ? resolve2(searchPath) : process.cwd();
1631
+ const targetPath = searchPath ? resolve3(searchPath) : process.cwd();
1232
1632
  let regex;
1233
1633
  try {
1234
1634
  regex = new RegExp(pattern);
@@ -1242,7 +1642,7 @@ async function grepFileTool(args) {
1242
1642
  }
1243
1643
  let targetStat;
1244
1644
  try {
1245
- targetStat = await stat3(targetPath);
1645
+ targetStat = await stat4(targetPath);
1246
1646
  } catch {
1247
1647
  const result2 = {
1248
1648
  success: false,
@@ -1261,7 +1661,7 @@ async function grepFileTool(args) {
1261
1661
  for (const filePath of files) {
1262
1662
  let content;
1263
1663
  try {
1264
- const buffer = await readFile3(filePath);
1664
+ const buffer = await readFile4(filePath);
1265
1665
  const checkLen = Math.min(buffer.length, 8192);
1266
1666
  let hasBinary = false;
1267
1667
  for (let i = 0; i < checkLen; i++) {
@@ -1425,17 +1825,25 @@ var webSearchTool = createZodFunctionTool(
1425
1825
  async (params) => runWebSearch(params)
1426
1826
  );
1427
1827
  export {
1828
+ E2BSandboxClient,
1428
1829
  FunctionTool,
1830
+ InMemorySandboxClient,
1429
1831
  OpenAPITool,
1430
1832
  ToolRegistry,
1833
+ applyWorkspaceManifest,
1431
1834
  bashTool,
1835
+ createBashTool,
1836
+ createEditTool,
1432
1837
  createFunctionTool,
1433
1838
  createOpenAPITool,
1839
+ createReadTool,
1840
+ createWriteTool,
1434
1841
  createZodFunctionTool,
1435
1842
  editTool,
1436
1843
  globTool,
1437
1844
  grepTool,
1438
1845
  readTool,
1846
+ validateWorkspaceManifestPath,
1439
1847
  webFetchTool,
1440
1848
  webSearchTool,
1441
1849
  writeTool,