agentenkit 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -1
- package/dist/adapters/brave.d.ts +21 -0
- package/dist/adapters/brave.d.ts.map +1 -0
- package/dist/adapters/brave.js +52 -0
- package/dist/adapters/brave.js.map +1 -0
- package/dist/adapters/computesdk.d.ts +71 -0
- package/dist/adapters/computesdk.d.ts.map +1 -0
- package/dist/adapters/computesdk.js +163 -0
- package/dist/adapters/computesdk.js.map +1 -0
- package/dist/adapters/docker.d.ts +35 -0
- package/dist/adapters/docker.d.ts.map +1 -0
- package/dist/adapters/docker.js +246 -0
- package/dist/adapters/docker.js.map +1 -0
- package/dist/adapters/e2b.d.ts +44 -0
- package/dist/adapters/e2b.d.ts.map +1 -0
- package/dist/adapters/e2b.js +349 -0
- package/dist/adapters/e2b.js.map +1 -0
- package/dist/adapters/jina.d.ts +34 -0
- package/dist/adapters/jina.d.ts.map +1 -0
- package/dist/adapters/jina.js +99 -0
- package/dist/adapters/jina.js.map +1 -0
- package/dist/adapters/local-sandbox.d.ts +32 -0
- package/dist/adapters/local-sandbox.d.ts.map +1 -0
- package/dist/adapters/local-sandbox.js +257 -0
- package/dist/adapters/local-sandbox.js.map +1 -0
- package/dist/adapters/memory.d.ts +81 -0
- package/dist/adapters/memory.d.ts.map +1 -1
- package/dist/adapters/memory.js +207 -0
- package/dist/adapters/memory.js.map +1 -1
- package/dist/adapters/page-reader.d.ts +39 -0
- package/dist/adapters/page-reader.d.ts.map +1 -0
- package/dist/adapters/page-reader.js +165 -0
- package/dist/adapters/page-reader.js.map +1 -0
- package/dist/adapters/sandbox-shell.d.ts +60 -0
- package/dist/adapters/sandbox-shell.d.ts.map +1 -0
- package/dist/adapters/sandbox-shell.js +129 -0
- package/dist/adapters/sandbox-shell.js.map +1 -0
- package/dist/adapters/sqlite.d.ts +37 -1
- package/dist/adapters/sqlite.d.ts.map +1 -1
- package/dist/adapters/sqlite.js +106 -7
- package/dist/adapters/sqlite.js.map +1 -1
- package/dist/adapters/web-search-shared.d.ts +11 -0
- package/dist/adapters/web-search-shared.d.ts.map +1 -0
- package/dist/adapters/web-search-shared.js +40 -0
- package/dist/adapters/web-search-shared.js.map +1 -0
- package/dist/core/builtin/definitions.d.ts +12 -0
- package/dist/core/builtin/definitions.d.ts.map +1 -0
- package/dist/core/builtin/definitions.js +149 -0
- package/dist/core/builtin/definitions.js.map +1 -0
- package/dist/core/builtin/html.d.ts +13 -0
- package/dist/core/builtin/html.d.ts.map +1 -0
- package/dist/core/builtin/html.js +96 -0
- package/dist/core/builtin/html.js.map +1 -0
- package/dist/core/builtin/index.d.ts +21 -0
- package/dist/core/builtin/index.d.ts.map +1 -0
- package/dist/core/builtin/index.js +55 -0
- package/dist/core/builtin/index.js.map +1 -0
- package/dist/core/builtin/run.d.ts +37 -0
- package/dist/core/builtin/run.d.ts.map +1 -0
- package/dist/core/builtin/run.js +55 -0
- package/dist/core/builtin/run.js.map +1 -0
- package/dist/core/builtin/sandbox-tools.d.ts +69 -0
- package/dist/core/builtin/sandbox-tools.d.ts.map +1 -0
- package/dist/core/builtin/sandbox-tools.js +389 -0
- package/dist/core/builtin/sandbox-tools.js.map +1 -0
- package/dist/core/builtin/sandbox.d.ts +52 -0
- package/dist/core/builtin/sandbox.d.ts.map +1 -0
- package/dist/core/builtin/sandbox.js +174 -0
- package/dist/core/builtin/sandbox.js.map +1 -0
- package/dist/core/builtin/web.d.ts +54 -0
- package/dist/core/builtin/web.d.ts.map +1 -0
- package/dist/core/builtin/web.js +167 -0
- package/dist/core/builtin/web.js.map +1 -0
- package/dist/core/deleteThread.d.ts.map +1 -1
- package/dist/core/deleteThread.js +3 -0
- package/dist/core/deleteThread.js.map +1 -1
- package/dist/core/engine.d.ts.map +1 -1
- package/dist/core/engine.js +29 -5
- package/dist/core/engine.js.map +1 -1
- package/dist/core/permanent.d.ts +11 -0
- package/dist/core/permanent.d.ts.map +1 -0
- package/dist/core/permanent.js +37 -0
- package/dist/core/permanent.js.map +1 -0
- package/dist/core/subagent.d.ts.map +1 -1
- package/dist/core/subagent.js +13 -3
- package/dist/core/subagent.js.map +1 -1
- package/dist/core/tools.d.ts +9 -3
- package/dist/core/tools.d.ts.map +1 -1
- package/dist/core/tools.js.map +1 -1
- package/dist/core/types.d.ts +17 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +4 -1
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +14 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/ports/runtime.d.ts +17 -0
- package/dist/ports/runtime.d.ts.map +1 -1
- package/dist/ports/sandbox.d.ts +159 -0
- package/dist/ports/sandbox.d.ts.map +1 -0
- package/dist/ports/sandbox.js +44 -0
- package/dist/ports/sandbox.js.map +1 -0
- package/dist/ports/tools.d.ts +60 -0
- package/dist/ports/tools.d.ts.map +1 -0
- package/dist/ports/tools.js +5 -0
- package/dist/ports/tools.js.map +1 -0
- package/dist/pricing.d.ts +24 -0
- package/dist/pricing.d.ts.map +1 -1
- package/dist/pricing.js +29 -0
- package/dist/pricing.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +3 -0
- package/dist/runtime.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { SandboxFileNotFoundError } from '../../ports/sandbox.js';
|
|
3
|
+
import { parkForInput } from '../hitl.js';
|
|
4
|
+
import { publishEvent } from '../publish.js';
|
|
5
|
+
import { recordToolUsage, toolUseRow } from './run.js';
|
|
6
|
+
import { withThreadSandbox } from './sandbox.js';
|
|
7
|
+
/** Where the tools keep their own files, in the sandbox's work folder: the
|
|
8
|
+
* bash folder, the programs code_execution ran, the editor's undo history.
|
|
9
|
+
* Hidden, so a plain `ls` does not show it. */
|
|
10
|
+
const STATE_DIR = '.agentenkit';
|
|
11
|
+
/** Changes the editor can take back, per file. */
|
|
12
|
+
const UNDO_DEPTH = 10;
|
|
13
|
+
const LOST_NOTE = "The sandbox this conversation used before has ended, so files from earlier are gone. This is a fresh one.";
|
|
14
|
+
const failed = (error, note) => (note ? { error, note } : { error });
|
|
15
|
+
/** Count one use and say whether it is over the run's limit. */
|
|
16
|
+
async function overLimit(run, tool, maxUses) {
|
|
17
|
+
if (!maxUses)
|
|
18
|
+
return false;
|
|
19
|
+
const scope = run.runId ?? run.threadId;
|
|
20
|
+
const ttl = Math.ceil(run.deps.config.hitlTtlMs / 1000) + 24 * 60 * 60;
|
|
21
|
+
return (await run.deps.kv.incrWithExpiry(`agent:tool:uses:${scope}:${tool}`, ttl)) > maxUses;
|
|
22
|
+
}
|
|
23
|
+
/** Ask for approval when the rule says so and this call is not already
|
|
24
|
+
* the approved one coming back. */
|
|
25
|
+
async function needsApproval(rule, input, opts) {
|
|
26
|
+
if (opts.approval)
|
|
27
|
+
return false;
|
|
28
|
+
return typeof rule === 'function' ? Boolean(await rule(input)) : rule;
|
|
29
|
+
}
|
|
30
|
+
/** Text over `max` UTF-16 units keeps its start and its end, with a line
|
|
31
|
+
* saying how much was cut from the middle: the start says what ran, the end
|
|
32
|
+
* holds the error. Never splits a character. The Go runtime cuts the same. */
|
|
33
|
+
export function cutMiddle(text, max) {
|
|
34
|
+
if (text.length <= max)
|
|
35
|
+
return { text, cut: false };
|
|
36
|
+
let head = Math.floor(max / 2);
|
|
37
|
+
let tail = text.length - (max - head);
|
|
38
|
+
if (isHigh(text.charCodeAt(head - 1)))
|
|
39
|
+
head--;
|
|
40
|
+
if (isLow(text.charCodeAt(tail)))
|
|
41
|
+
tail++;
|
|
42
|
+
return { text: `${text.slice(0, head)}\n… ${tail - head} characters cut …\n${text.slice(tail)}`, cut: true };
|
|
43
|
+
}
|
|
44
|
+
const isHigh = (c) => c >= 0xd800 && c <= 0xdbff;
|
|
45
|
+
const isLow = (c) => c >= 0xdc00 && c <= 0xdfff;
|
|
46
|
+
/** Output as it comes, on the thread as live-only `tool.output` events, a
|
|
47
|
+
* few at a time so a chatty command does not flood the bus. */
|
|
48
|
+
function liveOutput(run, tool, toolCallId) {
|
|
49
|
+
let buf = [];
|
|
50
|
+
let timer;
|
|
51
|
+
let chain = Promise.resolve();
|
|
52
|
+
const flush = () => {
|
|
53
|
+
timer = undefined;
|
|
54
|
+
if (buf.length === 0)
|
|
55
|
+
return;
|
|
56
|
+
const parts = buf;
|
|
57
|
+
buf = [];
|
|
58
|
+
for (const stream of ['stdout', 'stderr']) {
|
|
59
|
+
const text = parts.filter((p) => p.stream === stream).map((p) => p.text).join('');
|
|
60
|
+
if (!text)
|
|
61
|
+
continue;
|
|
62
|
+
const payload = { toolCallId: toolCallId ?? null, tool, stream, text, ...(run.agentId ? { agentId: run.agentId } : {}) };
|
|
63
|
+
chain = chain.then(() => publishEvent(run.deps, run.threadId, 'tool.output', payload)).catch(() => undefined);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const push = (stream) => (text) => {
|
|
67
|
+
buf.push({ stream, text });
|
|
68
|
+
timer ??= setTimeout(flush, 100);
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
onStdout: push('stdout'),
|
|
72
|
+
onStderr: push('stderr'),
|
|
73
|
+
done: async () => {
|
|
74
|
+
if (timer)
|
|
75
|
+
clearTimeout(timer);
|
|
76
|
+
flush();
|
|
77
|
+
await chain;
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function capOutput(run, r) {
|
|
82
|
+
const max = run.deps.config.builtinToolResultCapChars;
|
|
83
|
+
const out = cutMiddle(r.stdout, max);
|
|
84
|
+
const err = cutMiddle(r.stderr, max);
|
|
85
|
+
return { stdout: out.text, stderr: err.text, truncated: Boolean(r.truncated || out.cut || err.cut) };
|
|
86
|
+
}
|
|
87
|
+
const seconds = (ms) => Math.round(ms) / 1000;
|
|
88
|
+
// --- bash -----------------------------------------------------------------
|
|
89
|
+
/** The folder a bash call ends in is kept in the sandbox, and the next call
|
|
90
|
+
* starts there: the "session" the model expects, without a shell that has
|
|
91
|
+
* to outlive the process. */
|
|
92
|
+
function bashScript(command, restart) {
|
|
93
|
+
return [
|
|
94
|
+
`__ak="$PWD/${STATE_DIR}"; mkdir -p "$__ak"`,
|
|
95
|
+
...(restart ? ['rm -f "$__ak/bash-cwd"'] : []),
|
|
96
|
+
`if [ -f "$__ak/bash-cwd" ]; then cd -- "$(cat "$__ak/bash-cwd")" 2>/dev/null; fi`,
|
|
97
|
+
`trap 'pwd > "$__ak/bash-cwd"' EXIT`,
|
|
98
|
+
command,
|
|
99
|
+
// Saved again here, for a command that set its own EXIT trap in place
|
|
100
|
+
// of ours; the trap still covers one that calls exit.
|
|
101
|
+
'__ec=$?',
|
|
102
|
+
'pwd > "$__ak/bash-cwd"',
|
|
103
|
+
'exit $__ec',
|
|
104
|
+
].join('\n');
|
|
105
|
+
}
|
|
106
|
+
export async function runBash(options, input, run, opts) {
|
|
107
|
+
const command = typeof input.command === 'string' ? input.command : '';
|
|
108
|
+
const restart = input.restart === true;
|
|
109
|
+
if (!command.trim() && !restart)
|
|
110
|
+
return failed('bash: command is required');
|
|
111
|
+
if (await needsApproval(options.approval ?? true, input, opts))
|
|
112
|
+
throw parkForInput({ payload: input });
|
|
113
|
+
if (await overLimit(run, 'bash', options.maxUses))
|
|
114
|
+
return failed(`bash: this run has used its ${options.maxUses} commands`);
|
|
115
|
+
const live = liveOutput(run, 'bash', opts.toolCallId);
|
|
116
|
+
try {
|
|
117
|
+
return await withThreadSandbox(run, async ({ sandbox, lost }) => {
|
|
118
|
+
const r = await sandbox.runCommand(bashScript(command || ':', restart), {
|
|
119
|
+
timeoutMs: options.timeoutMs ?? 120_000,
|
|
120
|
+
onStdout: live.onStdout,
|
|
121
|
+
onStderr: live.onStderr,
|
|
122
|
+
...(opts.abortSignal ? { signal: opts.abortSignal } : {}),
|
|
123
|
+
});
|
|
124
|
+
await recordToolUsage(run, toolUseRow('bash', sandbox.provider, 1, seconds(r.durationMs)));
|
|
125
|
+
const out = capOutput(run, r);
|
|
126
|
+
return {
|
|
127
|
+
stdout: out.stdout,
|
|
128
|
+
stderr: out.stderr,
|
|
129
|
+
exitCode: r.exitCode,
|
|
130
|
+
...(out.truncated ? { truncated: true } : {}),
|
|
131
|
+
...(r.timedOut ? { timedOut: true, error: `bash: the command was stopped after ${seconds(options.timeoutMs ?? 120_000)} s` } : {}),
|
|
132
|
+
...(lost ? { note: LOST_NOTE } : {}),
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
await live.done();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// --- code_execution -------------------------------------------------------
|
|
141
|
+
const RUNNERS = {
|
|
142
|
+
// No __pycache__, which would show up among the files the program made;
|
|
143
|
+
// -u sends the output as it prints, not when it ends, so it streams live.
|
|
144
|
+
python: { ext: 'py', run: 'PYTHONDONTWRITEBYTECODE=1 python3 -u' },
|
|
145
|
+
javascript: { ext: 'js', run: 'node' },
|
|
146
|
+
};
|
|
147
|
+
/** The media type of a file the program made, from its name. */
|
|
148
|
+
export function mediaTypeOf(path) {
|
|
149
|
+
const ext = path.slice(path.lastIndexOf('.') + 1).toLowerCase();
|
|
150
|
+
return {
|
|
151
|
+
png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', webp: 'image/webp',
|
|
152
|
+
svg: 'image/svg+xml', pdf: 'application/pdf', csv: 'text/csv', json: 'application/json',
|
|
153
|
+
txt: 'text/plain', md: 'text/markdown', html: 'text/html',
|
|
154
|
+
}[ext] ?? 'application/octet-stream';
|
|
155
|
+
}
|
|
156
|
+
export async function runCodeExecution(options, input, run, opts) {
|
|
157
|
+
const code = typeof input.code === 'string' ? input.code : '';
|
|
158
|
+
if (!code.trim())
|
|
159
|
+
return failed('code_execution: code is required');
|
|
160
|
+
const language = input.language ?? 'python';
|
|
161
|
+
const runner = RUNNERS[language];
|
|
162
|
+
if (!runner)
|
|
163
|
+
return failed(`code_execution: language must be python or javascript, not ${JSON.stringify(language)}`);
|
|
164
|
+
if (await needsApproval(options.approval ?? false, input, opts))
|
|
165
|
+
throw parkForInput({ payload: input });
|
|
166
|
+
if (await overLimit(run, 'code_execution', options.maxUses)) {
|
|
167
|
+
return failed(`code_execution: this run has used its ${options.maxUses} runs`);
|
|
168
|
+
}
|
|
169
|
+
const limit = options.timeoutMs ?? 60_000;
|
|
170
|
+
const live = liveOutput(run, 'code_execution', opts.toolCallId);
|
|
171
|
+
try {
|
|
172
|
+
return await withThreadSandbox(run, async ({ sandbox, lost }) => {
|
|
173
|
+
const name = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
174
|
+
const file = `${STATE_DIR}/code/${name}.${runner.ext}`;
|
|
175
|
+
await sandbox.filesystem.writeFile(file, code);
|
|
176
|
+
// A marker made just before the run: the files newer than it are the
|
|
177
|
+
// ones the program made or changed.
|
|
178
|
+
// The program is read from stdin, so its imports (Python) and relative
|
|
179
|
+
// requires (Node) resolve from the work folder, not the hidden one.
|
|
180
|
+
const script = [
|
|
181
|
+
`touch "${STATE_DIR}/code/${name}.start"`,
|
|
182
|
+
`${runner.run} - < "${file}"`,
|
|
183
|
+
'__ec=$?',
|
|
184
|
+
`find . -path "./${STATE_DIR}" -prune -o -type f -newer "${STATE_DIR}/code/${name}.start" -print > "${STATE_DIR}/code/${name}.files" 2>/dev/null`,
|
|
185
|
+
'exit $__ec',
|
|
186
|
+
].join('\n');
|
|
187
|
+
const r = await sandbox.runCommand(script, {
|
|
188
|
+
timeoutMs: limit,
|
|
189
|
+
onStdout: live.onStdout,
|
|
190
|
+
onStderr: live.onStderr,
|
|
191
|
+
...(opts.abortSignal ? { signal: opts.abortSignal } : {}),
|
|
192
|
+
});
|
|
193
|
+
await recordToolUsage(run, toolUseRow('code_execution', sandbox.provider, 1, seconds(r.durationMs)));
|
|
194
|
+
const listed = await sandbox.filesystem.readFile(`${STATE_DIR}/code/${name}.files`).catch(() => '');
|
|
195
|
+
const files = listed
|
|
196
|
+
.split('\n')
|
|
197
|
+
.map((l) => l.replace(/^\.\//, ''))
|
|
198
|
+
.filter(Boolean)
|
|
199
|
+
.sort()
|
|
200
|
+
.map((path) => ({ path, mediaType: mediaTypeOf(path) }));
|
|
201
|
+
const out = capOutput(run, r);
|
|
202
|
+
const error = r.timedOut
|
|
203
|
+
? `code_execution: the program was stopped after ${seconds(limit)} s`
|
|
204
|
+
: r.exitCode !== 0
|
|
205
|
+
? `code_execution: the program exited with code ${r.exitCode}`
|
|
206
|
+
: undefined;
|
|
207
|
+
return {
|
|
208
|
+
stdout: out.stdout,
|
|
209
|
+
stderr: out.stderr,
|
|
210
|
+
exitCode: r.exitCode,
|
|
211
|
+
...(error ? { error } : {}),
|
|
212
|
+
files,
|
|
213
|
+
...(out.truncated ? { truncated: true } : {}),
|
|
214
|
+
...(lost ? { note: LOST_NOTE } : {}),
|
|
215
|
+
};
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
finally {
|
|
219
|
+
await live.done();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// --- text_editor ----------------------------------------------------------
|
|
223
|
+
const EDITS = new Set(['create', 'str_replace', 'insert', 'undo_edit']);
|
|
224
|
+
/** The editor's undo history for one file: earlier versions, newest last;
|
|
225
|
+
* null where the file did not exist yet. */
|
|
226
|
+
const historyPath = (path) => `${STATE_DIR}/history/${createHash('sha256').update(path).digest('hex').slice(0, 32)}.json`;
|
|
227
|
+
async function readHistory(sandbox, path) {
|
|
228
|
+
try {
|
|
229
|
+
return JSON.parse(await sandbox.filesystem.readFile(historyPath(path)));
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return [];
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
async function remember(sandbox, path, before) {
|
|
236
|
+
const history = [...(await readHistory(sandbox, path)), before].slice(-UNDO_DEPTH);
|
|
237
|
+
await sandbox.filesystem.writeFile(historyPath(path), JSON.stringify(history));
|
|
238
|
+
}
|
|
239
|
+
async function readOrNull(sandbox, path) {
|
|
240
|
+
try {
|
|
241
|
+
return await sandbox.filesystem.readFile(path);
|
|
242
|
+
}
|
|
243
|
+
catch (e) {
|
|
244
|
+
if (e instanceof SandboxFileNotFoundError)
|
|
245
|
+
return null;
|
|
246
|
+
throw e;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/** Lines of `text` numbered from `from`, as `cat -n` shows them. */
|
|
250
|
+
function numbered(lines, from) {
|
|
251
|
+
return lines.map((l, i) => `${String(from + i).padStart(6)}\t${l}`).join('\n');
|
|
252
|
+
}
|
|
253
|
+
/** A folder's contents two levels deep, hidden entries left out; null when
|
|
254
|
+
* `path` is not a folder. */
|
|
255
|
+
async function listFolder(sandbox, path) {
|
|
256
|
+
try {
|
|
257
|
+
await sandbox.filesystem.readdir(path);
|
|
258
|
+
}
|
|
259
|
+
catch (e) {
|
|
260
|
+
if (e instanceof SandboxFileNotFoundError)
|
|
261
|
+
return null;
|
|
262
|
+
throw e;
|
|
263
|
+
}
|
|
264
|
+
const out = [];
|
|
265
|
+
const walk = async (dir, prefix, depth) => {
|
|
266
|
+
let entries;
|
|
267
|
+
try {
|
|
268
|
+
entries = await sandbox.filesystem.readdir(dir);
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
for (const e of entries) {
|
|
274
|
+
if (e.name.startsWith('.'))
|
|
275
|
+
continue;
|
|
276
|
+
out.push(`${prefix}${e.name}${e.type === 'directory' ? '/' : ''}`);
|
|
277
|
+
if (e.type === 'directory' && depth < 2)
|
|
278
|
+
await walk(`${dir}/${e.name}`, `${prefix}${e.name}/`, depth + 1);
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
await walk(path, '', 1);
|
|
282
|
+
return out.join('\n');
|
|
283
|
+
}
|
|
284
|
+
const count = (text, part) => {
|
|
285
|
+
let n = 0;
|
|
286
|
+
for (let i = text.indexOf(part); i !== -1; i = text.indexOf(part, i + part.length))
|
|
287
|
+
n++;
|
|
288
|
+
return n;
|
|
289
|
+
};
|
|
290
|
+
async function edit(sandbox, run, input) {
|
|
291
|
+
const path = input.path;
|
|
292
|
+
const max = run.deps.config.builtinToolResultCapChars;
|
|
293
|
+
switch (input.command) {
|
|
294
|
+
case 'view': {
|
|
295
|
+
// A folder first: reading one as a file fails differently on each
|
|
296
|
+
// adapter, listing a file fails the same on all.
|
|
297
|
+
const listing = await listFolder(sandbox, path);
|
|
298
|
+
if (listing !== null) {
|
|
299
|
+
const shown = cutMiddle(listing, max);
|
|
300
|
+
return { path, content: shown.text, ...(shown.cut ? { truncated: true } : {}) };
|
|
301
|
+
}
|
|
302
|
+
const text = await readOrNull(sandbox, path);
|
|
303
|
+
if (text === null)
|
|
304
|
+
return failed(`text_editor: ${path} does not exist`);
|
|
305
|
+
const lines = text.split('\n');
|
|
306
|
+
let [from, to] = input.view_range ?? [1, -1];
|
|
307
|
+
if (to === -1 || to > lines.length)
|
|
308
|
+
to = lines.length;
|
|
309
|
+
if (!Number.isInteger(from) || !Number.isInteger(to) || from < 1 || from > Math.max(lines.length, 1) || to < from) {
|
|
310
|
+
return failed(`text_editor: view_range must be two line numbers within 1 and ${lines.length}`);
|
|
311
|
+
}
|
|
312
|
+
const shown = cutMiddle(numbered(lines.slice(from - 1, to), from), max);
|
|
313
|
+
return { path, content: shown.text, totalLines: lines.length, ...(shown.cut ? { truncated: true } : {}) };
|
|
314
|
+
}
|
|
315
|
+
case 'create': {
|
|
316
|
+
if (typeof input.file_text !== 'string')
|
|
317
|
+
return failed('text_editor: create needs file_text');
|
|
318
|
+
const before = await readOrNull(sandbox, path);
|
|
319
|
+
await remember(sandbox, path, before);
|
|
320
|
+
await sandbox.filesystem.writeFile(path, input.file_text);
|
|
321
|
+
return { ok: true, message: `${before === null ? 'Created' : 'Replaced'} ${path}.` };
|
|
322
|
+
}
|
|
323
|
+
case 'str_replace': {
|
|
324
|
+
if (typeof input.old_str !== 'string' || input.old_str === '')
|
|
325
|
+
return failed('text_editor: str_replace needs old_str');
|
|
326
|
+
const text = await readOrNull(sandbox, path);
|
|
327
|
+
if (text === null)
|
|
328
|
+
return failed(`text_editor: ${path} does not exist`);
|
|
329
|
+
const n = count(text, input.old_str);
|
|
330
|
+
if (n === 0)
|
|
331
|
+
return failed(`text_editor: old_str was not found in ${path}; it must match exactly, whitespace included`);
|
|
332
|
+
if (n > 1)
|
|
333
|
+
return failed(`text_editor: old_str matches ${n} places in ${path}; include more of the text around it so it matches one`);
|
|
334
|
+
await remember(sandbox, path, text);
|
|
335
|
+
const i = text.indexOf(input.old_str);
|
|
336
|
+
await sandbox.filesystem.writeFile(path, text.slice(0, i) + (input.new_str ?? '') + text.slice(i + input.old_str.length));
|
|
337
|
+
return { ok: true, message: `Replaced 1 place in ${path}.` };
|
|
338
|
+
}
|
|
339
|
+
case 'insert': {
|
|
340
|
+
if (typeof input.new_str !== 'string')
|
|
341
|
+
return failed('text_editor: insert needs new_str');
|
|
342
|
+
const text = await readOrNull(sandbox, path);
|
|
343
|
+
if (text === null)
|
|
344
|
+
return failed(`text_editor: ${path} does not exist`);
|
|
345
|
+
const lines = text.split('\n');
|
|
346
|
+
const at = input.insert_line;
|
|
347
|
+
if (!Number.isInteger(at) || at < 0 || at > lines.length) {
|
|
348
|
+
return failed(`text_editor: insert_line must be within 0 and ${lines.length}`);
|
|
349
|
+
}
|
|
350
|
+
await remember(sandbox, path, text);
|
|
351
|
+
lines.splice(at, 0, ...input.new_str.split('\n'));
|
|
352
|
+
await sandbox.filesystem.writeFile(path, lines.join('\n'));
|
|
353
|
+
return { ok: true, message: `Inserted after line ${at} of ${path}.` };
|
|
354
|
+
}
|
|
355
|
+
case 'undo_edit': {
|
|
356
|
+
const history = await readHistory(sandbox, path);
|
|
357
|
+
if (history.length === 0)
|
|
358
|
+
return failed(`text_editor: there is no change to ${path} to undo`);
|
|
359
|
+
const before = history.pop();
|
|
360
|
+
if (before === null)
|
|
361
|
+
await sandbox.filesystem.remove(path);
|
|
362
|
+
else
|
|
363
|
+
await sandbox.filesystem.writeFile(path, before);
|
|
364
|
+
await sandbox.filesystem.writeFile(historyPath(path), JSON.stringify(history));
|
|
365
|
+
return { ok: true, message: before === null ? `Removed ${path}, which the change had created.` : `Undid the last change to ${path}.` };
|
|
366
|
+
}
|
|
367
|
+
default:
|
|
368
|
+
return failed('text_editor: command must be view, create, str_replace, insert or undo_edit');
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
export async function runTextEditor(options, input, run, opts) {
|
|
372
|
+
if (typeof input.command !== 'string' || !['view', ...EDITS].includes(input.command)) {
|
|
373
|
+
return failed('text_editor: command must be view, create, str_replace, insert or undo_edit');
|
|
374
|
+
}
|
|
375
|
+
if (typeof input.path !== 'string' || !input.path.trim())
|
|
376
|
+
return failed('text_editor: path is required');
|
|
377
|
+
const rule = options.approval ?? ((i) => EDITS.has(i.command ?? ''));
|
|
378
|
+
if (await needsApproval(rule, input, opts))
|
|
379
|
+
throw parkForInput({ payload: input });
|
|
380
|
+
if (await overLimit(run, 'text_editor', options.maxUses)) {
|
|
381
|
+
return failed(`text_editor: this run has used its ${options.maxUses} edits and views`);
|
|
382
|
+
}
|
|
383
|
+
return withThreadSandbox(run, async ({ sandbox, lost }) => {
|
|
384
|
+
const result = await edit(sandbox, run, input);
|
|
385
|
+
await recordToolUsage(run, toolUseRow('text_editor', sandbox.provider, 1, 0));
|
|
386
|
+
return lost ? { ...result, note: LOST_NOTE } : result;
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
//# sourceMappingURL=sandbox-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox-tools.js","sourceRoot":"","sources":["../../../src/core/builtin/sandbox-tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { RuntimePorts } from '../../ports/runtime.js';
|
|
2
|
+
import { type Sandbox } from '../../ports/sandbox.js';
|
|
3
|
+
import { type ToolRun } from './run.js';
|
|
4
|
+
/** One sandbox per thread, kept between messages.
|
|
5
|
+
*
|
|
6
|
+
* The first sandbox call in a thread makes it and keeps its id in the kv
|
|
7
|
+
* under the thread. Every later call, in this run or the next (a new
|
|
8
|
+
* message, a resume after an approval, a retry, another worker), connects to
|
|
9
|
+
* the same one, so files stay. The run lock means one run at a time uses it;
|
|
10
|
+
* a run's subagents share it, as they share the thread.
|
|
11
|
+
*
|
|
12
|
+
* It ends when the thread is deleted, when it has sat idle past
|
|
13
|
+
* `sandboxIdleTtlMs`, or at `sandboxMaxLifetimeMs` after it was made. Each
|
|
14
|
+
* use pushes the sandbox's own timeout back, so an idle one shuts itself
|
|
15
|
+
* down even when no app is running to end it. The next call then makes a
|
|
16
|
+
* fresh one and says the old files are lost. The Go runtime does the same
|
|
17
|
+
* (core/sandbox.go). */
|
|
18
|
+
export interface ThreadSandbox {
|
|
19
|
+
sandbox: Sandbox;
|
|
20
|
+
/** True when this call made the sandbox. */
|
|
21
|
+
created: boolean;
|
|
22
|
+
/** True when the thread had a sandbox that is gone now: idle too long,
|
|
23
|
+
* past its lifetime, or ended on its own. Its files are lost, and a tool
|
|
24
|
+
* should tell the model so it does not expect them. */
|
|
25
|
+
lost: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare const sandboxKey: (threadId: string) => string;
|
|
28
|
+
/** The thread's sandbox, for a tool you write yourself. Pass the options
|
|
29
|
+
* your tool's `execute` was given:
|
|
30
|
+
*
|
|
31
|
+
* execute: async ({ path }, opts) => {
|
|
32
|
+
* const { sandbox } = await sandboxFor(opts);
|
|
33
|
+
* return sandbox.filesystem.readFile(path);
|
|
34
|
+
* }
|
|
35
|
+
*/
|
|
36
|
+
export declare function sandboxFor(toolOptions: unknown): Promise<ThreadSandbox>;
|
|
37
|
+
/** Runs `fn` on the thread's sandbox. When the sandbox turns out to be gone
|
|
38
|
+
* part way (it ended on its own), a fresh one is made and `fn` runs once
|
|
39
|
+
* more, told `lost: true`:
|
|
40
|
+
*
|
|
41
|
+
* execute: (args, opts) => withSandbox(opts, ({ sandbox, lost }) => ...)
|
|
42
|
+
*/
|
|
43
|
+
export declare function withSandbox<T>(toolOptions: unknown, fn: (ts: ThreadSandbox) => Promise<T>): Promise<T>;
|
|
44
|
+
export declare function withThreadSandbox<T>(run: ToolRun, fn: (ts: ThreadSandbox) => Promise<T>): Promise<T>;
|
|
45
|
+
export declare function threadSandbox(run: ToolRun): Promise<ThreadSandbox>;
|
|
46
|
+
/** Drops every handle this process holds, as a fresh worker would start.
|
|
47
|
+
* @internal For tests. */
|
|
48
|
+
export declare function forgetSandboxHandles(): void;
|
|
49
|
+
/** Ends the thread's sandbox, when it has one. Part of deleting a thread;
|
|
50
|
+
* never throws, since the thread is gone either way. */
|
|
51
|
+
export declare function destroyThreadSandbox(deps: RuntimePorts, threadId: string): Promise<void>;
|
|
52
|
+
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../../src/core/builtin/sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAA6C,KAAK,OAAO,EAAwB,MAAM,wBAAwB,CAAC;AACvH,OAAO,EAAa,KAAK,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnD;;;;;;;;;;;;;yBAayB;AAEzB,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,4CAA4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB;;4DAEwD;IACxD,IAAI,EAAE,OAAO,CAAC;CACf;AAUD,eAAO,MAAM,UAAU,GAAI,UAAU,MAAM,WAAqC,CAAC;AAejF;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAIvE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAItG;AAED,wBAAsB,iBAAiB,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAc1G;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CASlE;AAgED;2BAC2B;AAC3B,wBAAgB,oBAAoB,SAEnC;AAgBD;yDACyD;AACzD,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK9F"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { SandboxGoneError, SandboxUnsupportedError } from '../../ports/sandbox.js';
|
|
2
|
+
import { toolRunOf } from './run.js';
|
|
3
|
+
export const sandboxKey = (threadId) => `agent:tool:sandbox:${threadId}`;
|
|
4
|
+
/** A sandbox's own timeout is pushed back at most this often, and set this
|
|
5
|
+
* much past the idle limit so it never ends before the runtime counts it as
|
|
6
|
+
* idle. */
|
|
7
|
+
const PUSH_EVERY_MS = 60_000;
|
|
8
|
+
/** Handles this process already holds, by sandbox id, so a call does not
|
|
9
|
+
* reconnect every time. */
|
|
10
|
+
const handles = new Map();
|
|
11
|
+
const MAX_HANDLES = 1_000;
|
|
12
|
+
/** Calls under way, by thread: parallel subagents get one sandbox, not one
|
|
13
|
+
* each. */
|
|
14
|
+
const pending = new Map();
|
|
15
|
+
/** The thread's sandbox, for a tool you write yourself. Pass the options
|
|
16
|
+
* your tool's `execute` was given:
|
|
17
|
+
*
|
|
18
|
+
* execute: async ({ path }, opts) => {
|
|
19
|
+
* const { sandbox } = await sandboxFor(opts);
|
|
20
|
+
* return sandbox.filesystem.readFile(path);
|
|
21
|
+
* }
|
|
22
|
+
*/
|
|
23
|
+
export function sandboxFor(toolOptions) {
|
|
24
|
+
const run = toolRunOf(toolOptions);
|
|
25
|
+
if (!run)
|
|
26
|
+
return Promise.reject(new Error('sandboxFor: this tool is not running inside an agentenkit run'));
|
|
27
|
+
return threadSandbox(run);
|
|
28
|
+
}
|
|
29
|
+
/** Runs `fn` on the thread's sandbox. When the sandbox turns out to be gone
|
|
30
|
+
* part way (it ended on its own), a fresh one is made and `fn` runs once
|
|
31
|
+
* more, told `lost: true`:
|
|
32
|
+
*
|
|
33
|
+
* execute: (args, opts) => withSandbox(opts, ({ sandbox, lost }) => ...)
|
|
34
|
+
*/
|
|
35
|
+
export function withSandbox(toolOptions, fn) {
|
|
36
|
+
const run = toolRunOf(toolOptions);
|
|
37
|
+
if (!run)
|
|
38
|
+
return Promise.reject(new Error('withSandbox: this tool is not running inside an agentenkit run'));
|
|
39
|
+
return withThreadSandbox(run, fn);
|
|
40
|
+
}
|
|
41
|
+
export async function withThreadSandbox(run, fn) {
|
|
42
|
+
const ts = await threadSandbox(run);
|
|
43
|
+
try {
|
|
44
|
+
return await fn(ts);
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
if (!(e instanceof SandboxGoneError))
|
|
48
|
+
throw e;
|
|
49
|
+
handles.delete(ts.sandbox.sandboxId);
|
|
50
|
+
// Only while the record still names the sandbox that is gone: a parallel
|
|
51
|
+
// call may already have made the next one.
|
|
52
|
+
const key = sandboxKey(run.threadId);
|
|
53
|
+
const raw = await run.deps.kv.get(key);
|
|
54
|
+
if (parse(raw)?.id === ts.sandbox.sandboxId)
|
|
55
|
+
await run.deps.kv.delIfValue(key, raw);
|
|
56
|
+
return fn({ ...(await threadSandbox(run)), lost: true });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export function threadSandbox(run) {
|
|
60
|
+
const provider = run.deps.tools?.sandbox;
|
|
61
|
+
if (!provider)
|
|
62
|
+
return Promise.reject(new Error('No sandbox: pass setupAgentCore({ tools: { sandbox } })'));
|
|
63
|
+
const key = run.threadId;
|
|
64
|
+
const inFlight = pending.get(key);
|
|
65
|
+
if (inFlight)
|
|
66
|
+
return inFlight;
|
|
67
|
+
const p = acquire(run, provider).finally(() => pending.delete(key));
|
|
68
|
+
pending.set(key, p);
|
|
69
|
+
return p;
|
|
70
|
+
}
|
|
71
|
+
async function acquire(run, provider) {
|
|
72
|
+
const { kv, config } = run.deps;
|
|
73
|
+
const idle = config.sandboxIdleTtlMs;
|
|
74
|
+
const lifetime = config.sandboxMaxLifetimeMs;
|
|
75
|
+
const now = Date.now();
|
|
76
|
+
let rec = parse(await kv.get(sandboxKey(run.threadId)));
|
|
77
|
+
// A sandbox from another adapter cannot be reached from this one.
|
|
78
|
+
if (rec && rec.provider !== provider.name)
|
|
79
|
+
rec = null;
|
|
80
|
+
let lost = false;
|
|
81
|
+
if (rec && (now - rec.lastUsedAt > idle || now - rec.createdAt > lifetime)) {
|
|
82
|
+
await destroyQuietly(run.deps, provider, rec.id);
|
|
83
|
+
rec = null;
|
|
84
|
+
lost = true;
|
|
85
|
+
}
|
|
86
|
+
let sandbox;
|
|
87
|
+
let created = false;
|
|
88
|
+
if (rec) {
|
|
89
|
+
try {
|
|
90
|
+
sandbox = await connect(provider, rec.id);
|
|
91
|
+
const held = handles.get(rec.id);
|
|
92
|
+
if (now - held.pushedAt >= PUSH_EVERY_MS) {
|
|
93
|
+
await sandbox.setTimeout(Math.min(idle + PUSH_EVERY_MS, lifetime - (now - rec.createdAt))).catch((e) => {
|
|
94
|
+
if (!(e instanceof SandboxUnsupportedError))
|
|
95
|
+
throw e;
|
|
96
|
+
});
|
|
97
|
+
held.pushedAt = now;
|
|
98
|
+
}
|
|
99
|
+
rec.lastUsedAt = now;
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
if (!(e instanceof SandboxGoneError))
|
|
103
|
+
throw e;
|
|
104
|
+
handles.delete(rec.id);
|
|
105
|
+
rec = null;
|
|
106
|
+
lost = true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (!rec) {
|
|
110
|
+
sandbox = await provider.create({
|
|
111
|
+
timeoutMs: Math.min(idle + PUSH_EVERY_MS, lifetime),
|
|
112
|
+
metadata: {
|
|
113
|
+
threadId: run.threadId,
|
|
114
|
+
...(run.runId ? { runId: run.runId } : {}),
|
|
115
|
+
...(run.state ? { state: run.state } : {}),
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
remember(sandbox, now);
|
|
119
|
+
rec = { id: sandbox.sandboxId, provider: provider.name, createdAt: now, lastUsedAt: now };
|
|
120
|
+
created = true;
|
|
121
|
+
}
|
|
122
|
+
await kv.set(sandboxKey(run.threadId), JSON.stringify(rec), { exSeconds: Math.ceil(lifetime / 1000) });
|
|
123
|
+
return { sandbox: sandbox, created, lost };
|
|
124
|
+
}
|
|
125
|
+
async function connect(provider, id) {
|
|
126
|
+
const held = handles.get(id);
|
|
127
|
+
if (held)
|
|
128
|
+
return held.sandbox;
|
|
129
|
+
const sandbox = await provider.connect(id);
|
|
130
|
+
// A fresh handle pushes the timeout on its first use.
|
|
131
|
+
remember(sandbox, 0);
|
|
132
|
+
return sandbox;
|
|
133
|
+
}
|
|
134
|
+
/** Drops every handle this process holds, as a fresh worker would start.
|
|
135
|
+
* @internal For tests. */
|
|
136
|
+
export function forgetSandboxHandles() {
|
|
137
|
+
handles.clear();
|
|
138
|
+
}
|
|
139
|
+
function remember(sandbox, pushedAt) {
|
|
140
|
+
if (handles.size >= MAX_HANDLES)
|
|
141
|
+
handles.delete(handles.keys().next().value);
|
|
142
|
+
handles.set(sandbox.sandboxId, { sandbox, pushedAt });
|
|
143
|
+
}
|
|
144
|
+
async function destroyQuietly(deps, provider, id) {
|
|
145
|
+
handles.delete(id);
|
|
146
|
+
try {
|
|
147
|
+
await (await provider.connect(id)).destroy();
|
|
148
|
+
}
|
|
149
|
+
catch (e) {
|
|
150
|
+
if (!(e instanceof SandboxGoneError))
|
|
151
|
+
(deps.log ?? console).error('sandbox not destroyed', { sandboxId: id, err: e });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/** Ends the thread's sandbox, when it has one. Part of deleting a thread;
|
|
155
|
+
* never throws, since the thread is gone either way. */
|
|
156
|
+
export async function destroyThreadSandbox(deps, threadId) {
|
|
157
|
+
const provider = deps.tools?.sandbox;
|
|
158
|
+
const rec = parse(await deps.kv.get(sandboxKey(threadId)).catch(() => null));
|
|
159
|
+
if (provider && rec && rec.provider === provider.name)
|
|
160
|
+
await destroyQuietly(deps, provider, rec.id);
|
|
161
|
+
await deps.kv.del(sandboxKey(threadId)).catch(() => undefined);
|
|
162
|
+
}
|
|
163
|
+
function parse(raw) {
|
|
164
|
+
if (!raw)
|
|
165
|
+
return null;
|
|
166
|
+
try {
|
|
167
|
+
const r = JSON.parse(raw);
|
|
168
|
+
return typeof r.id === 'string' && typeof r.provider === 'string' ? r : null;
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=sandbox.js.map
|