sandboxedjs 0.1.35 → 0.1.37

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.
@@ -203,8 +203,8 @@ async function printReplBanner(box, opts) {
203
203
  " npm create vite@latest app -- --template react && cd app && npm i && npm run dev",
204
204
  " ffmpeg -f lavfi -i testsrc=size=64x64:rate=5:duration=1 out.mp4 && ls -l out.mp4",
205
205
  "",
206
- `${DIM}Known limits: no native binaries, no raw sockets, and esbuild-based`,
207
- `toolchains (Vite 4 and its contemporaries) cannot start. Current Vite works.${RESET}`,
206
+ `${DIM}Known limits: no raw sockets, and esbuild-based toolchains (Vite 4 and`,
207
+ `its contemporaries) cannot start. Current Vite works, build and dev server.${RESET}`,
208
208
  "",
209
209
  `${DIM}Ctrl-D or \`exit\` to leave.${RESET}`,
210
210
  "",
package/dist/agent.cjs ADDED
@@ -0,0 +1,584 @@
1
+ 'use strict';
2
+
3
+ // src/agent/backend.ts
4
+ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
5
+ "png",
6
+ "jpg",
7
+ "jpeg",
8
+ "gif",
9
+ "webp",
10
+ "ico",
11
+ "bmp",
12
+ "pdf",
13
+ "zip",
14
+ "gz",
15
+ "tar",
16
+ "wasm",
17
+ "so",
18
+ "dylib",
19
+ "dll",
20
+ "exe",
21
+ "woff",
22
+ "woff2",
23
+ "ttf",
24
+ "otf",
25
+ "mp3",
26
+ "mp4",
27
+ "mov",
28
+ "avi",
29
+ "bin",
30
+ "db",
31
+ "sqlite"
32
+ ]);
33
+ var MIME_BY_EXTENSION = {
34
+ png: "image/png",
35
+ jpg: "image/jpeg",
36
+ jpeg: "image/jpeg",
37
+ gif: "image/gif",
38
+ webp: "image/webp",
39
+ svg: "image/svg+xml",
40
+ pdf: "application/pdf",
41
+ json: "application/json",
42
+ js: "text/javascript",
43
+ ts: "text/x-typescript",
44
+ html: "text/html",
45
+ css: "text/css",
46
+ md: "text/markdown",
47
+ wasm: "application/wasm"
48
+ };
49
+ function extensionOf(filePath) {
50
+ const base = filePath.slice(filePath.lastIndexOf("/") + 1);
51
+ const dot = base.lastIndexOf(".");
52
+ return dot > 0 ? base.slice(dot + 1).toLowerCase() : "";
53
+ }
54
+ function globToRegExp(pattern) {
55
+ let out = "";
56
+ for (let i = 0; i < pattern.length; i += 1) {
57
+ const ch = pattern[i];
58
+ if (ch === "*") {
59
+ if (pattern[i + 1] === "*") {
60
+ i += 1;
61
+ if (pattern[i + 1] === "/") {
62
+ i += 1;
63
+ out += "(?:.*/)?";
64
+ } else {
65
+ out += ".*";
66
+ }
67
+ } else {
68
+ out += "[^/]*";
69
+ }
70
+ } else if (ch === "?") {
71
+ out += "[^/]";
72
+ } else {
73
+ out += ch.replace(/[.+^${}()|[\]\\]/g, "\\$&");
74
+ }
75
+ }
76
+ return new RegExp(`^${out}$`);
77
+ }
78
+ function joinPath(base, name) {
79
+ return base.endsWith("/") ? `${base}${name}` : `${base}/${name}`;
80
+ }
81
+ function requireAbsolute(filePath) {
82
+ return filePath.startsWith("/") ? null : `Path must be absolute: ${filePath}`;
83
+ }
84
+ function errorMessage(error) {
85
+ return error instanceof Error ? error.message : String(error);
86
+ }
87
+ function looksBinary(data, filePath) {
88
+ if (BINARY_EXTENSIONS.has(extensionOf(filePath))) return true;
89
+ const window = data.subarray(0, 1024);
90
+ for (const byte of window) if (byte === 0) return true;
91
+ return false;
92
+ }
93
+ var SandboxedJsBackend = class {
94
+ constructor(container, options = {}) {
95
+ this.container = container;
96
+ this.id = options.id ?? `sandboxedjs-${Math.random().toString(36).slice(2, 10)}`;
97
+ this.cwd = options.cwd;
98
+ this.timeoutMs = options.timeoutMs ?? 12e4;
99
+ this.maxOutputChars = options.maxOutputChars ?? 3e4;
100
+ this.defaultReadLimit = options.defaultReadLimit ?? 500;
101
+ this.maxGlobResults = options.maxGlobResults ?? 1e3;
102
+ this.defaultGrepMaxCount = options.defaultGrepMaxCount ?? 200;
103
+ }
104
+ container;
105
+ id;
106
+ cwd;
107
+ timeoutMs;
108
+ maxOutputChars;
109
+ defaultReadLimit;
110
+ maxGlobResults;
111
+ defaultGrepMaxCount;
112
+ async execute(command) {
113
+ try {
114
+ const result = await this.container.exec(command, {
115
+ cwd: this.cwd,
116
+ timeoutMs: this.timeoutMs
117
+ });
118
+ let output = result.output;
119
+ let truncated = false;
120
+ if (output.length > this.maxOutputChars) {
121
+ output = `[output truncated to the last ${this.maxOutputChars} characters]
122
+ ${output.slice(-this.maxOutputChars)}`;
123
+ truncated = true;
124
+ }
125
+ if (result.timedOut) {
126
+ output += `
127
+ [command timed out after ${this.timeoutMs}ms]`;
128
+ }
129
+ return { output, exitCode: result.exitCode, truncated };
130
+ } catch (error) {
131
+ return { output: errorMessage(error), exitCode: 1, truncated: false };
132
+ }
133
+ }
134
+ async ls(path) {
135
+ const invalid = requireAbsolute(path);
136
+ if (invalid) return { error: invalid };
137
+ try {
138
+ const entries = await this.container.fs.readdir(path);
139
+ const files = [];
140
+ for (const name of entries) {
141
+ const full = joinPath(path, name);
142
+ try {
143
+ const stats = await this.container.fs.stat(full);
144
+ const isDir = stats.isDirectory();
145
+ files.push({
146
+ path: isDir ? `${full}/` : full,
147
+ is_dir: isDir,
148
+ size: isDir ? void 0 : stats.size,
149
+ modified_at: new Date(stats.mtimeMs).toISOString()
150
+ });
151
+ } catch {
152
+ files.push({ path: full });
153
+ }
154
+ }
155
+ return { files };
156
+ } catch (error) {
157
+ return { error: errorMessage(error) };
158
+ }
159
+ }
160
+ async read(filePath, offset = 0, limit) {
161
+ const invalid = requireAbsolute(filePath);
162
+ if (invalid) return { error: invalid };
163
+ try {
164
+ const raw = await this.container.fs.readFile(filePath);
165
+ const mimeType = MIME_BY_EXTENSION[extensionOf(filePath)];
166
+ if (looksBinary(raw, filePath)) {
167
+ return { content: raw, mimeType: mimeType ?? "application/octet-stream" };
168
+ }
169
+ const text = new TextDecoder().decode(raw);
170
+ const lines = text.split("\n");
171
+ if (lines.length > 1 && lines[lines.length - 1] === "") lines.pop();
172
+ const start = Math.max(0, offset);
173
+ const end = Math.min(lines.length, start + (limit ?? this.defaultReadLimit));
174
+ return {
175
+ content: lines.slice(start, end).join("\n"),
176
+ mimeType: mimeType ?? "text/plain",
177
+ totalLines: lines.length,
178
+ startLine: start,
179
+ endLine: end,
180
+ nextOffset: end < lines.length ? end : void 0
181
+ };
182
+ } catch (error) {
183
+ return { error: errorMessage(error) };
184
+ }
185
+ }
186
+ async readRaw(filePath) {
187
+ const invalid = requireAbsolute(filePath);
188
+ if (invalid) return { error: invalid };
189
+ try {
190
+ const raw = await this.container.fs.readFile(filePath);
191
+ const mimeType = MIME_BY_EXTENSION[extensionOf(filePath)];
192
+ if (looksBinary(raw, filePath)) {
193
+ return { data: { content: raw, mimeType: mimeType ?? "application/octet-stream" } };
194
+ }
195
+ return {
196
+ data: { content: new TextDecoder().decode(raw), mimeType: mimeType ?? "text/plain" }
197
+ };
198
+ } catch (error) {
199
+ return { error: errorMessage(error) };
200
+ }
201
+ }
202
+ async write(filePath, content) {
203
+ const invalid = requireAbsolute(filePath);
204
+ if (invalid) return { error: invalid };
205
+ try {
206
+ const parent = filePath.slice(0, filePath.lastIndexOf("/")) || "/";
207
+ await this.container.fs.mkdir(parent, { recursive: true });
208
+ await this.container.fs.writeFile(filePath, content);
209
+ return { path: filePath };
210
+ } catch (error) {
211
+ return { error: errorMessage(error) };
212
+ }
213
+ }
214
+ async edit(filePath, oldString, newString, replaceAll = false) {
215
+ const invalid = requireAbsolute(filePath);
216
+ if (invalid) return { error: invalid };
217
+ try {
218
+ const text = await this.container.fs.readFile(filePath, "utf8");
219
+ const occurrences = oldString === "" ? 0 : text.split(oldString).length - 1;
220
+ if (occurrences === 0) {
221
+ return { error: `String not found in ${filePath}: ${JSON.stringify(oldString)}` };
222
+ }
223
+ if (occurrences > 1 && !replaceAll) {
224
+ return {
225
+ error: `String appears ${occurrences} times in ${filePath}. Provide more surrounding context to make it unique, or pass replaceAll.`
226
+ };
227
+ }
228
+ const updated = replaceAll ? text.split(oldString).join(newString) : text.replace(oldString, newString);
229
+ await this.container.fs.writeFile(filePath, updated);
230
+ return { path: filePath, occurrences };
231
+ } catch (error) {
232
+ return { error: errorMessage(error) };
233
+ }
234
+ }
235
+ async delete(filePath) {
236
+ const invalid = requireAbsolute(filePath);
237
+ if (invalid) return { error: invalid };
238
+ try {
239
+ await this.container.fs.rm(filePath, { recursive: true, force: false });
240
+ return { path: filePath };
241
+ } catch (error) {
242
+ return { error: errorMessage(error) };
243
+ }
244
+ }
245
+ async glob(pattern, path = "/") {
246
+ try {
247
+ const matcher = globToRegExp(pattern.startsWith("/") ? pattern : joinPath(path, pattern));
248
+ const files = [];
249
+ let truncated = false;
250
+ for (const candidate of await this.container.fs.walk(path)) {
251
+ if (!matcher.test(candidate)) continue;
252
+ if (files.length >= this.maxGlobResults) {
253
+ truncated = true;
254
+ break;
255
+ }
256
+ files.push({ path: candidate });
257
+ }
258
+ return { files, truncated };
259
+ } catch (error) {
260
+ return { error: errorMessage(error) };
261
+ }
262
+ }
263
+ async grep(pattern, path, glob, maxCount) {
264
+ const root = path ?? "/";
265
+ const cap = maxCount ?? this.defaultGrepMaxCount;
266
+ try {
267
+ const filter = glob ? globToRegExp(glob.includes("/") ? glob : `**/${glob}`) : null;
268
+ const matches = [];
269
+ let truncated = false;
270
+ outer: for (const candidate of await this.container.fs.walk(root)) {
271
+ if (filter && !filter.test(candidate)) continue;
272
+ let raw;
273
+ try {
274
+ raw = await this.container.fs.readFile(candidate);
275
+ } catch {
276
+ continue;
277
+ }
278
+ if (looksBinary(raw, candidate)) continue;
279
+ const lines = new TextDecoder().decode(raw).split("\n");
280
+ for (let i = 0; i < lines.length; i += 1) {
281
+ if (!lines[i].includes(pattern)) continue;
282
+ if (matches.length >= cap) {
283
+ truncated = true;
284
+ break outer;
285
+ }
286
+ matches.push({ path: candidate, line: i + 1, text: lines[i] });
287
+ }
288
+ }
289
+ return { matches, truncated };
290
+ } catch (error) {
291
+ return { error: errorMessage(error) };
292
+ }
293
+ }
294
+ async uploadFiles(files) {
295
+ const results = [];
296
+ for (const [filePath, content] of files) {
297
+ if (!filePath.startsWith("/")) {
298
+ results.push({ path: filePath, error: "invalid_path" });
299
+ continue;
300
+ }
301
+ try {
302
+ const parent = filePath.slice(0, filePath.lastIndexOf("/")) || "/";
303
+ await this.container.fs.mkdir(parent, { recursive: true });
304
+ await this.container.fs.writeFile(filePath, content);
305
+ results.push({ path: filePath, error: null });
306
+ } catch {
307
+ results.push({ path: filePath, error: "permission_denied" });
308
+ }
309
+ }
310
+ return results;
311
+ }
312
+ async downloadFiles(paths) {
313
+ const results = [];
314
+ for (const filePath of paths) {
315
+ if (!filePath.startsWith("/")) {
316
+ results.push({ path: filePath, content: null, error: "invalid_path" });
317
+ continue;
318
+ }
319
+ try {
320
+ const stats = await this.container.fs.stat(filePath);
321
+ if (stats.isDirectory()) {
322
+ results.push({ path: filePath, content: null, error: "is_directory" });
323
+ continue;
324
+ }
325
+ results.push({
326
+ path: filePath,
327
+ content: await this.container.fs.readFile(filePath),
328
+ error: null
329
+ });
330
+ } catch {
331
+ results.push({ path: filePath, content: null, error: "file_not_found" });
332
+ }
333
+ }
334
+ return results;
335
+ }
336
+ };
337
+
338
+ // src/agent/prompts.ts
339
+ var SANDBOX_ENVIRONMENT_PROMPT = `# Your environment
340
+
341
+ You are working inside a sandboxedjs container: a Linux-like environment that
342
+ runs entirely inside a JavaScript process. There is no Docker, no VM, and no
343
+ host machine you can reach. Everything below is real and available to you.
344
+
345
+ ## What you have
346
+
347
+ - A POSIX shell (\`sh\`/\`bash\` syntax): pipes, redirection, \`&&\`, \`||\`,
348
+ subshells, globs, heredocs, variables, functions, \`for\`/\`while\`/\`case\`.
349
+ - Around 140 coreutils: \`ls cat cp mv rm mkdir find grep sed awk head tail
350
+ sort uniq wc diff patch tar gzip curl chmod ln touch echo printf test\` and
351
+ the rest of the usual set.
352
+ - Node.js, with \`node\`, \`npm\` and \`npx\`. \`npm install\` resolves against
353
+ the real npm registry when outbound network is enabled.
354
+ - Python 3 via \`python3\` and \`pip\`, when the host enabled the Python runtime.
355
+ - A writable virtual filesystem rooted at \`/\`, persistent for the life of the
356
+ container.
357
+ - A virtual network stack. Servers you start inside the container really listen
358
+ on their ports and can really be requested.
359
+
360
+ ## What you do not have
361
+
362
+ - No Docker, no VM, no \`systemctl\`, no \`service\`, no \`apt\`/\`apt-get\`,
363
+ no \`yum\`, no \`brew\`. Never try to install system packages.
364
+ - No \`sudo\` and no reason for it: you already run as the container's user and
365
+ the filesystem is yours.
366
+ - No access to the host machine, its files, its network interfaces, or its
367
+ environment variables. Nothing outside the container exists for you.
368
+ - No GUI, no browser, no interactive editors. Do not run \`vim\`, \`nano\`,
369
+ \`less\`, or \`top\`; they will hang or fail. Read files by reading them and
370
+ edit them by editing them.
371
+ - No long-running foreground commands. A command that never exits will hit the
372
+ execution timeout and the turn is wasted.
373
+
374
+ ## Running servers
375
+
376
+ Start servers in the background and never block on them:
377
+
378
+ \`\`\`sh
379
+ node server.js > /tmp/server.log 2>&1 &
380
+ \`\`\`
381
+
382
+ Then poll the log for readiness rather than requesting the port immediately.
383
+ Do not run a dev server in the foreground. Do not use \`curl localhost:PORT\`
384
+ to prove a server works unless you started it in the background first \u2014 the
385
+ host, not you, is the one that will connect to it.
386
+
387
+ ## How your work is used
388
+
389
+ The container is the deliverable. Files you write to the filesystem are what
390
+ the user receives and what a preview will serve. Write real, complete files to
391
+ real paths \u2014 do not print a project to stdout and call it done.`;
392
+ var SANDBOX_AGENT_RULES = `# Rules
393
+
394
+ 1. Verify before you claim. If you say a server runs or a build passes, you
395
+ ran it in this container and read the output. Never report success you have
396
+ not observed.
397
+ 2. One command, one purpose. Chain with \`&&\` when steps depend on each other
398
+ so a failure stops the chain instead of hiding under a later success.
399
+ 3. Read a file before editing it. Edits are literal string replacements; they
400
+ fail when you are guessing at the current contents.
401
+ 4. Use absolute paths in file tools. Use \`cd\` inside a single shell command
402
+ when a command needs a working directory.
403
+ 5. Install dependencies with \`npm install <pkg>\`, in the directory that has
404
+ the \`package.json\`. Do not hand-write \`node_modules\` or invent versions
405
+ in \`package.json\` \u2014 let the installer resolve them.
406
+ 6. Background every server and long task, redirect its output to a log file,
407
+ then poll the log. Never leave a command running in the foreground.
408
+ 7. Keep command output small. Pipe noisy commands through \`tail\`, \`head\`
409
+ or \`grep\`. Output is truncated past the backend's limit and you will lose
410
+ the part you needed.
411
+ 8. When a command fails, read stderr and fix the cause. Do not retry the same
412
+ command unchanged, and do not work around a failure by faking its result.
413
+ 9. Prefer the project's own tooling \u2014 \`npm run build\`, \`npm test\`,
414
+ \`npx vite\` \u2014 over reimplementing what it already does.
415
+ 10. Do not attempt to escape the container, reach the host, or disable the
416
+ network policy. Outbound access is the host's decision, not yours.`;
417
+ function skill(name, description, body) {
418
+ return {
419
+ name,
420
+ description,
421
+ content: `---
422
+ name: ${name}
423
+ description: ${description}
424
+ ---
425
+
426
+ ${body}
427
+ `
428
+ };
429
+ }
430
+ var SANDBOX_SKILLS = [
431
+ skill(
432
+ "node-service",
433
+ "Scaffold, install, run and verify a Node.js HTTP service (Express, Fastify, plain http) inside the sandbox.",
434
+ `# Building a Node HTTP service
435
+
436
+ Follow these steps in order. Do not skip verification.
437
+
438
+ 1. Create the project directory and manifest:
439
+
440
+ \`\`\`sh
441
+ mkdir -p /app && cd /app && npm init -y
442
+ \`\`\`
443
+
444
+ 2. Install dependencies in one command:
445
+
446
+ \`\`\`sh
447
+ cd /app && npm install express
448
+ \`\`\`
449
+
450
+ Read the output. If it ends in an \`ENOTFOUND\` or network error, outbound
451
+ access is disabled for this container \u2014 say so and stop, rather than
452
+ inventing a dependency-free rewrite the user did not ask for.
453
+
454
+ 3. Write the server to a real file. Bind to \`0.0.0.0\` and log a line on
455
+ listen, so readiness is observable:
456
+
457
+ \`\`\`js
458
+ const express = require("express");
459
+ const app = express();
460
+ app.get("/", (_req, res) => res.send("hello world"));
461
+ const port = Number(process.env.PORT) || 3000;
462
+ app.listen(port, "0.0.0.0", () => console.log(\`listening on \${port}\`));
463
+ \`\`\`
464
+
465
+ 4. Start it in the background and wait for the log line:
466
+
467
+ \`\`\`sh
468
+ cd /app && node server.js > /tmp/server.log 2>&1 &
469
+ sleep 1 && cat /tmp/server.log
470
+ \`\`\`
471
+
472
+ 5. Verify it answers:
473
+
474
+ \`\`\`sh
475
+ curl -s -i http://127.0.0.1:3000/ | head -20
476
+ \`\`\`
477
+
478
+ A non-2xx status or an empty response means the server is not working. Read
479
+ \`/tmp/server.log\` and fix the cause before reporting anything.`
480
+ ),
481
+ skill(
482
+ "frontend-app",
483
+ "Scaffold and run a Vite-based frontend (React, Vue, Svelte, vanilla) inside the sandbox and confirm the dev server serves.",
484
+ `# Building a frontend app
485
+
486
+ 1. Scaffold non-interactively \u2014 the interactive prompt will hang:
487
+
488
+ \`\`\`sh
489
+ cd / && npm create vite@latest app -- --template react-ts
490
+ \`\`\`
491
+
492
+ 2. Install:
493
+
494
+ \`\`\`sh
495
+ cd /app && npm install
496
+ \`\`\`
497
+
498
+ 3. Edit the real source files under \`/app/src\`. Read a file before editing it.
499
+
500
+ 4. Prove it compiles before claiming it works:
501
+
502
+ \`\`\`sh
503
+ cd /app && npm run build 2>&1 | tail -30
504
+ \`\`\`
505
+
506
+ 5. Only if a live preview is wanted, start the dev server in the background on
507
+ a fixed host and port and confirm it came up:
508
+
509
+ \`\`\`sh
510
+ cd /app && npx vite --host 0.0.0.0 --port 5173 > /tmp/vite.log 2>&1 &
511
+ sleep 3 && tail -20 /tmp/vite.log
512
+ \`\`\`
513
+
514
+ Do not run \`npm run dev\` in the foreground.`
515
+ ),
516
+ skill(
517
+ "verify-work",
518
+ "Check that generated code actually builds, runs and passes its tests before reporting completion.",
519
+ `# Verifying before reporting
520
+
521
+ Never report a task complete on the strength of having written files.
522
+
523
+ - If the project has tests: \`cd <dir> && npm test 2>&1 | tail -40\`
524
+ - If it has a build: \`cd <dir> && npm run build 2>&1 | tail -40\`
525
+ - If it has neither and it is a script: run it and read the output.
526
+ - If it is a service: start it in the background and request it (see the
527
+ \`node-service\` skill).
528
+
529
+ Then state what you ran and what it printed. If something fails and you cannot
530
+ fix it, say exactly what fails and what you tried. A wrong claim of success is
531
+ worse than an honest failure.`
532
+ ),
533
+ skill(
534
+ "debug-failure",
535
+ "Diagnose a failing command, build, install or server inside the sandbox instead of retrying blindly.",
536
+ `# Debugging inside the sandbox
537
+
538
+ 1. Re-read the actual error. The cause is usually the first error line, not
539
+ the last.
540
+ 2. Confirm the state you assumed: \`ls -la\` the directory, \`cat\` the config,
541
+ \`cat package.json\`. Most failures are a wrong path or a missing install.
542
+ 3. Check the log of anything backgrounded: \`tail -50 /tmp/*.log\`.
543
+ 4. For a module resolution error, verify the package is installed where you
544
+ think: \`ls /app/node_modules/<pkg>/package.json\`.
545
+ 5. For a port that will not answer, check the process is alive (\`ps\`) and the
546
+ log shows a listen line. A crashed server leaves no port behind.
547
+ 6. Change exactly one thing, then re-run. Do not retry an unchanged command,
548
+ and do not delete the work to start over unless nothing else is left.`
549
+ )
550
+ ];
551
+ function sandboxSystemPrompt(options = {}) {
552
+ const parts = [SANDBOX_ENVIRONMENT_PROMPT, SANDBOX_AGENT_RULES];
553
+ if (options.skills !== false) {
554
+ parts.push(
555
+ `# Skills
556
+
557
+ ${SANDBOX_SKILLS.map(
558
+ (entry) => `## ${entry.name}
559
+
560
+ ${entry.description}
561
+
562
+ ${entry.content.split("---\n")[2].trim()}`
563
+ ).join("\n\n")}`
564
+ );
565
+ }
566
+ return parts.join("\n\n");
567
+ }
568
+ async function installSandboxSkills(container, directory = "/skills") {
569
+ for (const entry of SANDBOX_SKILLS) {
570
+ const dir = `${directory}/${entry.name}`;
571
+ await container.fs.mkdir(dir, { recursive: true });
572
+ await container.fs.writeFile(`${dir}/SKILL.md`, entry.content);
573
+ }
574
+ return directory;
575
+ }
576
+
577
+ exports.SANDBOX_AGENT_RULES = SANDBOX_AGENT_RULES;
578
+ exports.SANDBOX_ENVIRONMENT_PROMPT = SANDBOX_ENVIRONMENT_PROMPT;
579
+ exports.SANDBOX_SKILLS = SANDBOX_SKILLS;
580
+ exports.SandboxedJsBackend = SandboxedJsBackend;
581
+ exports.installSandboxSkills = installSandboxSkills;
582
+ exports.sandboxSystemPrompt = sandboxSystemPrompt;
583
+ //# sourceMappingURL=agent.cjs.map
584
+ //# sourceMappingURL=agent.cjs.map