@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.
- package/README.md +67 -21
- package/dist/browser/browser.d.ts +268 -0
- package/dist/browser/browser.js +1 -0
- package/dist/node/index.cjs +505 -257
- package/dist/node/index.d.cts +170 -15
- package/dist/node/index.d.ts +170 -15
- package/dist/node/index.js +444 -173
- package/package.json +10 -6
package/dist/node/index.cjs
CHANGED
|
@@ -1,56 +1,281 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var promises = require('fs/promises');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
var agentCore = require('@robota-sdk/agent-core');
|
|
6
|
+
var child_process = require('child_process');
|
|
7
|
+
var zod = require('zod');
|
|
8
|
+
var crypto = require('crypto');
|
|
9
|
+
var fg = require('fast-glob');
|
|
10
|
+
|
|
11
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
|
|
13
|
+
var fg__default = /*#__PURE__*/_interopDefault(fg);
|
|
14
|
+
|
|
15
|
+
// src/sandbox/e2b-sandbox-client.ts
|
|
16
|
+
var E2BSandboxClient = class {
|
|
17
|
+
sandbox;
|
|
18
|
+
connectSandbox;
|
|
19
|
+
createSandboxFromSnapshot;
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this.sandbox = options.sandbox;
|
|
22
|
+
this.connectSandbox = options.connectSandbox;
|
|
23
|
+
this.createSandboxFromSnapshot = options.createSandboxFromSnapshot;
|
|
24
|
+
}
|
|
25
|
+
async run(command, options) {
|
|
26
|
+
const result = await this.sandbox.commands.run(command, {
|
|
27
|
+
background: false,
|
|
28
|
+
timeoutMs: options?.timeoutMs,
|
|
29
|
+
cwd: options?.workingDirectory
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
stdout: result.stdout ?? "",
|
|
33
|
+
stderr: result.stderr ?? "",
|
|
34
|
+
exitCode: result.exitCode ?? result.exit_code ?? 0
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
async readFile(path) {
|
|
38
|
+
const content = await this.sandbox.files.read(path);
|
|
39
|
+
return typeof content === "string" ? content : Buffer.from(content).toString("utf8");
|
|
40
|
+
}
|
|
41
|
+
async writeFile(path, content) {
|
|
42
|
+
await this.sandbox.files.write(path, content);
|
|
43
|
+
}
|
|
44
|
+
async snapshot() {
|
|
45
|
+
if (this.sandbox.createSnapshot) {
|
|
46
|
+
const snapshot = await this.sandbox.createSnapshot();
|
|
47
|
+
const snapshotId = snapshot.snapshotId ?? snapshot.id;
|
|
48
|
+
if (!snapshotId) {
|
|
49
|
+
throw new Error("E2B createSnapshot() did not return a snapshot id.");
|
|
50
|
+
}
|
|
51
|
+
return snapshotId;
|
|
52
|
+
}
|
|
53
|
+
const sandboxId = this.sandbox.sandboxId;
|
|
54
|
+
if (!sandboxId) {
|
|
55
|
+
throw new Error("E2B sandboxId is required to create a resumable sandbox snapshot.");
|
|
56
|
+
}
|
|
57
|
+
if (!this.sandbox.pause) {
|
|
58
|
+
throw new Error("E2B sandbox adapter does not expose pause().");
|
|
59
|
+
}
|
|
60
|
+
await this.sandbox.pause();
|
|
61
|
+
return sandboxId;
|
|
62
|
+
}
|
|
63
|
+
async restore(snapshotId) {
|
|
64
|
+
if (this.createSandboxFromSnapshot) {
|
|
65
|
+
this.sandbox = await this.createSandboxFromSnapshot(snapshotId);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (this.connectSandbox) {
|
|
69
|
+
this.sandbox = await this.connectSandbox(snapshotId);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (this.sandbox.sandboxId === snapshotId && this.sandbox.connect) {
|
|
73
|
+
this.sandbox = await this.sandbox.connect();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
throw new Error(
|
|
77
|
+
"E2B sandbox restore requires connectSandbox(snapshotId) or sandbox.connect()."
|
|
78
|
+
);
|
|
17
79
|
}
|
|
18
|
-
return to;
|
|
19
80
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
81
|
|
|
30
|
-
// src/
|
|
31
|
-
var
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
82
|
+
// src/sandbox/in-memory-sandbox-client.ts
|
|
83
|
+
var InMemorySandboxClient = class {
|
|
84
|
+
files = /* @__PURE__ */ new Map();
|
|
85
|
+
snapshots = /* @__PURE__ */ new Map();
|
|
86
|
+
runHandler;
|
|
87
|
+
snapshotSequence = 0;
|
|
88
|
+
constructor(options = {}) {
|
|
89
|
+
for (const [path, content] of Object.entries(options.files ?? {})) {
|
|
90
|
+
this.files.set(path, content);
|
|
91
|
+
}
|
|
92
|
+
this.runHandler = options.runHandler;
|
|
93
|
+
}
|
|
94
|
+
async run(command, options) {
|
|
95
|
+
if (this.runHandler) {
|
|
96
|
+
return this.runHandler(command, options, this.files);
|
|
97
|
+
}
|
|
98
|
+
return { stdout: "", stderr: "", exitCode: 0 };
|
|
99
|
+
}
|
|
100
|
+
async readFile(path) {
|
|
101
|
+
const content = this.files.get(path);
|
|
102
|
+
if (content === void 0) {
|
|
103
|
+
throw new Error(`Sandbox file not found: ${path}`);
|
|
104
|
+
}
|
|
105
|
+
return content;
|
|
106
|
+
}
|
|
107
|
+
async writeFile(path, content) {
|
|
108
|
+
this.files.set(path, content);
|
|
109
|
+
}
|
|
110
|
+
async snapshot() {
|
|
111
|
+
const snapshotId = `snapshot-${++this.snapshotSequence}`;
|
|
112
|
+
this.snapshots.set(snapshotId, new Map(this.files));
|
|
113
|
+
return snapshotId;
|
|
114
|
+
}
|
|
115
|
+
async restore(snapshotId) {
|
|
116
|
+
const snapshot = this.snapshots.get(snapshotId);
|
|
117
|
+
if (!snapshot) {
|
|
118
|
+
throw new Error(`Sandbox snapshot not found: ${snapshotId}`);
|
|
119
|
+
}
|
|
120
|
+
this.files.clear();
|
|
121
|
+
for (const [path, content] of snapshot.entries()) {
|
|
122
|
+
this.files.set(path, content);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
getFile(path) {
|
|
126
|
+
return this.files.get(path);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
var DEFAULT_TARGET_ROOT = "/workspace";
|
|
130
|
+
var WINDOWS_ABSOLUTE_PATH_PATTERN = /^[A-Za-z]:[\\/]/;
|
|
131
|
+
var SHELL_QUOTE_PATTERN = /'/g;
|
|
132
|
+
async function applyWorkspaceManifest(sandboxClient, manifest, options = {}) {
|
|
133
|
+
if (sandboxClient.applyManifest) {
|
|
134
|
+
return sandboxClient.applyManifest(manifest, options);
|
|
135
|
+
}
|
|
136
|
+
const targetRoot = normalizeSandboxRoot(options.targetRoot ?? DEFAULT_TARGET_ROOT);
|
|
137
|
+
const appliedEntries = [];
|
|
138
|
+
for (const [rawPath, entry] of Object.entries(manifest.entries)) {
|
|
139
|
+
const path = validateWorkspaceManifestPath(rawPath);
|
|
140
|
+
const targetPath = joinSandboxPath(targetRoot, path);
|
|
141
|
+
appliedEntries.push(
|
|
142
|
+
await applyManifestEntry(sandboxClient, path, targetPath, targetRoot, entry, options)
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
return { entries: appliedEntries };
|
|
146
|
+
}
|
|
147
|
+
function validateWorkspaceManifestPath(path) {
|
|
148
|
+
if (path.length === 0) {
|
|
149
|
+
throw new Error("workspace manifest path must not be empty");
|
|
150
|
+
}
|
|
151
|
+
if (path.includes("\0")) {
|
|
152
|
+
throw new Error("workspace manifest path must not contain NUL bytes");
|
|
153
|
+
}
|
|
154
|
+
if (path.startsWith("/") || path.startsWith("\\") || WINDOWS_ABSOLUTE_PATH_PATTERN.test(path)) {
|
|
155
|
+
throw new Error("workspace manifest path must be workspace-relative");
|
|
156
|
+
}
|
|
157
|
+
const parts = path.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
158
|
+
if (parts.length === 0) {
|
|
159
|
+
throw new Error("workspace manifest path must not resolve to the workspace root");
|
|
160
|
+
}
|
|
161
|
+
if (parts.some((part) => part === "..")) {
|
|
162
|
+
throw new Error("workspace manifest path cannot contain traversal segments");
|
|
163
|
+
}
|
|
164
|
+
const normalizedParts = parts.filter((part) => part !== ".");
|
|
165
|
+
if (normalizedParts.length === 0) {
|
|
166
|
+
throw new Error("workspace manifest path must not resolve to the workspace root");
|
|
167
|
+
}
|
|
168
|
+
return normalizedParts.join("/");
|
|
169
|
+
}
|
|
170
|
+
async function applyManifestEntry(sandboxClient, path, targetPath, targetRoot, entry, options) {
|
|
171
|
+
switch (entry.type) {
|
|
172
|
+
case "file":
|
|
173
|
+
await writeSandboxFile(sandboxClient, targetPath, targetRoot, entry.content);
|
|
174
|
+
return createAppliedEntry(path, entry.type);
|
|
175
|
+
case "dir":
|
|
176
|
+
await createSandboxDirectory(sandboxClient, targetPath);
|
|
177
|
+
return createAppliedEntry(path, entry.type);
|
|
178
|
+
case "localFile":
|
|
179
|
+
await copyLocalFile(sandboxClient, entry.src, targetPath, targetRoot, options);
|
|
180
|
+
return createAppliedEntry(path, entry.type);
|
|
181
|
+
case "localDir":
|
|
182
|
+
await copyLocalDirectory(sandboxClient, entry.src, targetPath, options);
|
|
183
|
+
return createAppliedEntry(path, entry.type);
|
|
184
|
+
case "gitRepo":
|
|
185
|
+
await cloneGitRepository(sandboxClient, entry, targetPath);
|
|
186
|
+
return createAppliedEntry(path, entry.type);
|
|
187
|
+
case "s3Mount":
|
|
188
|
+
case "gcsMount":
|
|
189
|
+
case "r2Mount":
|
|
190
|
+
case "azureBlobMount":
|
|
191
|
+
return {
|
|
192
|
+
path,
|
|
193
|
+
type: entry.type,
|
|
194
|
+
status: "unsupported",
|
|
195
|
+
message: `${entry.type} requires a provider-specific sandbox adapter.`
|
|
196
|
+
};
|
|
197
|
+
default:
|
|
198
|
+
return assertUnreachable(entry);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function createAppliedEntry(path, type) {
|
|
202
|
+
return { path, type, status: "applied" };
|
|
203
|
+
}
|
|
204
|
+
async function copyLocalFile(sandboxClient, source, targetPath, targetRoot, options) {
|
|
205
|
+
const hostSourcePath = resolveHostSourcePath(source, options.hostRoot);
|
|
206
|
+
const content = await promises.readFile(hostSourcePath, "utf8");
|
|
207
|
+
await writeSandboxFile(sandboxClient, targetPath, targetRoot, content);
|
|
208
|
+
}
|
|
209
|
+
async function copyLocalDirectory(sandboxClient, source, targetPath, options) {
|
|
210
|
+
const hostSourcePath = resolveHostSourcePath(source, options.hostRoot);
|
|
211
|
+
await copyLocalDirectoryRecursive(sandboxClient, hostSourcePath, targetPath);
|
|
212
|
+
}
|
|
213
|
+
async function copyLocalDirectoryRecursive(sandboxClient, sourcePath, targetPath) {
|
|
214
|
+
await createSandboxDirectory(sandboxClient, targetPath);
|
|
215
|
+
const entries = await promises.readdir(sourcePath, { withFileTypes: true });
|
|
216
|
+
for (const entry of entries) {
|
|
217
|
+
const childSourcePath = path.join(sourcePath, entry.name);
|
|
218
|
+
const childTargetPath = joinSandboxPath(targetPath, entry.name);
|
|
219
|
+
if (entry.isDirectory()) {
|
|
220
|
+
await copyLocalDirectoryRecursive(sandboxClient, childSourcePath, childTargetPath);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (entry.isFile()) {
|
|
224
|
+
const content = await promises.readFile(childSourcePath, "utf8");
|
|
225
|
+
await sandboxClient.writeFile(childTargetPath, content);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async function cloneGitRepository(sandboxClient, entry, targetPath) {
|
|
230
|
+
const shallowArgs = entry.shallow === false ? "" : " --depth 1";
|
|
231
|
+
const refArgs = entry.ref ? ` --branch ${quoteShellArg(entry.ref)}` : "";
|
|
232
|
+
await runSandboxCommand(
|
|
233
|
+
sandboxClient,
|
|
234
|
+
`git clone${shallowArgs}${refArgs} ${quoteShellArg(entry.url)} ${quoteShellArg(targetPath)}`
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
async function writeSandboxFile(sandboxClient, targetPath, targetRoot, content) {
|
|
238
|
+
const parentPath = path.posix.dirname(targetPath);
|
|
239
|
+
if (parentPath !== targetRoot) {
|
|
240
|
+
await createSandboxDirectory(sandboxClient, parentPath);
|
|
241
|
+
}
|
|
242
|
+
await sandboxClient.writeFile(targetPath, content);
|
|
243
|
+
}
|
|
244
|
+
async function createSandboxDirectory(sandboxClient, targetPath) {
|
|
245
|
+
await runSandboxCommand(sandboxClient, `mkdir -p ${quoteShellArg(targetPath)}`);
|
|
246
|
+
}
|
|
247
|
+
async function runSandboxCommand(sandboxClient, command) {
|
|
248
|
+
const result = await sandboxClient.run(command);
|
|
249
|
+
if (result.exitCode !== 0) {
|
|
250
|
+
throw new Error(
|
|
251
|
+
`workspace manifest command failed: ${command}
|
|
252
|
+
${result.stderr ?? result.stdout}`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
function resolveHostSourcePath(source, hostRoot) {
|
|
257
|
+
return path.isAbsolute(source) ? path.resolve(source) : path.resolve(hostRoot ?? process.cwd(), source);
|
|
258
|
+
}
|
|
259
|
+
function normalizeSandboxRoot(root) {
|
|
260
|
+
const normalized = root.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
261
|
+
if (!normalized.startsWith("/")) {
|
|
262
|
+
throw new Error("workspace manifest targetRoot must be an absolute sandbox path");
|
|
263
|
+
}
|
|
264
|
+
return normalized.length === 0 ? "/" : normalized;
|
|
265
|
+
}
|
|
266
|
+
function joinSandboxPath(root, path) {
|
|
267
|
+
const normalizedRoot = normalizeSandboxRoot(root);
|
|
268
|
+
if (normalizedRoot === "/") {
|
|
269
|
+
return `/${path}`;
|
|
270
|
+
}
|
|
271
|
+
return `${normalizedRoot}/${path}`;
|
|
272
|
+
}
|
|
273
|
+
function quoteShellArg(value) {
|
|
274
|
+
return `'${value.replace(SHELL_QUOTE_PATTERN, "'\\''")}'`;
|
|
275
|
+
}
|
|
276
|
+
function assertUnreachable(value) {
|
|
277
|
+
throw new Error(`unsupported workspace manifest entry: ${JSON.stringify(value)}`);
|
|
278
|
+
}
|
|
54
279
|
var ToolRegistry = class {
|
|
55
280
|
tools = /* @__PURE__ */ new Map();
|
|
56
281
|
/**
|
|
@@ -58,18 +283,18 @@ var ToolRegistry = class {
|
|
|
58
283
|
*/
|
|
59
284
|
register(tool) {
|
|
60
285
|
if (!tool.schema?.name) {
|
|
61
|
-
throw new
|
|
286
|
+
throw new agentCore.ValidationError("Tool must have a valid schema with name");
|
|
62
287
|
}
|
|
63
288
|
const toolName = tool.schema.name;
|
|
64
289
|
this.validateToolSchema(tool.schema);
|
|
65
290
|
if (this.tools.has(toolName)) {
|
|
66
|
-
|
|
291
|
+
agentCore.logger.warn(`Tool "${toolName}" is already registered, overriding`, {
|
|
67
292
|
toolName,
|
|
68
293
|
existingTool: this.tools.get(toolName)?.constructor.name
|
|
69
294
|
});
|
|
70
295
|
}
|
|
71
296
|
this.tools.set(toolName, tool);
|
|
72
|
-
|
|
297
|
+
agentCore.logger.debug(`Tool "${toolName}" registered successfully`, {
|
|
73
298
|
toolName,
|
|
74
299
|
toolType: tool.constructor.name,
|
|
75
300
|
parameters: Object.keys(tool.schema.parameters?.properties || {})
|
|
@@ -80,11 +305,11 @@ var ToolRegistry = class {
|
|
|
80
305
|
*/
|
|
81
306
|
unregister(name) {
|
|
82
307
|
if (!this.tools.has(name)) {
|
|
83
|
-
|
|
308
|
+
agentCore.logger.warn(`Attempted to unregister non-existent tool "${name}"`);
|
|
84
309
|
return;
|
|
85
310
|
}
|
|
86
311
|
this.tools.delete(name);
|
|
87
|
-
|
|
312
|
+
agentCore.logger.debug(`Tool "${name}" unregistered successfully`);
|
|
88
313
|
}
|
|
89
314
|
/**
|
|
90
315
|
* Get tool by name
|
|
@@ -103,7 +328,7 @@ var ToolRegistry = class {
|
|
|
103
328
|
*/
|
|
104
329
|
getSchemas() {
|
|
105
330
|
const tools = this.getAll();
|
|
106
|
-
|
|
331
|
+
agentCore.logger.debug("[TOOL-FLOW] ToolRegistry.getSchemas() - Tools before schema extraction", {
|
|
107
332
|
count: tools.length,
|
|
108
333
|
tools: tools.map((t) => ({
|
|
109
334
|
name: t.schema?.name ?? "unnamed",
|
|
@@ -126,7 +351,7 @@ var ToolRegistry = class {
|
|
|
126
351
|
clear() {
|
|
127
352
|
const toolCount = this.tools.size;
|
|
128
353
|
this.tools.clear();
|
|
129
|
-
|
|
354
|
+
agentCore.logger.debug(`Cleared ${toolCount} tools from registry`);
|
|
130
355
|
}
|
|
131
356
|
/**
|
|
132
357
|
* Get tool names
|
|
@@ -152,26 +377,26 @@ var ToolRegistry = class {
|
|
|
152
377
|
*/
|
|
153
378
|
validateToolSchema(schema) {
|
|
154
379
|
if (!schema.name || typeof schema.name !== "string") {
|
|
155
|
-
throw new
|
|
380
|
+
throw new agentCore.ValidationError("Tool schema must have a valid name");
|
|
156
381
|
}
|
|
157
382
|
if (!schema.description || typeof schema.description !== "string") {
|
|
158
|
-
throw new
|
|
383
|
+
throw new agentCore.ValidationError("Tool schema must have a description");
|
|
159
384
|
}
|
|
160
385
|
if (!schema.parameters || typeof schema.parameters !== "object" || schema.parameters === null || Array.isArray(schema.parameters)) {
|
|
161
|
-
throw new
|
|
386
|
+
throw new agentCore.ValidationError("Tool schema must have parameters object");
|
|
162
387
|
}
|
|
163
388
|
if (schema.parameters.type !== "object") {
|
|
164
|
-
throw new
|
|
389
|
+
throw new agentCore.ValidationError('Tool parameters type must be "object"');
|
|
165
390
|
}
|
|
166
391
|
if (schema.parameters.properties) {
|
|
167
392
|
for (const propName of Object.keys(schema.parameters.properties)) {
|
|
168
393
|
const propSchema = schema.parameters.properties[propName];
|
|
169
394
|
if (!propSchema?.type) {
|
|
170
|
-
throw new
|
|
395
|
+
throw new agentCore.ValidationError(`Parameter "${propName}" must have a type`);
|
|
171
396
|
}
|
|
172
397
|
const validTypes = ["string", "number", "boolean", "array", "object"];
|
|
173
398
|
if (!validTypes.includes(propSchema.type)) {
|
|
174
|
-
throw new
|
|
399
|
+
throw new agentCore.ValidationError(
|
|
175
400
|
`Parameter "${propName}" has invalid type "${propSchema.type}"`
|
|
176
401
|
);
|
|
177
402
|
}
|
|
@@ -181,7 +406,7 @@ var ToolRegistry = class {
|
|
|
181
406
|
const properties = schema.parameters.properties || {};
|
|
182
407
|
for (const requiredField of schema.parameters.required) {
|
|
183
408
|
if (!properties[requiredField]) {
|
|
184
|
-
throw new
|
|
409
|
+
throw new agentCore.ValidationError(
|
|
185
410
|
`Required parameter "${requiredField}" is not defined in properties`
|
|
186
411
|
);
|
|
187
412
|
}
|
|
@@ -190,9 +415,6 @@ var ToolRegistry = class {
|
|
|
190
415
|
}
|
|
191
416
|
};
|
|
192
417
|
|
|
193
|
-
// src/implementations/function-tool.ts
|
|
194
|
-
var import_agent_core3 = require("@robota-sdk/agent-core");
|
|
195
|
-
|
|
196
418
|
// src/implementations/function-tool/schema-converter.ts
|
|
197
419
|
function zodToJsonSchema(schema, options = {}) {
|
|
198
420
|
const properties = {};
|
|
@@ -426,17 +648,17 @@ var FunctionTool = class {
|
|
|
426
648
|
this.schema.parameters.properties || {},
|
|
427
649
|
this.schema.parameters.additionalProperties
|
|
428
650
|
);
|
|
429
|
-
throw new
|
|
651
|
+
throw new agentCore.ValidationError(`Invalid parameters for tool "${toolName}": ${errors.join(", ")}`);
|
|
430
652
|
}
|
|
431
653
|
const startTime = Date.now();
|
|
432
654
|
let result;
|
|
433
655
|
try {
|
|
434
656
|
result = await this.fn(parameters, context);
|
|
435
657
|
} catch (error) {
|
|
436
|
-
if (error instanceof
|
|
658
|
+
if (error instanceof agentCore.ToolExecutionError || error instanceof agentCore.ValidationError) {
|
|
437
659
|
throw error;
|
|
438
660
|
}
|
|
439
|
-
throw new
|
|
661
|
+
throw new agentCore.ToolExecutionError(
|
|
440
662
|
`Function tool execution failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
441
663
|
toolName,
|
|
442
664
|
error instanceof Error ? error : new Error(String(error)),
|
|
@@ -490,13 +712,13 @@ var FunctionTool = class {
|
|
|
490
712
|
*/
|
|
491
713
|
validateConstructorInputs() {
|
|
492
714
|
if (!this.schema) {
|
|
493
|
-
throw new
|
|
715
|
+
throw new agentCore.ValidationError("Tool schema is required");
|
|
494
716
|
}
|
|
495
717
|
if (!this.fn || typeof this.fn !== "function") {
|
|
496
|
-
throw new
|
|
718
|
+
throw new agentCore.ValidationError("Tool function is required and must be a function");
|
|
497
719
|
}
|
|
498
720
|
if (!this.schema.name) {
|
|
499
|
-
throw new
|
|
721
|
+
throw new agentCore.ValidationError("Tool schema must have a name");
|
|
500
722
|
}
|
|
501
723
|
}
|
|
502
724
|
};
|
|
@@ -518,7 +740,7 @@ function createZodFunctionTool(name, description, zodSchema, fn) {
|
|
|
518
740
|
const wrappedFn = async (parameters2, context) => {
|
|
519
741
|
const parseResult = zodSchema.safeParse(parameters2);
|
|
520
742
|
if (!parseResult.success) {
|
|
521
|
-
throw new
|
|
743
|
+
throw new agentCore.ValidationError(`Zod validation failed: ${parseResult.error}`);
|
|
522
744
|
}
|
|
523
745
|
const result = await fn(parseResult.data || parameters2, context);
|
|
524
746
|
return typeof result === "string" ? result : JSON.stringify(result);
|
|
@@ -526,9 +748,6 @@ function createZodFunctionTool(name, description, zodSchema, fn) {
|
|
|
526
748
|
return new FunctionTool(schema, wrappedFn);
|
|
527
749
|
}
|
|
528
750
|
|
|
529
|
-
// src/implementations/openapi-tool.ts
|
|
530
|
-
var import_agent_core4 = require("@robota-sdk/agent-core");
|
|
531
|
-
|
|
532
751
|
// src/implementations/openapi-schema-converter.ts
|
|
533
752
|
var HTTP_METHODS = [
|
|
534
753
|
"get",
|
|
@@ -680,7 +899,7 @@ var OpenAPITool = class {
|
|
|
680
899
|
const toolName = this.schema.name;
|
|
681
900
|
const validation = this.validateParameters(parameters);
|
|
682
901
|
if (!validation.isValid) {
|
|
683
|
-
throw new
|
|
902
|
+
throw new agentCore.ValidationError(
|
|
684
903
|
`Invalid parameters for OpenAPI tool "${toolName}": ${validation.errors.join(", ")}`
|
|
685
904
|
);
|
|
686
905
|
}
|
|
@@ -699,11 +918,11 @@ var OpenAPITool = class {
|
|
|
699
918
|
}
|
|
700
919
|
};
|
|
701
920
|
} catch (error) {
|
|
702
|
-
if (error instanceof
|
|
921
|
+
if (error instanceof agentCore.ToolExecutionError || error instanceof agentCore.ValidationError) {
|
|
703
922
|
throw error;
|
|
704
923
|
}
|
|
705
924
|
const safeError = error instanceof Error ? error : new Error(String(error));
|
|
706
|
-
throw new
|
|
925
|
+
throw new agentCore.ToolExecutionError(
|
|
707
926
|
`OpenAPI tool execution failed: ${safeError.message}`,
|
|
708
927
|
toolName,
|
|
709
928
|
safeError,
|
|
@@ -850,24 +1069,44 @@ var OpenAPITool = class {
|
|
|
850
1069
|
function createOpenAPITool(config) {
|
|
851
1070
|
return new OpenAPITool(config);
|
|
852
1071
|
}
|
|
853
|
-
|
|
854
|
-
// src/builtins/bash-tool.ts
|
|
855
|
-
var import_node_child_process = require("child_process");
|
|
856
|
-
var import_zod = require("zod");
|
|
857
1072
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
858
|
-
var BashSchema =
|
|
859
|
-
command:
|
|
860
|
-
timeout:
|
|
861
|
-
workingDirectory:
|
|
1073
|
+
var BashSchema = zod.z.object({
|
|
1074
|
+
command: zod.z.string().describe("The bash command to execute"),
|
|
1075
|
+
timeout: zod.z.number().optional().describe("Optional timeout in milliseconds (max 600000). Default is 120000 (2 minutes)"),
|
|
1076
|
+
workingDirectory: zod.z.string().optional().describe("Working directory for the command. Defaults to the current working directory")
|
|
862
1077
|
});
|
|
863
|
-
async function runBash(args) {
|
|
1078
|
+
async function runBash(args, options = {}) {
|
|
864
1079
|
const { command, timeout = DEFAULT_TIMEOUT_MS, workingDirectory } = args;
|
|
865
|
-
|
|
1080
|
+
if (options.sandboxClient) {
|
|
1081
|
+
try {
|
|
1082
|
+
const sandboxResult = await options.sandboxClient.run(command, {
|
|
1083
|
+
timeoutMs: timeout,
|
|
1084
|
+
workingDirectory
|
|
1085
|
+
});
|
|
1086
|
+
const output = sandboxResult.stderr ? `${sandboxResult.stdout}
|
|
1087
|
+
stderr:
|
|
1088
|
+
${sandboxResult.stderr}` : sandboxResult.stdout;
|
|
1089
|
+
const result = {
|
|
1090
|
+
success: true,
|
|
1091
|
+
output,
|
|
1092
|
+
exitCode: sandboxResult.exitCode
|
|
1093
|
+
};
|
|
1094
|
+
return JSON.stringify(result);
|
|
1095
|
+
} catch (err) {
|
|
1096
|
+
const result = {
|
|
1097
|
+
success: false,
|
|
1098
|
+
output: "",
|
|
1099
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1100
|
+
};
|
|
1101
|
+
return JSON.stringify(result);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
return new Promise((resolve4) => {
|
|
866
1105
|
const stdoutChunks = [];
|
|
867
1106
|
const stderrChunks = [];
|
|
868
1107
|
let timedOut = false;
|
|
869
1108
|
let settled = false;
|
|
870
|
-
const child =
|
|
1109
|
+
const child = child_process.spawn("sh", ["-c", command], {
|
|
871
1110
|
cwd: workingDirectory ?? process.cwd(),
|
|
872
1111
|
env: process.env,
|
|
873
1112
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -891,7 +1130,7 @@ async function runBash(args) {
|
|
|
891
1130
|
if (settled) return;
|
|
892
1131
|
settled = true;
|
|
893
1132
|
clearTimeout(timer);
|
|
894
|
-
|
|
1133
|
+
resolve4(JSON.stringify(result));
|
|
895
1134
|
}
|
|
896
1135
|
child.on("error", (err) => {
|
|
897
1136
|
settle({
|
|
@@ -924,25 +1163,24 @@ ${stderr}` : stdout;
|
|
|
924
1163
|
});
|
|
925
1164
|
});
|
|
926
1165
|
}
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
var
|
|
938
|
-
var import_zod2 = require("zod");
|
|
1166
|
+
function createBashTool(options = {}) {
|
|
1167
|
+
return createZodFunctionTool(
|
|
1168
|
+
"Bash",
|
|
1169
|
+
"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.",
|
|
1170
|
+
BashSchema,
|
|
1171
|
+
async (params) => {
|
|
1172
|
+
return runBash(params, options);
|
|
1173
|
+
}
|
|
1174
|
+
);
|
|
1175
|
+
}
|
|
1176
|
+
var bashTool = createBashTool();
|
|
939
1177
|
var DEFAULT_LIMIT = 2e3;
|
|
940
|
-
var ReadSchema =
|
|
941
|
-
filePath:
|
|
942
|
-
offset:
|
|
1178
|
+
var ReadSchema = zod.z.object({
|
|
1179
|
+
filePath: zod.z.string().describe("The absolute path to the file to read"),
|
|
1180
|
+
offset: zod.z.number().optional().describe(
|
|
943
1181
|
"The line number to start reading from (1-based). Only provide if the file is too large to read at once"
|
|
944
1182
|
),
|
|
945
|
-
limit:
|
|
1183
|
+
limit: zod.z.number().optional().describe(
|
|
946
1184
|
`The number of lines to read (default: ${DEFAULT_LIMIT}). Only provide if the file is too large to read at once`
|
|
947
1185
|
)
|
|
948
1186
|
});
|
|
@@ -961,94 +1199,105 @@ function formatWithLineNumbers(lines, startLine) {
|
|
|
961
1199
|
return `${lineNum} ${line}`;
|
|
962
1200
|
}).join("\n");
|
|
963
1201
|
}
|
|
964
|
-
|
|
1202
|
+
function formatReadResult(filePath, content, startLine, limit) {
|
|
1203
|
+
const allLines = content.split("\n");
|
|
1204
|
+
if (allLines[allLines.length - 1] === "") {
|
|
1205
|
+
allLines.pop();
|
|
1206
|
+
}
|
|
1207
|
+
const zeroBasedStart = startLine - 1;
|
|
1208
|
+
const selectedLines = allLines.slice(zeroBasedStart, zeroBasedStart + limit);
|
|
1209
|
+
const output = formatWithLineNumbers(selectedLines, startLine);
|
|
1210
|
+
const totalLines = allLines.length;
|
|
1211
|
+
const returnedLines = selectedLines.length;
|
|
1212
|
+
const header = returnedLines < totalLines ? `[File: ${filePath} (lines ${startLine}-${startLine + returnedLines - 1} of ${totalLines})]
|
|
1213
|
+
` : `[File: ${filePath} (${totalLines} lines)]
|
|
1214
|
+
`;
|
|
1215
|
+
const result = {
|
|
1216
|
+
success: true,
|
|
1217
|
+
output: header + output
|
|
1218
|
+
};
|
|
1219
|
+
return JSON.stringify(result);
|
|
1220
|
+
}
|
|
1221
|
+
async function readFileTool(args, options = {}) {
|
|
965
1222
|
const { filePath, offset, limit = DEFAULT_LIMIT } = args;
|
|
966
1223
|
const startLine = offset !== void 0 && offset > 0 ? offset : 1;
|
|
1224
|
+
if (options.sandboxClient) {
|
|
1225
|
+
try {
|
|
1226
|
+
const content2 = await options.sandboxClient.readFile(filePath);
|
|
1227
|
+
return formatReadResult(filePath, content2, startLine, limit);
|
|
1228
|
+
} catch (err) {
|
|
1229
|
+
const result = {
|
|
1230
|
+
success: false,
|
|
1231
|
+
output: "",
|
|
1232
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1233
|
+
};
|
|
1234
|
+
return JSON.stringify(result);
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
967
1237
|
let fileStats;
|
|
968
1238
|
try {
|
|
969
|
-
fileStats = await
|
|
1239
|
+
fileStats = await promises.stat(filePath);
|
|
970
1240
|
} catch (err) {
|
|
971
|
-
const
|
|
1241
|
+
const result = {
|
|
972
1242
|
success: false,
|
|
973
1243
|
output: "",
|
|
974
1244
|
error: `File not found: ${filePath}`
|
|
975
1245
|
};
|
|
976
|
-
return JSON.stringify(
|
|
1246
|
+
return JSON.stringify(result);
|
|
977
1247
|
}
|
|
978
1248
|
if (!fileStats.isFile()) {
|
|
979
|
-
const
|
|
1249
|
+
const result = {
|
|
980
1250
|
success: false,
|
|
981
1251
|
output: "",
|
|
982
1252
|
error: `Path is not a file: ${filePath}`
|
|
983
1253
|
};
|
|
984
|
-
return JSON.stringify(
|
|
1254
|
+
return JSON.stringify(result);
|
|
985
1255
|
}
|
|
986
1256
|
let buffer;
|
|
987
1257
|
try {
|
|
988
|
-
buffer = await
|
|
1258
|
+
buffer = await promises.readFile(filePath);
|
|
989
1259
|
} catch (err) {
|
|
990
|
-
const
|
|
1260
|
+
const result = {
|
|
991
1261
|
success: false,
|
|
992
1262
|
output: "",
|
|
993
1263
|
error: err instanceof Error ? err.message : String(err)
|
|
994
1264
|
};
|
|
995
|
-
return JSON.stringify(
|
|
1265
|
+
return JSON.stringify(result);
|
|
996
1266
|
}
|
|
997
1267
|
if (isBinary(buffer)) {
|
|
998
|
-
const
|
|
1268
|
+
const result = {
|
|
999
1269
|
success: false,
|
|
1000
1270
|
output: "",
|
|
1001
1271
|
error: `Binary file not supported: ${filePath}`
|
|
1002
1272
|
};
|
|
1003
|
-
return JSON.stringify(
|
|
1273
|
+
return JSON.stringify(result);
|
|
1004
1274
|
}
|
|
1005
1275
|
const content = buffer.toString("utf8");
|
|
1006
|
-
|
|
1007
|
-
if (allLines[allLines.length - 1] === "") {
|
|
1008
|
-
allLines.pop();
|
|
1009
|
-
}
|
|
1010
|
-
const zeroBasedStart = startLine - 1;
|
|
1011
|
-
const selectedLines = allLines.slice(zeroBasedStart, zeroBasedStart + limit);
|
|
1012
|
-
const output = formatWithLineNumbers(selectedLines, startLine);
|
|
1013
|
-
const totalLines = allLines.length;
|
|
1014
|
-
const returnedLines = selectedLines.length;
|
|
1015
|
-
const header = returnedLines < totalLines ? `[File: ${filePath} (lines ${startLine}-${startLine + returnedLines - 1} of ${totalLines})]
|
|
1016
|
-
` : `[File: ${filePath} (${totalLines} lines)]
|
|
1017
|
-
`;
|
|
1018
|
-
const result = {
|
|
1019
|
-
success: true,
|
|
1020
|
-
output: header + output
|
|
1021
|
-
};
|
|
1022
|
-
return JSON.stringify(result);
|
|
1276
|
+
return formatReadResult(filePath, content, startLine, limit);
|
|
1023
1277
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
var
|
|
1035
|
-
|
|
1036
|
-
// src/builtins/atomic-file-write.ts
|
|
1037
|
-
var import_node_crypto = require("crypto");
|
|
1038
|
-
var import_promises2 = require("fs/promises");
|
|
1039
|
-
var import_node_path = require("path");
|
|
1278
|
+
function createReadTool(options = {}) {
|
|
1279
|
+
return createZodFunctionTool(
|
|
1280
|
+
"Read",
|
|
1281
|
+
"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.",
|
|
1282
|
+
ReadSchema,
|
|
1283
|
+
async (params) => {
|
|
1284
|
+
return readFileTool(params, options);
|
|
1285
|
+
}
|
|
1286
|
+
);
|
|
1287
|
+
}
|
|
1288
|
+
var readTool = createReadTool();
|
|
1040
1289
|
var TEMP_RANDOM_BYTES = 6;
|
|
1041
1290
|
var PRESERVED_MODE_BITS = 4095;
|
|
1042
1291
|
var MISSING_FILE_ERROR_CODE = "ENOENT";
|
|
1043
1292
|
function createTempFilePath(filePath) {
|
|
1044
|
-
const dir =
|
|
1045
|
-
const name =
|
|
1046
|
-
const suffix =
|
|
1047
|
-
return
|
|
1293
|
+
const dir = path.dirname(filePath);
|
|
1294
|
+
const name = path.basename(filePath);
|
|
1295
|
+
const suffix = crypto.randomBytes(TEMP_RANDOM_BYTES).toString("hex");
|
|
1296
|
+
return path.join(dir, `.${name}.robota-tmp-${process.pid}-${Date.now()}-${suffix}`);
|
|
1048
1297
|
}
|
|
1049
1298
|
async function readExistingMode(filePath) {
|
|
1050
1299
|
try {
|
|
1051
|
-
const fileStats = await
|
|
1300
|
+
const fileStats = await promises.stat(filePath);
|
|
1052
1301
|
return fileStats.mode & PRESERVED_MODE_BITS;
|
|
1053
1302
|
} catch (error) {
|
|
1054
1303
|
if (error instanceof Error && hasErrorCode(error, MISSING_FILE_ERROR_CODE)) return void 0;
|
|
@@ -1059,31 +1308,35 @@ function hasErrorCode(error, code) {
|
|
|
1059
1308
|
return "code" in error && error.code === code;
|
|
1060
1309
|
}
|
|
1061
1310
|
async function atomicWriteUtf8File(filePath, content) {
|
|
1062
|
-
const dir =
|
|
1063
|
-
await
|
|
1311
|
+
const dir = path.dirname(filePath);
|
|
1312
|
+
await promises.mkdir(dir, { recursive: true });
|
|
1064
1313
|
const existingMode = await readExistingMode(filePath);
|
|
1065
1314
|
const tempFilePath = createTempFilePath(filePath);
|
|
1066
1315
|
try {
|
|
1067
|
-
await
|
|
1316
|
+
await promises.writeFile(tempFilePath, content, "utf8");
|
|
1068
1317
|
if (existingMode !== void 0) {
|
|
1069
|
-
await
|
|
1318
|
+
await promises.chmod(tempFilePath, existingMode);
|
|
1070
1319
|
}
|
|
1071
|
-
await
|
|
1320
|
+
await promises.rename(tempFilePath, filePath);
|
|
1072
1321
|
} catch (error) {
|
|
1073
|
-
await
|
|
1322
|
+
await promises.rm(tempFilePath, { force: true }).catch(() => void 0);
|
|
1074
1323
|
throw error;
|
|
1075
1324
|
}
|
|
1076
1325
|
}
|
|
1077
1326
|
|
|
1078
1327
|
// src/builtins/write-tool.ts
|
|
1079
|
-
var WriteSchema =
|
|
1080
|
-
filePath:
|
|
1081
|
-
content:
|
|
1328
|
+
var WriteSchema = zod.z.object({
|
|
1329
|
+
filePath: zod.z.string().describe("The absolute path to the file to write"),
|
|
1330
|
+
content: zod.z.string().describe("The content to write to the file")
|
|
1082
1331
|
});
|
|
1083
|
-
async function writeFileTool(args) {
|
|
1332
|
+
async function writeFileTool(args, options = {}) {
|
|
1084
1333
|
const { filePath, content } = args;
|
|
1085
1334
|
try {
|
|
1086
|
-
|
|
1335
|
+
if (options.sandboxClient) {
|
|
1336
|
+
await options.sandboxClient.writeFile(filePath, content);
|
|
1337
|
+
} else {
|
|
1338
|
+
await atomicWriteUtf8File(filePath, content);
|
|
1339
|
+
}
|
|
1087
1340
|
const result = {
|
|
1088
1341
|
success: true,
|
|
1089
1342
|
output: `Written ${Buffer.byteLength(content, "utf8")} bytes to ${filePath}`
|
|
@@ -1098,31 +1351,30 @@ async function writeFileTool(args) {
|
|
|
1098
1351
|
return JSON.stringify(result);
|
|
1099
1352
|
}
|
|
1100
1353
|
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
var
|
|
1112
|
-
var
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
replaceAll: import_zod4.z.boolean().optional().describe(
|
|
1354
|
+
function createWriteTool(options = {}) {
|
|
1355
|
+
return createZodFunctionTool(
|
|
1356
|
+
"Write",
|
|
1357
|
+
"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.",
|
|
1358
|
+
WriteSchema,
|
|
1359
|
+
async (params) => {
|
|
1360
|
+
return writeFileTool(params, options);
|
|
1361
|
+
}
|
|
1362
|
+
);
|
|
1363
|
+
}
|
|
1364
|
+
var writeTool = createWriteTool();
|
|
1365
|
+
var EditSchema = zod.z.object({
|
|
1366
|
+
filePath: zod.z.string().describe("The absolute path to the file to modify"),
|
|
1367
|
+
oldString: zod.z.string().describe("The text to replace (must be an exact match of existing content)"),
|
|
1368
|
+
newString: zod.z.string().describe("The text to replace it with (must be different from old_string)"),
|
|
1369
|
+
replaceAll: zod.z.boolean().optional().describe(
|
|
1118
1370
|
"Replace all occurrences of old_string (default: false). Useful for renaming variables"
|
|
1119
1371
|
)
|
|
1120
1372
|
});
|
|
1121
|
-
async function editFileTool(args) {
|
|
1373
|
+
async function editFileTool(args, options = {}) {
|
|
1122
1374
|
const { filePath, oldString, newString, replaceAll = false } = args;
|
|
1123
1375
|
let content;
|
|
1124
1376
|
try {
|
|
1125
|
-
content = await (
|
|
1377
|
+
content = options.sandboxClient ? await options.sandboxClient.readFile(filePath) : await promises.readFile(filePath, "utf8");
|
|
1126
1378
|
} catch (err) {
|
|
1127
1379
|
const result2 = {
|
|
1128
1380
|
success: false,
|
|
@@ -1154,7 +1406,11 @@ async function editFileTool(args) {
|
|
|
1154
1406
|
}
|
|
1155
1407
|
const updated = replaceAll ? content.split(oldString).join(newString) : content.slice(0, content.indexOf(oldString)) + newString + content.slice(content.indexOf(oldString) + oldString.length);
|
|
1156
1408
|
try {
|
|
1157
|
-
|
|
1409
|
+
if (options.sandboxClient) {
|
|
1410
|
+
await options.sandboxClient.writeFile(filePath, updated);
|
|
1411
|
+
} else {
|
|
1412
|
+
await atomicWriteUtf8File(filePath, updated);
|
|
1413
|
+
}
|
|
1158
1414
|
} catch (err) {
|
|
1159
1415
|
const result2 = {
|
|
1160
1416
|
success: false,
|
|
@@ -1173,36 +1429,33 @@ async function editFileTool(args) {
|
|
|
1173
1429
|
};
|
|
1174
1430
|
return JSON.stringify(result);
|
|
1175
1431
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
var
|
|
1187
|
-
var import_node_path2 = require("path");
|
|
1188
|
-
var import_fast_glob = __toESM(require("fast-glob"), 1);
|
|
1189
|
-
var import_zod5 = require("zod");
|
|
1432
|
+
function createEditTool(options = {}) {
|
|
1433
|
+
return createZodFunctionTool(
|
|
1434
|
+
"Edit",
|
|
1435
|
+
"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.",
|
|
1436
|
+
EditSchema,
|
|
1437
|
+
async (params) => {
|
|
1438
|
+
return editFileTool(params, options);
|
|
1439
|
+
}
|
|
1440
|
+
);
|
|
1441
|
+
}
|
|
1442
|
+
var editTool = createEditTool();
|
|
1190
1443
|
var DEFAULT_MAX_RESULTS = 1e3;
|
|
1191
|
-
var GlobSchema =
|
|
1192
|
-
pattern:
|
|
1193
|
-
path:
|
|
1444
|
+
var GlobSchema = zod.z.object({
|
|
1445
|
+
pattern: zod.z.string().describe('The glob pattern to match files against (e.g. "**/*.ts", "src/**/*.tsx")'),
|
|
1446
|
+
path: zod.z.string().optional().describe(
|
|
1194
1447
|
"The directory to search in. Defaults to the current working directory. Must be a valid directory path if provided"
|
|
1195
1448
|
),
|
|
1196
|
-
limit:
|
|
1449
|
+
limit: zod.z.number().optional().describe(
|
|
1197
1450
|
"Maximum number of results to return (default: 1000). Use a smaller limit to save context space"
|
|
1198
1451
|
)
|
|
1199
1452
|
});
|
|
1200
1453
|
async function globFileTool(args) {
|
|
1201
1454
|
const { pattern, path: basePath } = args;
|
|
1202
|
-
const cwd = basePath ?
|
|
1455
|
+
const cwd = basePath ? path.resolve(basePath) : process.cwd();
|
|
1203
1456
|
let matches;
|
|
1204
1457
|
try {
|
|
1205
|
-
matches = await
|
|
1458
|
+
matches = await fg__default.default(pattern, {
|
|
1206
1459
|
cwd,
|
|
1207
1460
|
ignore: ["**/node_modules/**", "**/.git/**"],
|
|
1208
1461
|
dot: true,
|
|
@@ -1218,9 +1471,9 @@ async function globFileTool(args) {
|
|
|
1218
1471
|
}
|
|
1219
1472
|
const withMtime = await Promise.all(
|
|
1220
1473
|
matches.map(async (p) => {
|
|
1221
|
-
const absPath =
|
|
1474
|
+
const absPath = path.resolve(cwd, p);
|
|
1222
1475
|
try {
|
|
1223
|
-
const s = await
|
|
1476
|
+
const s = await promises.stat(absPath);
|
|
1224
1477
|
return { path: p, mtime: s.mtimeMs };
|
|
1225
1478
|
} catch {
|
|
1226
1479
|
return { path: p, mtime: 0 };
|
|
@@ -1253,21 +1506,16 @@ var globTool = createZodFunctionTool(
|
|
|
1253
1506
|
return globFileTool(params);
|
|
1254
1507
|
}
|
|
1255
1508
|
);
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
var import_zod6 = require("zod");
|
|
1261
|
-
var GrepSchema = import_zod6.z.object({
|
|
1262
|
-
pattern: import_zod6.z.string().describe("The regular expression pattern to search for in file contents"),
|
|
1263
|
-
path: import_zod6.z.string().optional().describe("File or directory to search in. Defaults to the current working directory"),
|
|
1264
|
-
glob: import_zod6.z.string().optional().describe(
|
|
1509
|
+
var GrepSchema = zod.z.object({
|
|
1510
|
+
pattern: zod.z.string().describe("The regular expression pattern to search for in file contents"),
|
|
1511
|
+
path: zod.z.string().optional().describe("File or directory to search in. Defaults to the current working directory"),
|
|
1512
|
+
glob: zod.z.string().optional().describe(
|
|
1265
1513
|
'Glob pattern to filter files (e.g. "*.ts", "*.{ts,tsx}"). Only files matching this pattern will be searched'
|
|
1266
1514
|
),
|
|
1267
|
-
contextLines:
|
|
1515
|
+
contextLines: zod.z.number().optional().describe(
|
|
1268
1516
|
'Number of context lines to show before and after each match. Only applies when outputMode is "content". Default: 0'
|
|
1269
1517
|
),
|
|
1270
|
-
outputMode:
|
|
1518
|
+
outputMode: zod.z.enum(["files_with_matches", "content"]).optional().describe(
|
|
1271
1519
|
'Output mode: "files_with_matches" shows only file paths (default), "content" shows matching lines with context'
|
|
1272
1520
|
)
|
|
1273
1521
|
});
|
|
@@ -1284,16 +1532,16 @@ async function collectFiles(dirPath, glob) {
|
|
|
1284
1532
|
async function walk(current) {
|
|
1285
1533
|
let entryNames;
|
|
1286
1534
|
try {
|
|
1287
|
-
entryNames = await
|
|
1535
|
+
entryNames = await promises.readdir(current);
|
|
1288
1536
|
} catch {
|
|
1289
1537
|
return;
|
|
1290
1538
|
}
|
|
1291
1539
|
for (const name of entryNames) {
|
|
1292
1540
|
if (name === "node_modules" || name === ".git") continue;
|
|
1293
|
-
const fullPath =
|
|
1541
|
+
const fullPath = path.join(current, name);
|
|
1294
1542
|
let fileStat;
|
|
1295
1543
|
try {
|
|
1296
|
-
fileStat = await
|
|
1544
|
+
fileStat = await promises.stat(fullPath);
|
|
1297
1545
|
} catch {
|
|
1298
1546
|
continue;
|
|
1299
1547
|
}
|
|
@@ -1349,7 +1597,7 @@ async function grepFileTool(args) {
|
|
|
1349
1597
|
contextLines = 0,
|
|
1350
1598
|
outputMode = "files_with_matches"
|
|
1351
1599
|
} = args;
|
|
1352
|
-
const targetPath = searchPath ?
|
|
1600
|
+
const targetPath = searchPath ? path.resolve(searchPath) : process.cwd();
|
|
1353
1601
|
let regex;
|
|
1354
1602
|
try {
|
|
1355
1603
|
regex = new RegExp(pattern);
|
|
@@ -1363,7 +1611,7 @@ async function grepFileTool(args) {
|
|
|
1363
1611
|
}
|
|
1364
1612
|
let targetStat;
|
|
1365
1613
|
try {
|
|
1366
|
-
targetStat = await
|
|
1614
|
+
targetStat = await promises.stat(targetPath);
|
|
1367
1615
|
} catch {
|
|
1368
1616
|
const result2 = {
|
|
1369
1617
|
success: false,
|
|
@@ -1382,7 +1630,7 @@ async function grepFileTool(args) {
|
|
|
1382
1630
|
for (const filePath of files) {
|
|
1383
1631
|
let content;
|
|
1384
1632
|
try {
|
|
1385
|
-
const buffer = await
|
|
1633
|
+
const buffer = await promises.readFile(filePath);
|
|
1386
1634
|
const checkLen = Math.min(buffer.length, 8192);
|
|
1387
1635
|
let hasBinary = false;
|
|
1388
1636
|
for (let i = 0; i < checkLen; i++) {
|
|
@@ -1413,14 +1661,11 @@ var grepTool = createZodFunctionTool(
|
|
|
1413
1661
|
return grepFileTool(params);
|
|
1414
1662
|
}
|
|
1415
1663
|
);
|
|
1416
|
-
|
|
1417
|
-
// src/builtins/web-fetch-tool.ts
|
|
1418
|
-
var import_zod7 = require("zod");
|
|
1419
1664
|
var DEFAULT_TIMEOUT_MS2 = 3e4;
|
|
1420
1665
|
var MAX_RESPONSE_BYTES = 5e6;
|
|
1421
|
-
var WebFetchSchema =
|
|
1422
|
-
url:
|
|
1423
|
-
headers:
|
|
1666
|
+
var WebFetchSchema = zod.z.object({
|
|
1667
|
+
url: zod.z.string().describe("The URL to fetch"),
|
|
1668
|
+
headers: zod.z.record(zod.z.string()).optional().describe("Optional HTTP headers as key-value pairs")
|
|
1424
1669
|
});
|
|
1425
1670
|
function htmlToText(html) {
|
|
1426
1671
|
return html.replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/ /g, " ").replace(/\s+/g, " ").trim();
|
|
@@ -1481,14 +1726,11 @@ var webFetchTool = createZodFunctionTool(
|
|
|
1481
1726
|
WebFetchSchema,
|
|
1482
1727
|
async (params) => runWebFetch(params)
|
|
1483
1728
|
);
|
|
1484
|
-
|
|
1485
|
-
// src/builtins/web-search-tool.ts
|
|
1486
|
-
var import_zod8 = require("zod");
|
|
1487
1729
|
var DEFAULT_LIMIT2 = 10;
|
|
1488
1730
|
var DEFAULT_TIMEOUT_MS3 = 15e3;
|
|
1489
|
-
var WebSearchSchema =
|
|
1490
|
-
query:
|
|
1491
|
-
limit:
|
|
1731
|
+
var WebSearchSchema = zod.z.object({
|
|
1732
|
+
query: zod.z.string().describe("The search query"),
|
|
1733
|
+
limit: zod.z.number().optional().describe(`Maximum number of results to return (default: ${DEFAULT_LIMIT2})`)
|
|
1492
1734
|
});
|
|
1493
1735
|
async function runWebSearch(args) {
|
|
1494
1736
|
const { query, limit = DEFAULT_LIMIT2 } = args;
|
|
@@ -1545,21 +1787,27 @@ var webSearchTool = createZodFunctionTool(
|
|
|
1545
1787
|
WebSearchSchema,
|
|
1546
1788
|
async (params) => runWebSearch(params)
|
|
1547
1789
|
);
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1790
|
+
|
|
1791
|
+
exports.E2BSandboxClient = E2BSandboxClient;
|
|
1792
|
+
exports.FunctionTool = FunctionTool;
|
|
1793
|
+
exports.InMemorySandboxClient = InMemorySandboxClient;
|
|
1794
|
+
exports.OpenAPITool = OpenAPITool;
|
|
1795
|
+
exports.ToolRegistry = ToolRegistry;
|
|
1796
|
+
exports.applyWorkspaceManifest = applyWorkspaceManifest;
|
|
1797
|
+
exports.bashTool = bashTool;
|
|
1798
|
+
exports.createBashTool = createBashTool;
|
|
1799
|
+
exports.createEditTool = createEditTool;
|
|
1800
|
+
exports.createFunctionTool = createFunctionTool;
|
|
1801
|
+
exports.createOpenAPITool = createOpenAPITool;
|
|
1802
|
+
exports.createReadTool = createReadTool;
|
|
1803
|
+
exports.createWriteTool = createWriteTool;
|
|
1804
|
+
exports.createZodFunctionTool = createZodFunctionTool;
|
|
1805
|
+
exports.editTool = editTool;
|
|
1806
|
+
exports.globTool = globTool;
|
|
1807
|
+
exports.grepTool = grepTool;
|
|
1808
|
+
exports.readTool = readTool;
|
|
1809
|
+
exports.validateWorkspaceManifestPath = validateWorkspaceManifestPath;
|
|
1810
|
+
exports.webFetchTool = webFetchTool;
|
|
1811
|
+
exports.webSearchTool = webSearchTool;
|
|
1812
|
+
exports.writeTool = writeTool;
|
|
1813
|
+
exports.zodToJsonSchema = zodToJsonSchema;
|