docs: agent runs inside the sandbox, consistent Claude Agent SDK usage

All four provider guides now:
- Run the Claude agent INSIDE the sandbox (not from orchestrating code)
- Use the Claude Agent SDK consistently (not Vercel AI SDK)
- Agent script is written into the sandbox and executed there
- Agent uses Bash/Read/Write tools on the SMFS mount
This commit is contained in:
Dhravya 2026-04-27 22:50:31 +00:00
parent faeef886ee
commit 83867d57d3
4 changed files with 257 additions and 238 deletions

View file

@ -9,10 +9,10 @@ agent can read and write memory with plain bash commands.
## How it works
1. Build a container image with SMFS pre-installed
1. Build a container image with SMFS and the Claude Agent SDK pre-installed
2. Deploy it as a Cloudflare Container
3. On startup, mount a Supermemory container inside the container
4. Run a Claude agent with bash access — it reads/writes the SMFS mount naturally
3. On startup, mount a Supermemory container and run the agent
4. The agent uses `cat`, `ls`, `echo`, etc. on the mount — everything persists to Supermemory
## Prerequisites
@ -26,18 +26,15 @@ agent can read and write memory with plain bash commands.
### Dockerfile
```dockerfile Dockerfile
FROM node:20-slim
FROM python:3.12-slim
# Install FUSE and bash
RUN apt-get update && apt-get install -y fuse3 curl bash && rm -rf /var/lib/apt/lists/*
RUN echo 'user_allow_other' >> /etc/fuse.conf
# Install SMFS
RUN curl -fsSL https://smfs.ai/install | bash
RUN pip install claude-agent-sdk
# Install the Claude Agent SDK
RUN npm install -g @anthropic-ai/claude-agent-sdk
COPY agent.py /app/agent.py
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
@ -50,36 +47,33 @@ ENTRYPOINT ["/entrypoint.sh"]
#!/bin/bash
set -e
# Log in and mount
smfs login --key "$SUPERMEMORY_API_KEY"
smfs mount my_agent --ephemeral --path /memory --foreground &
sleep 5
# Run the agent
node agent.js
python3 /app/agent.py
```
### Agent
```typescript agent.ts
import { query } from "@anthropic-ai/claude-agent-sdk";
```python agent.py
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async function main() {
for await (const message of query({
prompt: `You have access to a persistent memory filesystem mounted at /memory.
Use bash commands to explore it (ls, cat) and write notes to it (echo "..." > file).
async def main():
async for message in query(
prompt="""You have a persistent memory filesystem at /memory.
Use bash to explore it (ls, cat) and write notes (echo "..." > file).
Read /memory/profile.md to learn about the user.
Then create /memory/session_notes.md with a summary of what you found.`,
options: {
allowedTools: ["Bash", "Read", "Write"],
},
})) {
if (message.type === "text") console.log(message.text);
}
}
Then create /memory/session_notes.md summarizing what you found.""",
options=ClaudeAgentOptions(
allowed_tools=["Bash", "Read", "Write"],
),
):
print(message)
main();
asyncio.run(main())
```
## Worker + Container pattern
@ -89,11 +83,7 @@ Use a Cloudflare Worker as the HTTP frontend that triggers the container:
```typescript worker.ts
export default {
async fetch(request: Request, env: any) {
// Start the container (it runs the agent with SMFS mounted)
const container = await env.MY_CONTAINER.start();
// The container runs the agent and writes results to SMFS
// Read the result back
const response = await container.fetch("/result");
return response;
},
@ -112,10 +102,10 @@ max_instances = 5
## Tips
- Use `--ephemeral` when mounting inside containers — it keeps the cache in memory
- Use `--ephemeral` when mounting inside containers — keeps the cache in memory
only, but writes still push to Supermemory
- Use `smfs grep 'query'` for semantic search across all files in the container
- Set `SUPERMEMORY_API_KEY` and `ANTHROPIC_API_KEY` as Cloudflare secrets:
- Set secrets via Wrangler:
```bash
wrangler secret put SUPERMEMORY_API_KEY
wrangler secret put ANTHROPIC_API_KEY

View file

@ -10,8 +10,8 @@ your agent can read and write memory with plain bash commands.
1. Create a Daytona sandbox
2. Install SMFS and mount a Supermemory container inside it
3. Run a Claude agent inside the sandbox — it uses `cat`, `ls`, `echo`, etc. on the mount
4. Everything the agent writes is persisted to Supermemory automatically
3. Install the Claude Agent SDK inside the sandbox and run the agent there
4. The agent uses `cat`, `ls`, `echo`, etc. on the mount — everything persists to Supermemory
## Prerequisites
@ -24,22 +24,20 @@ your agent can read and write memory with plain bash commands.
<Tabs>
<Tab title="TypeScript">
```bash
npm install @anthropic-ai/claude-agent-sdk @daytonaio/sdk
npm install @daytonaio/sdk
```
```typescript agent.ts
import { Daytona } from "@daytonaio/sdk";
import { query } from "@anthropic-ai/claude-agent-sdk";
async function main() {
// 1. Create a Daytona sandbox
const daytona = new Daytona({
apiKey: process.env.DAYTONA_API_KEY!,
apiUrl: "https://app.daytona.io/api",
});
const sandbox = await daytona.create();
// 2. Install SMFS, log in, and mount
// Install SMFS, log in, and mount
await sandbox.process.exec("curl -fsSL https://smfs.ai/install | bash");
await sandbox.process.exec(
`~/.local/bin/smfs login --key ${process.env.SUPERMEMORY_API_KEY}`
@ -48,24 +46,40 @@ your agent can read and write memory with plain bash commands.
"~/.local/bin/smfs mount my_agent --ephemeral --path /home/daytona/memory"
);
// 3. Run a Claude agent inside the sandbox with bash access
for await (const message of query({
prompt: `You have access to a persistent memory filesystem mounted at /home/daytona/memory.
Use bash commands to explore it (ls, cat) and write notes to it (echo "..." > file).
// Install Claude Agent SDK inside the sandbox
await sandbox.process.exec("pip install claude-agent-sdk");
First, read /home/daytona/memory/profile.md to learn about the user.
Then create /home/daytona/memory/session_notes.md with a summary of what you found.`,
options: {
allowedTools: ["Bash", "Read", "Write"],
},
})) {
if (message.type === "text") console.log(message.text);
}
// Write the agent script into the sandbox
await sandbox.fs.uploadFile(
"/home/daytona/agent.py",
new TextEncoder().encode(`
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
import os
// 4. Clean up
await sandbox.process.exec(
"~/.local/bin/smfs unmount my_agent 2>/dev/null"
async def main():
async for message in query(
prompt="""You have a persistent memory filesystem at /home/daytona/memory.
Use bash to explore it (ls, cat) and write notes (echo "..." > file).
Read /home/daytona/memory/profile.md to learn about the user.
Then create /home/daytona/memory/session_notes.md summarizing what you found.""",
options=ClaudeAgentOptions(
allowed_tools=["Bash", "Read", "Write"],
),
):
print(message)
asyncio.run(main())
`)
);
// Run the agent inside the sandbox
const result = await sandbox.process.exec(
`ANTHROPIC_API_KEY=${process.env.ANTHROPIC_API_KEY} python3 /home/daytona/agent.py`
);
console.log(result.result);
await daytona.delete(sandbox);
}
@ -74,51 +88,64 @@ your agent can read and write memory with plain bash commands.
</Tab>
<Tab title="Python">
```bash
pip install claude-agent-sdk daytona-sdk
pip install daytona-sdk
```
```python agent.py
import asyncio
import os
from claude_agent_sdk import query, ClaudeAgentOptions
from daytona_sdk import Daytona, DaytonaConfig
async def main():
# 1. Create a Daytona sandbox
config = DaytonaConfig(
api_key=os.environ["DAYTONA_API_KEY"],
api_url="https://app.daytona.io/api",
)
daytona = Daytona(config)
sandbox = daytona.create()
config = DaytonaConfig(
api_key=os.environ["DAYTONA_API_KEY"],
api_url="https://app.daytona.io/api",
)
daytona = Daytona(config)
sandbox = daytona.create()
# 2. Install SMFS, log in, and mount
sandbox.process.exec("curl -fsSL https://smfs.ai/install | bash")
sandbox.process.exec(
f"~/.local/bin/smfs login --key {os.environ['SUPERMEMORY_API_KEY']}"
)
sandbox.process.exec(
"~/.local/bin/smfs mount my_agent --ephemeral --path /home/daytona/memory"
)
# Install SMFS, log in, and mount
sandbox.process.exec("curl -fsSL https://smfs.ai/install | bash")
sandbox.process.exec(
f"~/.local/bin/smfs login --key {os.environ['SUPERMEMORY_API_KEY']}"
)
sandbox.process.exec(
"~/.local/bin/smfs mount my_agent --ephemeral --path /home/daytona/memory"
)
# 3. Run a Claude agent inside the sandbox with bash access
async for message in query(
prompt="""You have access to a persistent memory filesystem mounted at /home/daytona/memory.
Use bash commands to explore it (ls, cat) and write notes to it.
# Install Claude Agent SDK inside the sandbox
sandbox.process.exec("pip install claude-agent-sdk")
First, read /home/daytona/memory/profile.md to learn about the user.
Then create /home/daytona/memory/session_notes.md with a summary of what you found.""",
options=ClaudeAgentOptions(
allowed_tools=["Bash", "Read", "Write"],
),
):
print(message)
# Write the agent script into the sandbox
AGENT_SCRIPT = '''
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
# 4. Clean up
sandbox.process.exec("~/.local/bin/smfs unmount my_agent 2>/dev/null")
daytona.delete(sandbox)
async def main():
async for message in query(
prompt="""You have a persistent memory filesystem at /home/daytona/memory.
Use bash to explore it (ls, cat) and write notes (echo "..." > file).
asyncio.run(main())
Read /home/daytona/memory/profile.md to learn about the user.
Then create /home/daytona/memory/session_notes.md summarizing what you found.""",
options=ClaudeAgentOptions(
allowed_tools=["Bash", "Read", "Write"],
),
):
print(message)
asyncio.run(main())
'''
sandbox.process.exec(
f"cat << 'EOF' > /home/daytona/run_agent.py\n{AGENT_SCRIPT}\nEOF"
)
# Run the agent inside the sandbox
result = sandbox.process.exec(
f"ANTHROPIC_API_KEY={os.environ['ANTHROPIC_API_KEY']}"
" python3 /home/daytona/run_agent.py"
)
print(result.result)
daytona.delete(sandbox)
```
</Tab>
</Tabs>
@ -131,7 +158,7 @@ your agent can read and write memory with plain bash commands.
## Tips
- Use `--ephemeral` when mounting inside sandboxes — it keeps the cache in memory
- Use `--ephemeral` when mounting inside sandboxes — keeps the cache in memory
only, but writes still push to Supermemory
- Use `smfs grep 'query'` for semantic search across all files in the container
- The agent can write structured data (JSON, markdown) to the mount and it

View file

@ -10,8 +10,8 @@ agent can read and write memory with plain bash commands.
1. Create an E2B sandbox
2. Install SMFS and mount a Supermemory container inside it
3. Run a Claude agent inside the sandbox — it uses `cat`, `ls`, `echo`, etc. on the mount
4. Everything the agent writes is persisted to Supermemory automatically
3. Install the Claude Agent SDK inside the sandbox and run the agent there
4. The agent uses `cat`, `ls`, `echo`, etc. on the mount — everything persists to Supermemory
## Prerequisites
@ -24,25 +24,26 @@ agent can read and write memory with plain bash commands.
<Tabs>
<Tab title="TypeScript">
```bash
npm install @anthropic-ai/claude-agent-sdk @e2b/code-interpreter
npm install @e2b/code-interpreter
```
```typescript agent.ts
import { Sandbox } from "@e2b/code-interpreter";
import { query, ClaudeAgentOptions } from "@anthropic-ai/claude-agent-sdk";
async function main() {
// 1. Create an E2B sandbox
const sandbox = await Sandbox.create({ timeoutMs: 300_000 });
// 2. Fix FUSE permissions (required in E2B)
// Fix FUSE permissions (required in E2B)
await sandbox.commands.run("sudo chmod 666 /dev/fuse");
await sandbox.commands.run(
"echo 'user_allow_other' | sudo tee -a /etc/fuse.conf > /dev/null"
);
// 3. Install SMFS, log in, and mount
await sandbox.commands.run("curl -fsSL https://smfs.ai/install | bash");
// Install SMFS, log in, and mount
await sandbox.commands.run(
"curl -fsSL https://smfs.ai/install | bash",
{ timeoutMs: 60_000 }
);
await sandbox.commands.run(
`~/.local/bin/smfs login --key ${process.env.SUPERMEMORY_API_KEY}`
);
@ -50,22 +51,40 @@ agent can read and write memory with plain bash commands.
"bash -c '~/.local/bin/smfs mount my_agent --ephemeral --path /home/user/memory --foreground > /tmp/smfs.log 2>&1 & sleep 5'"
);
// 4. Run a Claude agent inside the sandbox with bash access
for await (const message of query({
prompt: `You have access to a persistent memory filesystem mounted at /home/user/memory.
Use bash commands to explore it (ls, cat) and write notes to it (echo "..." > file).
// Install Claude Agent SDK inside the sandbox
await sandbox.commands.run(
"pip install claude-agent-sdk",
{ timeoutMs: 60_000 }
);
First, read /home/user/memory/profile.md to learn about the user.
Then create /home/user/memory/session_notes.md with a summary of what you found.`,
options: {
allowedTools: ["Bash", "Read", "Write"],
},
})) {
if (message.type === "text") console.log(message.text);
}
// Write the agent script into the sandbox
await sandbox.files.write("/home/user/agent.py", `
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
async for message in query(
prompt="""You have a persistent memory filesystem at /home/user/memory.
Use bash to explore it (ls, cat) and write notes (echo "..." > file).
Read /home/user/memory/profile.md to learn about the user.
Then create /home/user/memory/session_notes.md summarizing what you found.""",
options=ClaudeAgentOptions(
allowed_tools=["Bash", "Read", "Write"],
),
):
print(message)
asyncio.run(main())
`);
// Run the agent inside the sandbox
const result = await sandbox.commands.run(
`ANTHROPIC_API_KEY=${process.env.ANTHROPIC_API_KEY} python3 /home/user/agent.py`,
{ timeoutMs: 120_000 }
);
console.log(result.stdout);
// 5. Clean up
await sandbox.commands.run("~/.local/bin/smfs unmount my_agent 2>/dev/null");
await sandbox.kill();
}
@ -74,57 +93,70 @@ agent can read and write memory with plain bash commands.
</Tab>
<Tab title="Python">
```bash
pip install claude-agent-sdk e2b-code-interpreter
pip install e2b-code-interpreter
```
```python agent.py
import asyncio
import os
from claude_agent_sdk import query, ClaudeAgentOptions
from e2b_code_interpreter import Sandbox
async def main():
# 1. Create an E2B sandbox
sandbox = Sandbox.create(timeout=300)
sandbox = Sandbox.create(timeout=300)
# 2. Fix FUSE permissions (required in E2B)
sandbox.commands.run("sudo chmod 666 /dev/fuse")
sandbox.commands.run(
"echo 'user_allow_other' | sudo tee -a /etc/fuse.conf > /dev/null"
)
# Fix FUSE permissions (required in E2B)
sandbox.commands.run("sudo chmod 666 /dev/fuse")
sandbox.commands.run(
"echo 'user_allow_other' | sudo tee -a /etc/fuse.conf > /dev/null"
)
# 3. Install SMFS, log in, and mount
sandbox.commands.run("curl -fsSL https://smfs.ai/install | bash")
sandbox.commands.run(
f"~/.local/bin/smfs login --key {os.environ['SUPERMEMORY_API_KEY']}"
)
sandbox.commands.run(
"bash -c '~/.local/bin/smfs mount my_agent --ephemeral"
" --path /home/user/memory --foreground > /tmp/smfs.log 2>&1"
" & sleep 5'",
timeout=15,
)
# Install SMFS, log in, and mount
sandbox.commands.run(
"curl -fsSL https://smfs.ai/install | bash",
timeout=60,
)
sandbox.commands.run(
f"~/.local/bin/smfs login --key {os.environ['SUPERMEMORY_API_KEY']}"
)
sandbox.commands.run(
"bash -c '~/.local/bin/smfs mount my_agent --ephemeral"
" --path /home/user/memory --foreground > /tmp/smfs.log 2>&1"
" & sleep 5'",
timeout=15,
)
# 4. Run a Claude agent inside the sandbox with bash access
async for message in query(
prompt="""You have access to a persistent memory filesystem mounted at /home/user/memory.
Use bash commands to explore it (ls, cat) and write notes to it.
# Install Claude Agent SDK inside the sandbox
sandbox.commands.run("pip install claude-agent-sdk", timeout=60)
First, read /home/user/memory/profile.md to learn about the user.
Then create /home/user/memory/session_notes.md with a summary of what you found.""",
options=ClaudeAgentOptions(
allowed_tools=["Bash", "Read", "Write"],
),
):
print(message)
# Write the agent script into the sandbox
AGENT_SCRIPT = '''
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
# 5. Clean up
sandbox.commands.run(
"~/.local/bin/smfs unmount my_agent 2>/dev/null", timeout=10
)
sandbox.kill()
async def main():
async for message in query(
prompt="""You have a persistent memory filesystem at /home/user/memory.
Use bash to explore it (ls, cat) and write notes (echo "..." > file).
asyncio.run(main())
Read /home/user/memory/profile.md to learn about the user.
Then create /home/user/memory/session_notes.md summarizing what you found.""",
options=ClaudeAgentOptions(
allowed_tools=["Bash", "Read", "Write"],
),
):
print(message)
asyncio.run(main())
'''
sandbox.files.write("/home/user/run_agent.py", AGENT_SCRIPT)
# Run the agent inside the sandbox
result = sandbox.commands.run(
f"ANTHROPIC_API_KEY={os.environ['ANTHROPIC_API_KEY']}"
" python3 /home/user/run_agent.py",
timeout=120,
)
print(result.stdout)
sandbox.kill()
```
</Tab>
</Tabs>
@ -151,10 +183,8 @@ with it pre-installed:
```dockerfile e2b.Dockerfile
FROM e2b/code-interpreter:latest
# Pre-install SMFS
RUN curl -fsSL https://smfs.ai/install | bash
# Fix FUSE permissions
RUN pip install claude-agent-sdk
RUN chmod 666 /dev/fuse
RUN echo 'user_allow_other' >> /etc/fuse.conf
```
@ -163,11 +193,11 @@ RUN echo 'user_allow_other' >> /etc/fuse.conf
e2b template build -d e2b.Dockerfile
```
Then your agent code only needs to log in and mount — no install step.
Then your orchestrating code only needs to log in, mount, and run the agent.
## Tips
- Use `--ephemeral` when mounting inside sandboxes — it keeps the cache in memory
- Use `--ephemeral` when mounting inside sandboxes — keeps the cache in memory
only, but writes still push to Supermemory
- Use `smfs grep 'query'` for semantic search across all files in the container
- The agent can write structured data (JSON, markdown) to the mount and it

View file

@ -3,14 +3,14 @@ title: "Vercel AI SDK"
description: "Give your AI agent persistent memory using SMFS with the Vercel AI SDK"
---
Mount a Supermemory container on your server and give your Vercel AI SDK agent
access to it through a bash tool.
Mount a Supermemory container on your server and let a Claude agent access it
through the built-in bash tool.
## How it works
1. Install SMFS on your server and mount a Supermemory container
2. Define a bash tool that runs commands against the mount
3. The Vercel AI SDK agent uses the tool to read/write memory with standard commands
2. Run a Claude agent with bash tool access — it reads/writes the mount using standard commands
3. Everything the agent writes persists to Supermemory automatically
<Note>
The Vercel AI SDK runs in your server process (not in a sandbox). SMFS mounts
@ -20,101 +20,73 @@ access to it through a bash tool.
## Prerequisites
- A [Supermemory API key](https://supermemory.ai)
- An [OpenAI](https://platform.openai.com) or [Anthropic](https://console.anthropic.com) API key
- An [Anthropic API key](https://console.anthropic.com)
- SMFS installed on your server: `curl -fsSL https://smfs.ai/install | bash`
## Quick start
First, mount SMFS on your server:
```bash
npm install ai @ai-sdk/anthropic zod
smfs login --key $SUPERMEMORY_API_KEY
smfs mount my_agent --path ./memory
```
Then run the agent:
```bash
pip install claude-agent-sdk
```
```python agent.py
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
async for message in query(
prompt="""You have a persistent memory filesystem at ./memory.
Use bash to explore it (ls, cat) and write notes (echo "..." > file).
Read ./memory/profile.md to learn about the user.
Then create ./memory/session_notes.md summarizing what you found.""",
options=ClaudeAgentOptions(
allowed_tools=["Bash", "Read", "Write"],
cwd="./memory",
),
):
print(message)
asyncio.run(main())
```
Or with TypeScript:
```bash
npm install @anthropic-ai/claude-agent-sdk
```
```typescript agent.ts
import { generateText, tool } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
import { execSync } from "child_process";
// Mount SMFS before starting the server:
// smfs login --key $SUPERMEMORY_API_KEY
// smfs mount my_agent --path ./memory
const MEMORY_PATH = "./memory";
import { query } from "@anthropic-ai/claude-agent-sdk";
async function main() {
const result = await generateText({
model: anthropic("claude-sonnet-4-20250514"),
tools: {
bash: tool({
description:
"Run a bash command. The persistent memory filesystem is at " +
MEMORY_PATH,
parameters: z.object({ command: z.string() }),
execute: async ({ command }) => {
try {
return execSync(command, {
cwd: MEMORY_PATH,
encoding: "utf-8",
timeout: 10_000,
});
} catch (e: any) {
return e.stderr || e.message;
}
},
}),
for await (const message of query({
prompt: `You have a persistent memory filesystem at ./memory.
Use bash to explore it (ls, cat) and write notes (echo "..." > file).
Read ./memory/profile.md to learn about the user.
Then create ./memory/session_notes.md summarizing what you found.`,
options: {
allowedTools: ["Bash", "Read", "Write"],
cwd: "./memory",
},
maxSteps: 10,
prompt: `You have access to a persistent memory filesystem at ${MEMORY_PATH}.
Use the bash tool to explore it (ls, cat) and write notes (echo "..." > file).
Read profile.md to learn about the user, then create session_notes.md with a summary.`,
});
console.log(result.text);
})) {
if (message.type === "text") console.log(message.text);
}
}
main();
```
## Streaming
```typescript
import { streamText, tool } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
import { execSync } from "child_process";
const MEMORY_PATH = "./memory";
const result = streamText({
model: anthropic("claude-sonnet-4-20250514"),
tools: {
bash: tool({
description:
"Run a bash command against the memory filesystem at " + MEMORY_PATH,
parameters: z.object({ command: z.string() }),
execute: async ({ command }) => {
try {
return execSync(command, {
cwd: MEMORY_PATH,
encoding: "utf-8",
timeout: 10_000,
});
} catch (e: any) {
return e.stderr || e.message;
}
},
}),
},
maxSteps: 10,
prompt: "Read my memory and summarize what you know about me.",
});
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
}
```
## Tips
- Mount SMFS once when your server starts, not per-request