docs: replace ASCII diagrams with Mermaid, add both agent patterns

This commit is contained in:
Dhravya 2026-04-27 23:19:39 +00:00
parent a979f98e42
commit ae8108adbe
4 changed files with 431 additions and 106 deletions

View file

@ -9,24 +9,35 @@ agent can read and write memory using standard filesystem commands.
## How it works
```
┌──────────────────────────────────────────┐
│ Cloudflare Container │
│ │
│ ┌──────────┐ ┌────────────────────┐ │
│ │ Claude │───▶│ /memory │ │
│ │ Agent │ │ (SMFS mount) │ │
│ └──────────┘ └────────┬───────────┘ │
│ │ │
└───────────────────────────┼──────────────┘
┌───────▼───────┐
│ Supermemory │
└───────────────┘
There are two ways to wire SMFS into a Cloudflare Container — pick the one that
fits your architecture.
### Agent inside the container
The agent process runs inside the container with direct access to the SMFS
mount. The entrypoint sets up the mount and starts the agent.
```mermaid
graph LR
subgraph Cloudflare Container
Agent["Claude Agent"] -->|"cat, ls, echo"| Mount["/memory\n(SMFS mount)"]
end
Mount -->|sync| SM["Supermemory"]
```
SMFS and the Claude Agent SDK are baked into the container image. On startup,
the entrypoint mounts memory and runs the agent.
### Agent outside the container
The agent runs in a Cloudflare Worker and sends commands to the container over
HTTP. The container exposes a simple exec endpoint.
```mermaid
graph LR
Agent["Worker\n(agent logic)"] -->|"fetch('/exec')"| Container
subgraph Container ["Cloudflare Container"]
Mount["/memory\n(SMFS mount)"]
end
Mount -->|sync| SM["Supermemory"]
```
## Prerequisites
@ -35,7 +46,14 @@ the entrypoint mounts memory and runs the agent.
- A [Cloudflare account](https://dash.cloudflare.com) with Containers enabled
- [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/install-and-update/)
## 1. Dockerfile
---
## Pattern A: Agent inside the container
SMFS and the Claude Agent SDK are baked into the container image. On startup,
the entrypoint mounts memory and runs the agent.
### Dockerfile
```dockerfile Dockerfile
FROM python:3.12-slim
@ -54,7 +72,7 @@ RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
```
## 2. Entrypoint
### Entrypoint
```bash entrypoint.sh
#!/bin/bash
@ -67,20 +85,22 @@ sleep 3
exec python3 /app/agent.py
```
## 3. Agent
### Agent code
```python agent.py
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
MEMORY = "/memory"
async def main():
async for message in query(
prompt="You have a persistent memory filesystem at /memory. "
prompt=f"You have a persistent memory filesystem at {MEMORY}. "
"Read profile.md to learn about the user, then create "
"session_notes.md summarizing what you found.",
options=ClaudeAgentOptions(
allowed_tools=["Bash", "Read", "Write"],
cwd="/memory",
cwd=MEMORY,
),
):
print(message)
@ -88,7 +108,7 @@ async def main():
asyncio.run(main())
```
## 4. Deploy
### Deploy
```toml wrangler.toml
name = "memory-agent"
@ -106,19 +126,82 @@ wrangler secret put ANTHROPIC_API_KEY
wrangler deploy
```
## Worker frontend (optional)
---
Use a Worker as the HTTP frontend that triggers the container:
## Pattern B: Agent outside the container
The agent logic lives in a Worker. The container just runs SMFS and exposes an
HTTP endpoint for executing commands against the mount.
### Container (exec server)
```dockerfile Dockerfile
FROM python:3.12-slim
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
RUN curl -fsSL https://smfs.ai/install | bash -s -- 0.0.1-rc2
ENV PATH="/root/.local/bin:$PATH"
RUN pip install flask
COPY server.py /app/server.py
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
```
```bash entrypoint.sh
#!/bin/bash
set -e
smfs login --key "$SUPERMEMORY_API_KEY"
smfs mount my_agent --ephemeral --path /memory --foreground &
sleep 3
exec python3 /app/server.py
```
```python server.py
import subprocess
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/exec", methods=["POST"])
def exec_command():
cmd = request.json["command"]
result = subprocess.run(
cmd, shell=True, capture_output=True, text=True, cwd="/memory", timeout=10
)
return jsonify(stdout=result.stdout, stderr=result.stderr, code=result.returncode)
app.run(host="0.0.0.0", port=8080)
```
### Worker (agent logic)
```typescript worker.ts
export default {
async fetch(request: Request, env: any) {
const container = await env.MY_CONTAINER.start();
return container.fetch("/result");
const profile = await container
.fetch("/exec", {
method: "POST",
body: JSON.stringify({ command: "cat /memory/profile.md" }),
headers: { "Content-Type": "application/json" },
})
.then((r: Response) => r.json());
return Response.json({ profile: profile.stdout });
},
};
```
---
## Tips
- Use `--ephemeral` for container mounts — keeps the cache in memory only, but

View file

@ -14,23 +14,35 @@ your agent can read and write memory using standard filesystem commands.
[local mount](/smfs/providers/vercel) instead.
</Warning>
## How it works (once network is resolved)
## How it works
There are two ways to wire SMFS into a Daytona sandbox — pick the one that fits
your architecture.
### Agent inside the sandbox
The agent process runs inside the sandbox and accesses the SMFS mount directly.
```mermaid
graph LR
subgraph Daytona Sandbox
Agent["Claude Agent"] -->|"cat, ls, echo"| Mount["/home/daytona/memory\n(SMFS mount)"]
end
Mount -->|sync| SM["Supermemory"]
```
┌──────────────────────────────────────────┐
│ Daytona Sandbox │
│ │
│ ┌──────────┐ ┌────────────────────┐ │
│ │ Claude │───▶│ /home/daytona/ │ │
│ │ Agent │ │ memory │ │
│ │ │ │ (SMFS mount) │ │
│ └──────────┘ └────────┬───────────┘ │
│ │ │
└───────────────────────────┼──────────────┘
┌───────▼───────┐
│ Supermemory │
└───────────────┘
### Agent outside the sandbox
The agent runs in your orchestrating code and executes commands inside the
sandbox remotely.
```mermaid
graph LR
Agent["Claude Agent\n(your server)"] -->|"sandbox.process.exec()"| Sandbox
subgraph Sandbox ["Daytona Sandbox"]
Mount["/home/daytona/memory\n(SMFS mount)"]
end
Mount -->|sync| SM["Supermemory"]
```
## Prerequisites
@ -39,7 +51,11 @@ your agent can read and write memory using standard filesystem commands.
- A [Daytona API key](https://app.daytona.io) — go to **API Keys** in the sidebar
- An [Anthropic API key](https://console.anthropic.com)
## 1. Write your agent
---
## Pattern A: Agent inside the sandbox
### Agent code
```python agent.py
import asyncio
@ -62,7 +78,7 @@ async def main():
asyncio.run(main())
```
## 2. Run it
### Orchestration
<Tabs>
<Tab title="Python">
@ -80,17 +96,19 @@ asyncio.run(main())
},
)
# Install SMFS
# Install SMFS (from GitHub releases — smfs.ai is unreachable from Daytona)
sandbox.process.exec(
"mkdir -p $HOME/.local/bin && "
"curl -sL https://github.com/supermemoryai/smfs/releases/download/"
"v0.0.1-rc2/smfs-linux-x64 -o $HOME/.local/bin/smfs && "
"chmod +x $HOME/.local/bin/smfs"
)
# Fix FUSE config
# Fix FUSE config and install agent SDK
sandbox.process.exec(
"echo 'user_allow_other' | sudo tee -a /etc/fuse.conf > /dev/null"
)
sandbox.process.exec("pip install claude-agent-sdk")
# Mount memory
sandbox.process.exec("$HOME/.local/bin/smfs login --key $SUPERMEMORY_API_KEY")
@ -128,10 +146,11 @@ asyncio.run(main())
"chmod +x $HOME/.local/bin/smfs"
);
// Fix FUSE config
// Fix FUSE config and install agent SDK
await sandbox.process.exec(
"echo 'user_allow_other' | sudo tee -a /etc/fuse.conf > /dev/null"
);
await sandbox.process.exec("pip install claude-agent-sdk");
// Mount memory
await sandbox.process.exec(
@ -142,7 +161,7 @@ asyncio.run(main())
"--path /home/daytona/memory --foreground &' && sleep 3"
);
// Run the agent
// Upload and run the agent
const result = await sandbox.process.exec("python3 agent.py");
console.log(result.result);
@ -151,11 +170,106 @@ asyncio.run(main())
</Tab>
</Tabs>
<Note>
Daytona sandboxes can't reach `smfs.ai`, so the install downloads the binary
directly from GitHub releases. The SMFS binary and Claude Agent SDK both
install successfully — only the Supermemory API connection is blocked.
</Note>
---
## Pattern B: Agent outside the sandbox
The agent runs in your server process and executes commands inside the sandbox
remotely via `sandbox.process.exec()`.
<Tabs>
<Tab title="Python">
```python run.py
import os
from daytona_sdk import Daytona, DaytonaConfig
daytona = Daytona(DaytonaConfig(
api_key=os.environ["DAYTONA_API_KEY"],
))
sandbox = daytona.create(
env_vars={
"SUPERMEMORY_API_KEY": os.environ["SUPERMEMORY_API_KEY"],
},
)
# Install and mount SMFS
sandbox.process.exec(
"mkdir -p $HOME/.local/bin && "
"curl -sL https://github.com/supermemoryai/smfs/releases/download/"
"v0.0.1-rc2/smfs-linux-x64 -o $HOME/.local/bin/smfs && "
"chmod +x $HOME/.local/bin/smfs"
)
sandbox.process.exec(
"echo 'user_allow_other' | sudo tee -a /etc/fuse.conf > /dev/null"
)
sandbox.process.exec("$HOME/.local/bin/smfs login --key $SUPERMEMORY_API_KEY")
sandbox.process.exec(
"bash -c '$HOME/.local/bin/smfs mount my_agent --ephemeral"
" --path /home/daytona/memory --foreground &' && sleep 3"
)
# Agent runs here — executes commands in the sandbox
profile = sandbox.process.exec("cat /home/daytona/memory/profile.md")
print("Profile:", profile.result)
sandbox.process.exec(
"bash -c 'echo \"Session started at $(date)\" > /home/daytona/memory/session_notes.md'"
)
files = sandbox.process.exec("ls /home/daytona/memory")
print("Files:", files.result)
daytona.delete(sandbox)
```
</Tab>
<Tab title="TypeScript">
```typescript run.ts
import { Daytona } from "@daytonaio/sdk";
const daytona = new Daytona({
apiKey: process.env.DAYTONA_API_KEY!,
});
const sandbox = await daytona.create({
envVars: {
SUPERMEMORY_API_KEY: process.env.SUPERMEMORY_API_KEY!,
},
});
// Install and mount SMFS
await sandbox.process.exec(
"mkdir -p $HOME/.local/bin && " +
"curl -sL https://github.com/supermemoryai/smfs/releases/download/" +
"v0.0.1-rc2/smfs-linux-x64 -o $HOME/.local/bin/smfs && " +
"chmod +x $HOME/.local/bin/smfs"
);
await sandbox.process.exec(
"echo 'user_allow_other' | sudo tee -a /etc/fuse.conf > /dev/null"
);
await sandbox.process.exec(
"$HOME/.local/bin/smfs login --key $SUPERMEMORY_API_KEY"
);
await sandbox.process.exec(
"bash -c '$HOME/.local/bin/smfs mount my_agent --ephemeral " +
"--path /home/daytona/memory --foreground &' && sleep 3"
);
// Agent runs here — executes commands in the sandbox
const profile = await sandbox.process.exec("cat /home/daytona/memory/profile.md");
console.log("Profile:", profile.result);
await sandbox.process.exec(
`bash -c 'echo "Session started at $(date)" > /home/daytona/memory/session_notes.md'`
);
const files = await sandbox.process.exec("ls /home/daytona/memory");
console.log("Files:", files.result);
await daytona.delete(sandbox);
```
</Tab>
</Tabs>
---
## Tips
@ -164,3 +278,9 @@ asyncio.run(main())
- The binary installs to `~/.local/bin/` which isn't on PATH by default in
Daytona's zsh — use the full path or `export PATH=$HOME/.local/bin:$PATH`
- Use `pip install claude-agent-sdk` to install the agent SDK (PyPI is reachable)
<Note>
Daytona sandboxes can't reach `smfs.ai`, so the install downloads the binary
directly from GitHub releases. The SMFS binary and Claude Agent SDK both
install successfully — only the Supermemory API connection is blocked.
</Note>

View file

@ -8,25 +8,36 @@ agent can read and write memory using standard filesystem commands.
## How it works
```
┌─────────────────────────────────────────┐
│ E2B Sandbox │
│ │
│ ┌──────────┐ ┌───────────────────┐ │
│ │ Claude │───▶│ /home/user/memory │ │
│ │ Agent │ │ (SMFS mount) │ │
│ └──────────┘ └────────┬──────────┘ │
│ │ │
└───────────────────────────┼──────────────┘
┌───────▼───────┐
│ Supermemory │
└───────────────┘
There are two ways to wire SMFS into an E2B sandbox — pick the one that fits
your architecture.
### Agent inside the sandbox
The agent process runs inside the sandbox and accesses the SMFS mount directly.
Your orchestrating code just boots the sandbox and kicks off the agent.
```mermaid
graph LR
subgraph E2B Sandbox
Agent["Claude Agent"] -->|"cat, ls, echo"| Mount["/home/user/memory\n(SMFS mount)"]
end
Mount -->|sync| SM["Supermemory"]
```
The agent runs inside the sandbox. SMFS mounts a Supermemory container as a
regular directory. The agent uses `cat`, `ls`, `echo` — standard bash. Writes
sync to Supermemory automatically.
### Agent outside the sandbox
The agent runs in your orchestrating code and executes commands inside the
sandbox remotely. Useful when you want to keep the agent loop in your own
infra.
```mermaid
graph LR
Agent["Claude Agent\n(your server)"] -->|"sbx.commands.run()"| Sandbox
subgraph Sandbox ["E2B Sandbox"]
Mount["/home/user/memory\n(SMFS mount)"]
end
Mount -->|sync| SM["Supermemory"]
```
## Prerequisites
@ -52,10 +63,14 @@ RUN pip install claude-agent-sdk
e2b template build -d e2b.Dockerfile
```
## 2. Write your agent
---
This is the code that runs inside the sandbox. It's just normal Python —
nothing sandbox-specific:
## Pattern A: Agent inside the sandbox
The agent runs inside the sandbox as a Python script. Your orchestrating code
just sets up the mount and starts it.
### Agent code
```python agent.py
import asyncio
@ -78,7 +93,7 @@ async def main():
asyncio.run(main())
```
## 3. Run it
### Orchestration
<Tabs>
<Tab title="Python">
@ -105,8 +120,9 @@ asyncio.run(main())
" --path /home/user/memory --foreground &' && sleep 3"
)
# Run the agent
result = sbx.commands.run("python3 agent.py", timeout=120)
# Upload and run the agent
sbx.files.write("/home/user/agent.py", open("agent.py").read())
result = sbx.commands.run("python3 /home/user/agent.py", timeout=120)
print(result.stdout)
sbx.kill()
@ -115,6 +131,7 @@ asyncio.run(main())
<Tab title="TypeScript">
```typescript run.ts
import { Sandbox } from "@e2b/code-interpreter";
import { readFileSync } from "fs";
const sbx = await Sandbox.create({
template: "your-template-id",
@ -134,8 +151,9 @@ asyncio.run(main())
"bash -c 'smfs mount my_agent --ephemeral --path /home/user/memory --foreground &' && sleep 3"
);
// Run the agent
const result = await sbx.commands.run("python3 agent.py", {
// Upload and run the agent
await sbx.files.write("/home/user/agent.py", readFileSync("agent.py", "utf-8"));
const result = await sbx.commands.run("python3 /home/user/agent.py", {
timeoutMs: 120_000,
});
console.log(result.stdout);
@ -145,12 +163,92 @@ asyncio.run(main())
</Tab>
</Tabs>
---
## Pattern B: Agent outside the sandbox
The agent runs in your server process and executes commands inside the sandbox
remotely via `sbx.commands.run()`. The SMFS mount lives inside the sandbox —
the agent never touches the filesystem directly.
<Tabs>
<Tab title="Python">
```python run.py
import os
from e2b_code_interpreter import Sandbox
sbx = Sandbox.create(
template="your-template-id",
timeout=300,
envs={
"SUPERMEMORY_API_KEY": os.environ["SUPERMEMORY_API_KEY"],
},
)
# Set up SMFS inside the sandbox
sbx.commands.run("sudo chmod 666 /dev/fuse")
sbx.commands.run("smfs login --key $SUPERMEMORY_API_KEY")
sbx.commands.run(
"bash -c 'smfs mount my_agent --ephemeral"
" --path /home/user/memory --foreground &' && sleep 3"
)
# Agent runs here — executes commands in the sandbox
profile = sbx.commands.run("cat /home/user/memory/profile.md").stdout
print("Profile:", profile)
sbx.commands.run(
"sudo bash -c 'echo \"Session started at $(date)\" > /home/user/memory/session_notes.md'"
)
files = sbx.commands.run("ls /home/user/memory").stdout
print("Files:", files)
sbx.kill()
```
</Tab>
<Tab title="TypeScript">
```typescript run.ts
import { Sandbox } from "@e2b/code-interpreter";
const sbx = await Sandbox.create({
template: "your-template-id",
timeoutMs: 300_000,
envs: {
SUPERMEMORY_API_KEY: process.env.SUPERMEMORY_API_KEY!,
},
});
// Set up SMFS inside the sandbox
await sbx.commands.run("sudo chmod 666 /dev/fuse");
await sbx.commands.run("smfs login --key $SUPERMEMORY_API_KEY");
await sbx.commands.run(
"bash -c 'smfs mount my_agent --ephemeral --path /home/user/memory --foreground &' && sleep 3"
);
// Agent runs here — executes commands in the sandbox
const profile = await sbx.commands.run("cat /home/user/memory/profile.md");
console.log("Profile:", profile.stdout);
await sbx.commands.run(
`sudo bash -c 'echo "Session started at $(date)" > /home/user/memory/session_notes.md'`
);
const files = await sbx.commands.run("ls /home/user/memory");
console.log("Files:", files.stdout);
await sbx.kill();
```
</Tab>
</Tabs>
<Note>
The FUSE mount is owned by root. The Claude agent handles this automatically
with the Bash tool (it uses `sudo` when needed). If you're writing files
manually, use `sudo bash -c 'echo "..." > /path/file'`.
The FUSE mount is owned by root. When writing files from outside the agent,
use `sudo bash -c 'echo "..." > /path/file'`.
</Note>
---
## Tips
- Use `--ephemeral` for sandbox mounts — keeps the cache in memory only, but

View file

@ -8,24 +8,35 @@ write memory using standard filesystem commands.
## How it works
```
┌──────────────────────────────────────┐
│ Your Server │
│ │
│ ┌──────────┐ ┌────────────────┐ │
│ │ Claude │───▶│ ./memory │ │
│ │ Agent │ │ (SMFS mount) │ │
│ └──────────┘ └───────┬────────┘ │
│ │ │
└──────────────────────────┼───────────┘
┌───────▼───────┐
│ Supermemory │
└───────────────┘
There are two ways to wire SMFS into a Vercel-based agent — pick the one that
fits your architecture.
### Claude Agent SDK (agent has full filesystem access)
The agent runs as a separate process with direct access to the SMFS mount.
Best when you want the agent to have full bash, read, and write capabilities.
```mermaid
graph LR
subgraph Your Server
Agent["Claude Agent"] -->|"cat, ls, echo"| Mount["./memory\n(SMFS mount)"]
end
Mount -->|sync| SM["Supermemory"]
```
SMFS mounts directly on the host. No sandbox needed — the agent runs in your
server process and accesses memory through the filesystem.
### Vercel AI SDK (agent uses a tool)
The agent runs inside `generateText` and accesses memory through a bash tool
you define. Best when you're building an API route and want to keep everything
in one TypeScript process.
```mermaid
graph LR
subgraph Your Server
AI["generateText()"] -->|"bash tool"| Mount["./memory\n(SMFS mount)"]
end
Mount -->|sync| SM["Supermemory"]
```
## Prerequisites
@ -33,27 +44,36 @@ server process and accesses memory through the filesystem.
- An [Anthropic API key](https://console.anthropic.com)
- SMFS installed: `curl -fsSL https://smfs.ai/install | bash`
## 1. Mount memory
## Mount memory
Start the mount once when your server boots — not per-request:
```bash
smfs login --key $SUPERMEMORY_API_KEY
smfs mount my_agent --path ./memory
```
## 2. Write your agent
---
## Pattern A: Claude Agent SDK
Write a standalone agent script. Nothing server-specific — just Python that
reads and writes files.
```python agent.py
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
MEMORY = "./memory"
async def main():
async for message in query(
prompt="You have a persistent memory filesystem at ./memory. "
prompt=f"You have a persistent memory filesystem at {MEMORY}. "
"Read profile.md to learn about the user, then create "
"session_notes.md summarizing what you found.",
options=ClaudeAgentOptions(
allowed_tools=["Bash", "Read", "Write"],
cwd="./memory",
cwd=MEMORY,
),
):
print(message)
@ -65,13 +85,12 @@ asyncio.run(main())
python3 agent.py
```
That's it. The agent reads and writes files in `./memory` using standard bash
commands. Everything syncs to Supermemory automatically.
---
## Using with the Vercel AI SDK
## Pattern B: Vercel AI SDK
If you're building an API route with the Vercel AI SDK, expose the memory
filesystem as a tool:
Expose the memory filesystem as a bash tool inside an API route. The agent
calls the tool to run commands against the mount.
```typescript api/agent.ts
import { generateText, tool } from "ai";
@ -79,7 +98,6 @@ import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
import { execSync } from "child_process";
// SMFS is mounted at ./memory (started when the server boots)
const MEMORY = "./memory";
export async function POST(req: Request) {
@ -93,7 +111,11 @@ export async function POST(req: Request) {
parameters: z.object({ command: z.string() }),
execute: async ({ command }) => {
try {
return execSync(command, { cwd: MEMORY, encoding: "utf-8", timeout: 10_000 });
return execSync(command, {
cwd: MEMORY,
encoding: "utf-8",
timeout: 10_000,
});
} catch (e: any) {
return e.stderr || e.message;
}
@ -108,6 +130,8 @@ export async function POST(req: Request) {
}
```
---
## Tips
- Mount SMFS once when your server starts, not per-request