Skip to content

Commit c53d7d7

Browse files
feat: add sandbox execution support for feature parity with Python deepagents (#88)
* feat: catch up with Python * add unit tests * add example * format * Potential fix for code scanning alert no. 7: Unsafe shell command constructed from library input Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * format * make sure it is truly boolean --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
1 parent 3628325 commit c53d7d7

25 files changed

+3023
-361
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ jobs:
2828
run: corepack enable
2929
- name: Install pnpm
3030
uses: pnpm/action-setup@v4
31-
with:
32-
version: 10
3331
- name: Use Node.js
3432
uses: actions/setup-node@v3
3533
with:
@@ -49,8 +47,6 @@ jobs:
4947
run: corepack enable
5048
- name: Install pnpm
5149
uses: pnpm/action-setup@v4
52-
with:
53-
version: 10
5450
- name: Use Node.js
5551
uses: actions/setup-node@v3
5652
with:
@@ -70,8 +66,6 @@ jobs:
7066
run: corepack enable
7167
- name: Install pnpm
7268
uses: pnpm/action-setup@v4
73-
with:
74-
version: 10
7569
- name: Use Node.js
7670
uses: actions/setup-node@v3
7771
with:

.github/workflows/release.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ jobs:
2424

2525
- name: Install pnpm
2626
uses: pnpm/action-setup@v4
27-
with:
28-
version: 10
2927

3028
- name: Setup Node.js
3129
uses: actions/setup-node@v4

.github/workflows/unit-tests.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ jobs:
2929
run: corepack enable
3030
- name: Install pnpm
3131
uses: pnpm/action-setup@v4
32-
with:
33-
version: 10
3432
- name: Use Node.js ${{ matrix.node-version }}
3533
uses: actions/setup-node@v4
3634
with:

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,10 @@ credentials.json
4949
.langgraph_api
5050

5151
**/.claude/settings.local.json
52+
53+
# Agent OS
54+
agent-os
55+
.claude
56+
57+
# Sandbox workspace directory - created at runtime
58+
sandbox-workspace/

README.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,81 @@ const agent4 = createDeepAgent({
440440

441441
See [examples/backends/](examples/backends/) for detailed examples of each backend type.
442442

443+
### Sandbox Execution
444+
445+
For agents that need to run shell commands, you can create a sandbox backend by extending `BaseSandbox`. This enables the `execute` tool which allows agents to run arbitrary shell commands in an isolated environment.
446+
447+
```typescript
448+
import {
449+
createDeepAgent,
450+
BaseSandbox,
451+
type ExecuteResponse,
452+
type FileUploadResponse,
453+
type FileDownloadResponse,
454+
} from "deepagents";
455+
import { spawn } from "child_process";
456+
457+
// Create a concrete sandbox by extending BaseSandbox
458+
class LocalShellSandbox extends BaseSandbox {
459+
readonly id = "local-shell";
460+
private readonly workingDirectory: string;
461+
462+
constructor(workingDirectory: string) {
463+
super();
464+
this.workingDirectory = workingDirectory;
465+
}
466+
467+
// Only execute() is required - BaseSandbox implements all file operations
468+
async execute(command: string): Promise<ExecuteResponse> {
469+
return new Promise((resolve) => {
470+
const child = spawn("/bin/bash", ["-c", command], {
471+
cwd: this.workingDirectory,
472+
});
473+
474+
const chunks: string[] = [];
475+
child.stdout.on("data", (data) => chunks.push(data.toString()));
476+
child.stderr.on("data", (data) => chunks.push(data.toString()));
477+
478+
child.on("close", (exitCode) => {
479+
resolve({
480+
output: chunks.join(""),
481+
exitCode,
482+
truncated: false,
483+
});
484+
});
485+
});
486+
}
487+
488+
async uploadFiles(
489+
files: Array<[string, Uint8Array]>,
490+
): Promise<FileUploadResponse[]> {
491+
// Implement file upload logic
492+
return files.map(([path]) => ({ path, error: null }));
493+
}
494+
495+
async downloadFiles(paths: string[]): Promise<FileDownloadResponse[]> {
496+
// Implement file download logic
497+
return paths.map((path) => ({
498+
path,
499+
content: null,
500+
error: "file_not_found",
501+
}));
502+
}
503+
}
504+
505+
// Use the sandbox with your agent
506+
const sandbox = new LocalShellSandbox("./workspace");
507+
508+
const agent = createDeepAgent({
509+
backend: sandbox,
510+
systemPrompt: "You can run shell commands using the execute tool.",
511+
});
512+
```
513+
514+
When using a sandbox backend, the agent gains access to an `execute` tool that can run shell commands. The tool automatically returns the command output, exit code, and whether the output was truncated.
515+
516+
See [examples/sandbox/local-sandbox.ts](examples/sandbox/local-sandbox.ts) for a complete implementation.
517+
443518
## Deep Agents Middleware
444519

445520
Deep Agents are built with a modular middleware architecture. As a reminder, Deep Agents have access to:
@@ -486,6 +561,7 @@ Context engineering is one of the main challenges in building effective agents.
486561
- **edit_file**: Edit an existing file in your filesystem
487562
- **glob**: Find files matching a pattern
488563
- **grep**: Search for text within files
564+
- **execute**: Run shell commands (only available when using a `SandboxBackendProtocol`)
489565

490566
```typescript
491567
import { createAgent } from "langchain";

0 commit comments

Comments
 (0)