mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-09-07 08:26:15 +00:00
docs: replace ASCII diagrams with Mermaid, add both agent patterns
This commit is contained in:
parent
a979f98e42
commit
ae8108adbe
4 changed files with 431 additions and 106 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue