mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-09-05 08:06:19 +00:00
docs: rewrite SMFS provider guides to use mount + Claude Agents SDK
All four guides now show the mount pattern as the primary approach: mount SMFS inside the sandbox, then run a Claude agent with bash tool access so it reads/writes memory using standard commands. - E2B: tested end-to-end (install, login, mount, read, write, grep) - Daytona: mount pattern with TLS warning - Vercel AI SDK: SMFS mounted on host, bash tool for the agent - Cloudflare: Container with SMFS pre-installed in Dockerfile
This commit is contained in:
parent
8ae6b708a9
commit
faeef886ee
4 changed files with 361 additions and 642 deletions
|
|
@ -1,205 +1,122 @@
|
|||
---
|
||||
title: "Cloudflare"
|
||||
sidebarTitle: "Cloudflare"
|
||||
description: "Add persistent memory to Cloudflare Workers agents with SMFS."
|
||||
icon: "cloud"
|
||||
description: "Give your AI agent persistent memory inside a Cloudflare Container using SMFS"
|
||||
---
|
||||
|
||||
[Cloudflare Workers](https://workers.cloudflare.com) run at the edge in V8 isolates — no filesystem, no shell. That's exactly what `@supermemory/bash` is built for. It gives your Worker a virtual bash environment backed by Supermemory, so your agent can `ls`, `cat`, `grep`, and write to a persistent memory container without needing a real filesystem.
|
||||
Mount a Supermemory container inside a
|
||||
[Cloudflare Container](https://developers.cloudflare.com/containers/) so your
|
||||
agent can read and write memory with plain bash commands.
|
||||
|
||||
## Architecture
|
||||
## How it works
|
||||
|
||||
Cloudflare Workers can't mount SMFS (no FUSE, no disk). Instead, use `@supermemory/bash` as a tool for your agent. The bash tool runs entirely in-process — no child processes, no disk I/O, no native dependencies.
|
||||
|
||||
```
|
||||
Request → Worker → LLM → calls bash tool → @supermemory/bash → Supermemory API
|
||||
```
|
||||
|
||||
<Note>
|
||||
If you're using [Cloudflare Containers](https://developers.cloudflare.com/containers/) (full Linux containers at the edge), you can install the `smfs` binary directly. See [Alternative: Cloudflare Containers](#alternative-cloudflare-containers) below.
|
||||
</Note>
|
||||
1. Build a container image with SMFS 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
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A [Supermemory](https://console.supermemory.ai) account and API key
|
||||
- A [Cloudflare](https://dash.cloudflare.com) account
|
||||
- Node.js 18+ and Wrangler CLI
|
||||
- A [Supermemory API key](https://supermemory.ai)
|
||||
- An [Anthropic API key](https://console.anthropic.com)
|
||||
- A [Cloudflare account](https://dash.cloudflare.com) with Containers enabled
|
||||
- [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/install-and-update/)
|
||||
|
||||
## 1. Set up a Worker
|
||||
## Container setup
|
||||
|
||||
```bash
|
||||
npm create cloudflare@latest -- my-memory-agent
|
||||
cd my-memory-agent
|
||||
npm install @supermemory/bash
|
||||
```
|
||||
|
||||
Add your Supermemory API key as a secret:
|
||||
|
||||
```bash
|
||||
npx wrangler secret put SUPERMEMORY_API_KEY
|
||||
```
|
||||
|
||||
## 2. Build an agent with memory
|
||||
|
||||
```typescript src/index.ts
|
||||
import { createBash } from "@supermemory/bash";
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const { bash, toolDescription } = await createBash({
|
||||
apiKey: env.SUPERMEMORY_API_KEY,
|
||||
containerTag: "user_42",
|
||||
});
|
||||
|
||||
// Read the user's profile
|
||||
const profile = await bash.exec("cat /profile.md");
|
||||
|
||||
// Search memory semantically
|
||||
const results = await bash.exec("sgrep 'project deadlines'");
|
||||
|
||||
// Write new memory
|
||||
await bash.exec('echo "Met with client on April 27" >> /notes.md');
|
||||
|
||||
return new Response(JSON.stringify({
|
||||
profile: profile.stdout,
|
||||
search: results.stdout,
|
||||
}));
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## 3. Full agent with tool calling
|
||||
|
||||
Wire `@supermemory/bash` as a tool for an LLM. This example uses the OpenAI API, but any provider works:
|
||||
|
||||
```typescript src/index.ts
|
||||
import { createBash } from "@supermemory/bash";
|
||||
|
||||
interface Env {
|
||||
SUPERMEMORY_API_KEY: string;
|
||||
OPENAI_API_KEY: string;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const { messages, userId } = await request.json() as {
|
||||
messages: any[];
|
||||
userId: string;
|
||||
};
|
||||
|
||||
const { bash, toolDescription } = await createBash({
|
||||
apiKey: env.SUPERMEMORY_API_KEY,
|
||||
containerTag: `user_${userId}`,
|
||||
});
|
||||
|
||||
// Call the LLM with the bash tool
|
||||
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Authorization": `Bearer ${env.OPENAI_API_KEY}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: "gpt-4o",
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: `You have persistent memory via a bash tool.
|
||||
Use it to remember things about the user.
|
||||
Start by reading /profile.md for context.`,
|
||||
},
|
||||
...messages,
|
||||
],
|
||||
tools: [{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "bash",
|
||||
description: toolDescription,
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { cmd: { type: "string" } },
|
||||
required: ["cmd"],
|
||||
},
|
||||
},
|
||||
}],
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json() as any;
|
||||
|
||||
// Handle tool calls
|
||||
if (data.choices[0].message.tool_calls) {
|
||||
const toolCall = data.choices[0].message.tool_calls[0];
|
||||
const cmd = JSON.parse(toolCall.function.arguments).cmd;
|
||||
const result = await bash.exec(cmd);
|
||||
|
||||
// Send the result back to the LLM
|
||||
const followUp = await fetch("https://api.openai.com/v1/chat/completions", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Authorization": `Bearer ${env.OPENAI_API_KEY}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: "gpt-4o",
|
||||
messages: [
|
||||
...messages,
|
||||
data.choices[0].message,
|
||||
{
|
||||
role: "tool",
|
||||
tool_call_id: toolCall.id,
|
||||
content: JSON.stringify(result),
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
const followUpData = await followUp.json() as any;
|
||||
return new Response(followUpData.choices[0].message.content);
|
||||
}
|
||||
|
||||
return new Response(data.choices[0].message.content);
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## Alternative: Cloudflare Containers
|
||||
|
||||
[Cloudflare Containers](https://developers.cloudflare.com/containers/) give you full Linux environments at the edge. Unlike Workers, containers have a real filesystem and shell — so you can install and mount SMFS directly.
|
||||
### Dockerfile
|
||||
|
||||
```dockerfile Dockerfile
|
||||
FROM node:20-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
|
||||
|
||||
# Your agent code
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
RUN npm install
|
||||
# Install the Claude Agent SDK
|
||||
RUN npm install -g @anthropic-ai/claude-agent-sdk
|
||||
|
||||
CMD ["node", "agent.js"]
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
```
|
||||
|
||||
Inside the container, mount SMFS as you would on any Linux machine:
|
||||
### Entrypoint
|
||||
|
||||
```bash
|
||||
smfs login --key $SUPERMEMORY_API_KEY
|
||||
smfs mount agent_memory --ephemeral --path /memory
|
||||
```bash 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
|
||||
```
|
||||
|
||||
Your agent can then use standard Unix commands to interact with memory.
|
||||
### Agent
|
||||
|
||||
```typescript agent.ts
|
||||
import { query } from "@anthropic-ai/claude-agent-sdk";
|
||||
|
||||
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).
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
## Worker + Container pattern
|
||||
|
||||
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;
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
```toml wrangler.toml
|
||||
name = "memory-agent"
|
||||
main = "worker.ts"
|
||||
|
||||
[[containers]]
|
||||
class_name = "MY_CONTAINER"
|
||||
image = "./Dockerfile"
|
||||
max_instances = 5
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
- **Workers have a 30-second CPU time limit.** Keep tool call chains short. If your agent needs many steps, consider using Durable Objects for longer-running workflows.
|
||||
- **One container tag per user.** Use the user's ID as the container tag for isolated memory per user.
|
||||
- **`sgrep` for semantic search.** Inside the bash tool, `sgrep "query"` searches by meaning. Regular `grep` does literal matching.
|
||||
- **Cache-friendly.** `@supermemory/bash` warms its path index on `createBash`. For Workers that handle many requests, consider caching the instance in a Durable Object.
|
||||
|
||||
## Resources
|
||||
|
||||
- [Cloudflare Workers docs](https://developers.cloudflare.com/workers/)
|
||||
- [Cloudflare Containers docs](https://developers.cloudflare.com/containers/)
|
||||
- [SMFS Bash Tool reference](/smfs/bash-tool)
|
||||
- [Supermemory quickstart](/quickstart)
|
||||
- Use `--ephemeral` when mounting inside containers — it 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:
|
||||
```bash
|
||||
wrangler secret put SUPERMEMORY_API_KEY
|
||||
wrangler secret put ANTHROPIC_API_KEY
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,121 +1,71 @@
|
|||
---
|
||||
title: "Daytona"
|
||||
sidebarTitle: "Daytona"
|
||||
description: "Give your Daytona sandboxes persistent memory with SMFS."
|
||||
icon: "server"
|
||||
description: "Give your AI agent persistent memory inside a Daytona sandbox using SMFS"
|
||||
---
|
||||
|
||||
[Daytona](https://www.daytona.io) gives AI agents isolated Linux sandboxes that boot in milliseconds. Pair it with SMFS and your agent gets both safe code execution **and** persistent memory it can `ls`, `cat`, and `grep`.
|
||||
Mount a Supermemory container inside a [Daytona](https://daytona.io) sandbox so
|
||||
your agent can read and write memory with plain bash commands.
|
||||
|
||||
## Architecture
|
||||
## How it works
|
||||
|
||||
Daytona sandboxes run full Linux with shell access, filesystem, and network. There are two ways to wire SMFS in:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Bash Tool in your agent code" icon="terminal">
|
||||
Use `@supermemory/bash` in the code that **orchestrates** the sandbox. The agent reads memory via the bash tool, then sends code to Daytona for execution. Works everywhere.
|
||||
</Card>
|
||||
<Card title="Mount inside the sandbox" icon="hard-drive">
|
||||
Install the `smfs` binary inside the sandbox and mount a container directly. Requires unrestricted outbound HTTPS from the sandbox.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Note>
|
||||
Some Daytona sandbox configurations may restrict outbound TLS to certain hosts. If `smfs mount` or `smfs login` fails with connection errors, use the Bash Tool pattern instead — it runs in your orchestrating code, not inside the sandbox.
|
||||
</Note>
|
||||
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
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A [Supermemory](https://console.supermemory.ai) account and API key
|
||||
- A [Daytona](https://app.daytona.io) account and API key
|
||||
- Node.js 18+ or Python 3.10+
|
||||
- A [Supermemory API key](https://supermemory.ai)
|
||||
- A [Daytona API key](https://app.daytona.io) — go to **API Keys** in the sidebar
|
||||
- An [Anthropic API key](https://console.anthropic.com)
|
||||
|
||||
## 1. Get your API keys
|
||||
|
||||
### Supermemory
|
||||
|
||||
1. Go to [console.supermemory.ai](https://console.supermemory.ai)
|
||||
2. Navigate to **Settings → API Keys**
|
||||
3. Create a new key and copy it
|
||||
|
||||
### Daytona
|
||||
|
||||
1. Go to [app.daytona.io](https://app.daytona.io)
|
||||
2. Sign up and verify your email
|
||||
3. Set your default region (US or EU)
|
||||
4. Navigate to **API Keys** in the sidebar
|
||||
5. Click **Create Key**, give it a name, and copy the key
|
||||
|
||||
<Warning>
|
||||
You can only view a Daytona API key once. Store it somewhere safe immediately after creation.
|
||||
</Warning>
|
||||
|
||||
## 2. Install the SDKs
|
||||
## Quick start
|
||||
|
||||
<Tabs>
|
||||
<Tab title="TypeScript">
|
||||
```bash
|
||||
npm install @supermemory/bash @daytonaio/sdk
|
||||
npm install @anthropic-ai/claude-agent-sdk @daytonaio/sdk
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Python">
|
||||
```bash
|
||||
pip install daytona-sdk
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 3. Build an agent with memory + code execution
|
||||
|
||||
<Tabs>
|
||||
<Tab title="TypeScript">
|
||||
The recommended TypeScript pattern: use `@supermemory/bash` for memory in your orchestrating code and the Daytona SDK for code execution. The LLM gets both as tools.
|
||||
|
||||
```typescript agent.ts
|
||||
import { createBash } from "@supermemory/bash";
|
||||
import { Daytona } from "@daytonaio/sdk";
|
||||
import { generateText, tool } from "ai";
|
||||
import { openai } from "@ai-sdk/openai";
|
||||
import { z } from "zod";
|
||||
import { query } from "@anthropic-ai/claude-agent-sdk";
|
||||
|
||||
async function main() {
|
||||
// 1. Set up memory (SMFS bash tool)
|
||||
const { bash, toolDescription } = await createBash({
|
||||
apiKey: process.env.SUPERMEMORY_API_KEY!,
|
||||
containerTag: "agent_memory",
|
||||
});
|
||||
|
||||
// 2. Set up code execution (Daytona sandbox)
|
||||
// 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();
|
||||
|
||||
// 3. Give the LLM both tools
|
||||
const result = await generateText({
|
||||
model: openai("gpt-4o"),
|
||||
tools: {
|
||||
memory: tool({
|
||||
description: toolDescription,
|
||||
parameters: z.object({ cmd: z.string() }),
|
||||
execute: async ({ cmd }) => bash.exec(cmd),
|
||||
}),
|
||||
execute_code: tool({
|
||||
description: "Run code in an isolated sandbox",
|
||||
parameters: z.object({ code: z.string() }),
|
||||
execute: async ({ code }) => {
|
||||
const res = await sandbox.process.exec(code);
|
||||
return res.result;
|
||||
},
|
||||
}),
|
||||
},
|
||||
prompt: "Check my memory for the user's preferred language, then write and run a hello world in that language.",
|
||||
});
|
||||
// 2. 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}`
|
||||
);
|
||||
await sandbox.process.exec(
|
||||
"~/.local/bin/smfs mount my_agent --ephemeral --path /home/daytona/memory"
|
||||
);
|
||||
|
||||
console.log(result.text);
|
||||
// 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).
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 4. Clean up
|
||||
await sandbox.process.exec(
|
||||
"~/.local/bin/smfs unmount my_agent 2>/dev/null"
|
||||
);
|
||||
await daytona.delete(sandbox);
|
||||
}
|
||||
|
||||
|
|
@ -123,64 +73,66 @@ Daytona sandboxes run full Linux with shell access, filesystem, and network. The
|
|||
```
|
||||
</Tab>
|
||||
<Tab title="Python">
|
||||
In Python, install and mount SMFS inside the Daytona sandbox. The agent reads and writes memory via standard shell commands.
|
||||
|
||||
<Warning>
|
||||
This requires unrestricted outbound HTTPS from the sandbox. If `smfs login` fails with a connection error, see the note at the top of this page.
|
||||
</Warning>
|
||||
```bash
|
||||
pip install claude-agent-sdk daytona-sdk
|
||||
```
|
||||
|
||||
```python agent.py
|
||||
import asyncio
|
||||
import os
|
||||
from claude_agent_sdk import query, ClaudeAgentOptions
|
||||
from daytona_sdk import Daytona, DaytonaConfig
|
||||
|
||||
# 1. Set up code execution (Daytona sandbox)
|
||||
config = DaytonaConfig(
|
||||
api_key=os.environ["DAYTONA_API_KEY"],
|
||||
api_url="https://app.daytona.io/api",
|
||||
)
|
||||
daytona = Daytona(config)
|
||||
sandbox = daytona.create()
|
||||
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()
|
||||
|
||||
# 2. Install SMFS inside the sandbox
|
||||
sandbox.process.exec(
|
||||
"curl -fsSL https://smfs.ai/install | bash"
|
||||
)
|
||||
# 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"
|
||||
)
|
||||
|
||||
# 3. Log in and mount
|
||||
sandbox.process.exec(
|
||||
f"~/.local/bin/smfs login --key {os.environ['SUPERMEMORY_API_KEY']}"
|
||||
)
|
||||
sandbox.process.exec(
|
||||
"~/.local/bin/smfs mount agent_memory --ephemeral"
|
||||
)
|
||||
# 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.
|
||||
|
||||
# 4. Read the auto-generated profile
|
||||
response = sandbox.process.exec("cat agent_memory/profile.md")
|
||||
print(response.result)
|
||||
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)
|
||||
|
||||
# 5. Semantic search
|
||||
response = sandbox.process.exec(
|
||||
"~/.local/bin/smfs grep 'preferred language'"
|
||||
)
|
||||
print(response.result)
|
||||
# 4. Clean up
|
||||
sandbox.process.exec("~/.local/bin/smfs unmount my_agent 2>/dev/null")
|
||||
daytona.delete(sandbox)
|
||||
|
||||
# 6. Clean up
|
||||
sandbox.process.exec("~/.local/bin/smfs unmount agent_memory")
|
||||
daytona.delete(sandbox)
|
||||
asyncio.run(main())
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Note>
|
||||
Some Daytona datacenter IPs may be blocked by upstream firewalls. If
|
||||
`smfs login` or `smfs mount` fails with a TLS connection error, check
|
||||
that outbound HTTPS to `api.supermemory.ai` is not restricted.
|
||||
</Note>
|
||||
|
||||
## Tips
|
||||
|
||||
- **Use `--ephemeral` for sandboxes.** Sandbox filesystems are temporary. Ephemeral mode avoids writing a local SQLite cache that will be thrown away.
|
||||
- **One container, many sandboxes.** Mount the same container tag from multiple sandboxes. Bidirectional sync keeps them in step.
|
||||
- **Give each agent a subdirectory.** If multiple agents share a container, scope them to `/agent_a/`, `/agent_b/`, etc. They can still read across the whole mount.
|
||||
- **Clean up sandboxes.** Call `daytona.delete(sandbox)` when done to avoid burning through free credits.
|
||||
|
||||
## Resources
|
||||
|
||||
- [Daytona docs](https://www.daytona.io/docs)
|
||||
- [Daytona SDK reference](https://www.daytona.io/docs/en/tools/api/)
|
||||
- [SMFS Mount reference](/smfs/mount)
|
||||
- [SMFS Bash Tool reference](/smfs/bash-tool)
|
||||
- Use `--ephemeral` when mounting inside sandboxes — it 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
|
||||
persists across sandbox sessions via Supermemory
|
||||
|
|
|
|||
|
|
@ -1,108 +1,71 @@
|
|||
---
|
||||
title: "E2B"
|
||||
sidebarTitle: "E2B"
|
||||
description: "Give your E2B sandboxes persistent memory with SMFS."
|
||||
icon: "cube"
|
||||
description: "Give your AI agent persistent memory inside an E2B sandbox using SMFS"
|
||||
---
|
||||
|
||||
[E2B](https://e2b.dev) runs AI-generated code in secure Firecracker microVMs. Pair it with SMFS and your agent gets both safe code execution **and** persistent memory it can `ls`, `cat`, and `grep`.
|
||||
Mount a Supermemory container inside an [E2B](https://e2b.dev) sandbox so your
|
||||
agent can read and write memory with plain bash commands.
|
||||
|
||||
## Architecture
|
||||
## How it works
|
||||
|
||||
E2B sandboxes are ephemeral Linux microVMs with full shell access and unrestricted network. Two ways to wire SMFS in:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Mount inside the sandbox" icon="hard-drive">
|
||||
Install the `smfs` binary inside the sandbox and mount a container. The agent uses standard Unix commands to read and write memory.
|
||||
</Card>
|
||||
<Card title="Bash Tool in your agent code" icon="terminal">
|
||||
Use `@supermemory/bash` in the code that **orchestrates** the sandbox. Memory lives in your agent code; code execution lives in E2B.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
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
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A [Supermemory](https://console.supermemory.ai) account and API key
|
||||
- An [E2B](https://e2b.dev) account and API key
|
||||
- Node.js 18+ or Python 3.10+
|
||||
- A [Supermemory API key](https://supermemory.ai)
|
||||
- An [E2B API key](https://e2b.dev)
|
||||
- An [Anthropic API key](https://console.anthropic.com)
|
||||
|
||||
## 1. Get your API keys
|
||||
|
||||
### Supermemory
|
||||
|
||||
1. Go to [console.supermemory.ai](https://console.supermemory.ai)
|
||||
2. Navigate to **Settings → API Keys**
|
||||
3. Create a new key and copy it
|
||||
|
||||
### E2B
|
||||
|
||||
1. Go to [e2b.dev](https://e2b.dev) and sign up
|
||||
2. Open the [Dashboard](https://e2b.dev/dashboard)
|
||||
3. Navigate to **API Keys**
|
||||
4. Copy your API key
|
||||
|
||||
## 2. Install the SDKs
|
||||
## Quick start
|
||||
|
||||
<Tabs>
|
||||
<Tab title="TypeScript">
|
||||
```bash
|
||||
npm install @supermemory/bash @e2b/code-interpreter
|
||||
npm install @anthropic-ai/claude-agent-sdk @e2b/code-interpreter
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Python">
|
||||
```bash
|
||||
pip install e2b-code-interpreter
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 3. Build an agent with memory + code execution
|
||||
|
||||
<Tabs>
|
||||
<Tab title="TypeScript">
|
||||
The recommended TypeScript pattern: use `@supermemory/bash` for memory in your orchestrating code and the E2B SDK for code execution. The LLM gets both as tools.
|
||||
|
||||
```typescript agent.ts
|
||||
import { createBash } from "@supermemory/bash";
|
||||
import { Sandbox } from "@e2b/code-interpreter";
|
||||
import { generateText, tool } from "ai";
|
||||
import { openai } from "@ai-sdk/openai";
|
||||
import { z } from "zod";
|
||||
import { query, ClaudeAgentOptions } from "@anthropic-ai/claude-agent-sdk";
|
||||
|
||||
async function main() {
|
||||
// 1. Set up memory (SMFS bash tool)
|
||||
const { bash, toolDescription } = await createBash({
|
||||
apiKey: process.env.SUPERMEMORY_API_KEY!,
|
||||
containerTag: "agent_memory",
|
||||
});
|
||||
// 1. Create an E2B sandbox
|
||||
const sandbox = await Sandbox.create({ timeoutMs: 300_000 });
|
||||
|
||||
// 2. Set up code execution (E2B sandbox)
|
||||
const sandbox = await Sandbox.create();
|
||||
// 2. 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. Give the LLM both tools
|
||||
const result = await generateText({
|
||||
model: openai("gpt-4o"),
|
||||
tools: {
|
||||
memory: tool({
|
||||
description: toolDescription,
|
||||
parameters: z.object({ cmd: z.string() }),
|
||||
execute: async ({ cmd }) => bash.exec(cmd),
|
||||
}),
|
||||
execute_code: tool({
|
||||
description: "Run Python code in an isolated sandbox",
|
||||
parameters: z.object({ code: z.string() }),
|
||||
execute: async ({ code }) => {
|
||||
const execution = await sandbox.runCode(code);
|
||||
return execution.text;
|
||||
},
|
||||
}),
|
||||
// 3. Install SMFS, log in, and mount
|
||||
await sandbox.commands.run("curl -fsSL https://smfs.ai/install | bash");
|
||||
await sandbox.commands.run(
|
||||
`~/.local/bin/smfs login --key ${process.env.SUPERMEMORY_API_KEY}`
|
||||
);
|
||||
await sandbox.commands.run(
|
||||
"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).
|
||||
|
||||
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"],
|
||||
},
|
||||
prompt: "Check my memory for the user's preferences, then write a script that uses them.",
|
||||
});
|
||||
})) {
|
||||
if (message.type === "text") console.log(message.text);
|
||||
}
|
||||
|
||||
console.log(result.text);
|
||||
|
||||
// 4. Clean up
|
||||
// 5. Clean up
|
||||
await sandbox.commands.run("~/.local/bin/smfs unmount my_agent 2>/dev/null");
|
||||
await sandbox.kill();
|
||||
}
|
||||
|
||||
|
|
@ -110,122 +73,80 @@ E2B sandboxes are ephemeral Linux microVMs with full shell access and unrestrict
|
|||
```
|
||||
</Tab>
|
||||
<Tab title="Python">
|
||||
In Python, mount SMFS inside the E2B sandbox. The agent reads and writes memory via standard shell commands.
|
||||
```bash
|
||||
pip install claude-agent-sdk e2b-code-interpreter
|
||||
```
|
||||
|
||||
```python agent.py
|
||||
import asyncio
|
||||
import os
|
||||
from claude_agent_sdk import query, ClaudeAgentOptions
|
||||
from e2b_code_interpreter import Sandbox
|
||||
|
||||
# 1. Create an E2B sandbox
|
||||
sandbox = Sandbox.create(timeout=300)
|
||||
async def main():
|
||||
# 1. Create an E2B sandbox
|
||||
sandbox = Sandbox.create(timeout=300)
|
||||
|
||||
# 2. Fix FUSE permissions (required in E2B sandboxes)
|
||||
sandbox.commands.run("sudo chmod 666 /dev/fuse")
|
||||
sandbox.commands.run(
|
||||
"echo 'user_allow_other' | sudo tee -a /etc/fuse.conf > /dev/null"
|
||||
)
|
||||
# 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"
|
||||
)
|
||||
|
||||
# 3. Install SMFS and log in
|
||||
sandbox.commands.run("curl -fsSL https://smfs.ai/install | bash")
|
||||
sandbox.commands.run(
|
||||
f"~/.local/bin/smfs login --key {os.environ['SUPERMEMORY_API_KEY']}"
|
||||
)
|
||||
# 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,
|
||||
)
|
||||
|
||||
# 4. Mount memory
|
||||
sandbox.commands.run(
|
||||
"bash -c '~/.local/bin/smfs mount agent_memory --ephemeral"
|
||||
" --path /home/user/memory --foreground > /tmp/smfs.log 2>&1"
|
||||
" & sleep 5 && echo MOUNTED'",
|
||||
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.
|
||||
|
||||
# 5. Read the auto-generated profile
|
||||
result = sandbox.commands.run("cat /home/user/memory/profile.md")
|
||||
print(result.stdout)
|
||||
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)
|
||||
|
||||
# 6. Semantic search
|
||||
result = sandbox.commands.run(
|
||||
"~/.local/bin/smfs grep 'preferred language'"
|
||||
)
|
||||
print(result.stdout)
|
||||
# 5. Clean up
|
||||
sandbox.commands.run(
|
||||
"~/.local/bin/smfs unmount my_agent 2>/dev/null", timeout=10
|
||||
)
|
||||
sandbox.kill()
|
||||
|
||||
# 7. Clean up
|
||||
sandbox.commands.run(
|
||||
"~/.local/bin/smfs unmount agent_memory 2>/dev/null",
|
||||
timeout=10,
|
||||
)
|
||||
sandbox.kill()
|
||||
asyncio.run(main())
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Mount SMFS inside the sandbox
|
||||
|
||||
E2B sandboxes have full network access and FUSE support, so you can install and mount SMFS directly. Two setup steps are needed first:
|
||||
|
||||
<Warning>
|
||||
E2B sandboxes require FUSE permission fixes before mounting. Run these commands once after creating the sandbox:
|
||||
E2B sandboxes require two FUSE permission fixes before mounting:
|
||||
1. `sudo chmod 666 /dev/fuse` — the device exists but is root-only by default
|
||||
2. `echo 'user_allow_other' | sudo tee -a /etc/fuse.conf` — needed for the `allow_other` mount option
|
||||
|
||||
```bash
|
||||
sudo chmod 666 /dev/fuse
|
||||
echo 'user_allow_other' | sudo tee -a /etc/fuse.conf > /dev/null
|
||||
```
|
||||
Without these, `smfs mount` will fail with a permission error.
|
||||
</Warning>
|
||||
|
||||
```python
|
||||
from e2b_code_interpreter import Sandbox
|
||||
import os
|
||||
|
||||
sandbox = Sandbox.create(timeout=300)
|
||||
|
||||
# One-time FUSE setup
|
||||
sandbox.commands.run("sudo chmod 666 /dev/fuse")
|
||||
sandbox.commands.run(
|
||||
"echo 'user_allow_other' | sudo tee -a /etc/fuse.conf > /dev/null"
|
||||
)
|
||||
|
||||
# Install SMFS
|
||||
sandbox.commands.run("curl -fsSL https://smfs.ai/install | bash")
|
||||
|
||||
# Log in
|
||||
sandbox.commands.run(
|
||||
f"~/.local/bin/smfs login --key {os.environ['SUPERMEMORY_API_KEY']}"
|
||||
)
|
||||
|
||||
# Mount with ephemeral mode (recommended for sandboxes)
|
||||
# Run in background since mount is a long-running daemon
|
||||
sandbox.commands.run(
|
||||
"bash -c '~/.local/bin/smfs mount agent_memory --ephemeral"
|
||||
" --path /home/user/memory --foreground > /tmp/smfs.log 2>&1"
|
||||
" & sleep 5 && echo MOUNTED'",
|
||||
timeout=15,
|
||||
)
|
||||
|
||||
# Read the auto-generated profile
|
||||
result = sandbox.commands.run("cat /home/user/memory/profile.md")
|
||||
print(result.stdout)
|
||||
|
||||
# Write to memory (use sudo — FUSE mount is owned by root)
|
||||
sandbox.commands.run(
|
||||
"sudo bash -c 'echo \"User prefers Python\" > /home/user/memory/notes.md'"
|
||||
)
|
||||
|
||||
# Semantic grep works inside the mount
|
||||
result = sandbox.commands.run("~/.local/bin/smfs grep 'deadlines'")
|
||||
print(result.stdout)
|
||||
|
||||
# Clean up
|
||||
sandbox.commands.run("~/.local/bin/smfs unmount agent_memory 2>/dev/null", timeout=10)
|
||||
sandbox.kill()
|
||||
```
|
||||
|
||||
<Note>
|
||||
The FUSE mount is owned by root. Writing files requires `sudo` (e.g., `sudo bash -c 'echo "..." > /path/file'`). Reads work without sudo.
|
||||
The FUSE mount is owned by root. Writing files requires `sudo`
|
||||
(e.g., `sudo bash -c 'echo "..." > /path/file'`). Reads work without sudo.
|
||||
The Claude agent handles this automatically when using the Bash tool.
|
||||
</Note>
|
||||
|
||||
## Custom E2B template with SMFS pre-installed
|
||||
## Custom E2B template
|
||||
|
||||
For faster startup, bake SMFS and the FUSE fixes into a custom E2B template:
|
||||
For production, bake SMFS into a custom E2B template so every sandbox starts
|
||||
with it pre-installed:
|
||||
|
||||
```dockerfile e2b.Dockerfile
|
||||
FROM e2b/code-interpreter:latest
|
||||
|
|
@ -233,7 +154,8 @@ FROM e2b/code-interpreter:latest
|
|||
# Pre-install SMFS
|
||||
RUN curl -fsSL https://smfs.ai/install | bash
|
||||
|
||||
# Fix FUSE permissions for SMFS mount
|
||||
# Fix FUSE permissions
|
||||
RUN chmod 666 /dev/fuse
|
||||
RUN echo 'user_allow_other' >> /etc/fuse.conf
|
||||
```
|
||||
|
||||
|
|
@ -241,38 +163,12 @@ RUN echo 'user_allow_other' >> /etc/fuse.conf
|
|||
e2b template build -d e2b.Dockerfile
|
||||
```
|
||||
|
||||
Then use your custom template — no setup needed at runtime:
|
||||
|
||||
```python
|
||||
sandbox = Sandbox.create(template="your-custom-template")
|
||||
|
||||
# FUSE device still needs chmod at runtime (device nodes reset on boot)
|
||||
sandbox.commands.run("sudo chmod 666 /dev/fuse")
|
||||
|
||||
# Mount directly
|
||||
sandbox.commands.run(
|
||||
f"~/.local/bin/smfs login --key {os.environ['SUPERMEMORY_API_KEY']}"
|
||||
)
|
||||
sandbox.commands.run(
|
||||
"bash -c '~/.local/bin/smfs mount agent_memory --ephemeral"
|
||||
" --path /home/user/memory --foreground > /tmp/smfs.log 2>&1"
|
||||
" & sleep 5'",
|
||||
timeout=15,
|
||||
)
|
||||
```
|
||||
Then your agent code only needs to log in and mount — no install step.
|
||||
|
||||
## Tips
|
||||
|
||||
- **Use `--ephemeral` for sandboxes.** E2B sandboxes are short-lived. Ephemeral mode avoids writing a local cache that will be thrown away.
|
||||
- **Always fix FUSE permissions.** E2B sandboxes need `sudo chmod 666 /dev/fuse` and `user_allow_other` in `/etc/fuse.conf` before mounting.
|
||||
- **Mount in background.** The SMFS daemon is long-running. Use `bash -c '... --foreground &'` with a sleep to let it initialize.
|
||||
- **Custom templates save time.** Pre-install SMFS in a template to skip the install step on every sandbox boot.
|
||||
- **E2B sandboxes have a timeout.** Default is 5 minutes. Pass `timeout=300` (or more) when creating the sandbox if your agent needs longer.
|
||||
- **One container, many sandboxes.** Mount the same container tag from multiple E2B sandboxes. Bidirectional sync keeps them in step.
|
||||
|
||||
## Resources
|
||||
|
||||
- [E2B docs](https://e2b.dev/docs)
|
||||
- [E2B SDK reference](https://e2b.dev/docs/sdk-reference)
|
||||
- [SMFS Mount reference](/smfs/mount)
|
||||
- [SMFS Bash Tool reference](/smfs/bash-tool)
|
||||
- Use `--ephemeral` when mounting inside sandboxes — it 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
|
||||
persists across sandbox sessions via Supermemory
|
||||
|
|
|
|||
|
|
@ -1,63 +1,73 @@
|
|||
---
|
||||
title: "Vercel AI SDK"
|
||||
sidebarTitle: "Vercel AI SDK"
|
||||
description: "Add persistent memory to agents built with the Vercel AI SDK."
|
||||
icon: "triangle"
|
||||
description: "Give your AI agent persistent memory using SMFS with the Vercel AI SDK"
|
||||
---
|
||||
|
||||
The [Vercel AI SDK](https://sdk.vercel.ai) is the most popular framework for building AI agents in TypeScript. SMFS plugs in as a tool — give your agent a `bash` tool backed by `@supermemory/bash` and it can `ls`, `cat`, `grep`, and write to a Supermemory container without any filesystem to manage.
|
||||
Mount a Supermemory container on your server and give your Vercel AI SDK agent
|
||||
access to it through a bash tool.
|
||||
|
||||
## Architecture
|
||||
## How it works
|
||||
|
||||
The Vercel AI SDK uses a tool-calling pattern: you define tools, the model decides when to call them. `@supermemory/bash` fits this perfectly — it's a single tool that exposes a virtual filesystem backed by Supermemory.
|
||||
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
|
||||
|
||||
```
|
||||
User → AI SDK → LLM → calls bash tool → @supermemory/bash → Supermemory API
|
||||
```
|
||||
|
||||
No sandbox needed. The bash tool runs in your server process and handles all the memory operations.
|
||||
<Note>
|
||||
The Vercel AI SDK runs in your server process (not in a sandbox). SMFS mounts
|
||||
directly on the host where your server runs.
|
||||
</Note>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A [Supermemory](https://console.supermemory.ai) account and API key
|
||||
- Node.js 18+
|
||||
- An LLM provider (OpenAI, Anthropic, Google, etc.)
|
||||
- A [Supermemory API key](https://supermemory.ai)
|
||||
- An [OpenAI](https://platform.openai.com) or [Anthropic](https://console.anthropic.com) API key
|
||||
- SMFS installed on your server: `curl -fsSL https://smfs.ai/install | bash`
|
||||
|
||||
## 1. Install
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
npm install @supermemory/bash ai @ai-sdk/openai zod
|
||||
npm install ai @ai-sdk/anthropic zod
|
||||
```
|
||||
|
||||
## 2. Create an agent with memory
|
||||
|
||||
```typescript agent.ts
|
||||
import { createBash } from "@supermemory/bash";
|
||||
import { generateText, tool } from "ai";
|
||||
import { openai } from "@ai-sdk/openai";
|
||||
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";
|
||||
|
||||
async function main() {
|
||||
// Set up the SMFS bash tool
|
||||
const { bash, toolDescription } = await createBash({
|
||||
apiKey: process.env.SUPERMEMORY_API_KEY!,
|
||||
containerTag: "user_42",
|
||||
});
|
||||
|
||||
const result = await generateText({
|
||||
model: openai("gpt-4o"),
|
||||
model: anthropic("claude-sonnet-4-20250514"),
|
||||
tools: {
|
||||
bash: tool({
|
||||
description: toolDescription,
|
||||
parameters: z.object({ cmd: z.string() }),
|
||||
execute: async ({ cmd }) => bash.exec(cmd),
|
||||
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;
|
||||
}
|
||||
},
|
||||
}),
|
||||
},
|
||||
maxSteps: 10,
|
||||
system: `You have access to a persistent memory filesystem via the bash tool.
|
||||
Use it to remember things about the user across conversations.
|
||||
Start by reading /profile.md to see what you already know.`,
|
||||
prompt: "What do you remember about me?",
|
||||
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);
|
||||
|
|
@ -66,103 +76,47 @@ Start by reading /profile.md to see what you already know.`,
|
|||
main();
|
||||
```
|
||||
|
||||
That's it. The model will call `bash({ cmd: "cat /profile.md" })` to read the user's profile, `bash({ cmd: "ls /" })` to browse memory, and `bash({ cmd: "grep 'preferences'" })` to search semantically.
|
||||
## Streaming
|
||||
|
||||
## 3. Multi-step agent with memory
|
||||
|
||||
A more complete example with conversation history and memory persistence:
|
||||
|
||||
```typescript chat.ts
|
||||
import { createBash } from "@supermemory/bash";
|
||||
import { generateText, tool, CoreMessage } from "ai";
|
||||
import { openai } from "@ai-sdk/openai";
|
||||
import { z } from "zod";
|
||||
|
||||
// Create one bash instance per user (reuse across requests)
|
||||
const userBashInstances = new Map();
|
||||
|
||||
async function getBash(userId: string) {
|
||||
if (!userBashInstances.has(userId)) {
|
||||
const instance = await createBash({
|
||||
apiKey: process.env.SUPERMEMORY_API_KEY!,
|
||||
containerTag: `user_${userId}`,
|
||||
});
|
||||
userBashInstances.set(userId, instance);
|
||||
}
|
||||
return userBashInstances.get(userId);
|
||||
}
|
||||
|
||||
export async function chat(userId: string, messages: CoreMessage[]) {
|
||||
const { bash, toolDescription } = await getBash(userId);
|
||||
|
||||
return generateText({
|
||||
model: openai("gpt-4o"),
|
||||
tools: {
|
||||
bash: tool({
|
||||
description: toolDescription,
|
||||
parameters: z.object({ cmd: z.string() }),
|
||||
execute: async ({ cmd }) => bash.exec(cmd),
|
||||
}),
|
||||
},
|
||||
maxSteps: 10,
|
||||
system: `You are a helpful assistant with persistent memory.
|
||||
|
||||
Use the bash tool to:
|
||||
- Read /profile.md at the start of each conversation for context
|
||||
- Save important facts to /memory.md
|
||||
- Search with grep when looking for specific information
|
||||
- Organize notes in subdirectories as needed`,
|
||||
messages,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## With Vercel AI SDK Streams
|
||||
|
||||
For streaming responses in a Next.js app:
|
||||
|
||||
```typescript app/api/chat/route.ts
|
||||
import { createBash } from "@supermemory/bash";
|
||||
```typescript
|
||||
import { streamText, tool } from "ai";
|
||||
import { openai } from "@ai-sdk/openai";
|
||||
import { anthropic } from "@ai-sdk/anthropic";
|
||||
import { z } from "zod";
|
||||
import { execSync } from "child_process";
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const { messages, userId } = await req.json();
|
||||
const MEMORY_PATH = "./memory";
|
||||
|
||||
const { bash, toolDescription } = await createBash({
|
||||
apiKey: process.env.SUPERMEMORY_API_KEY!,
|
||||
containerTag: `user_${userId}`,
|
||||
});
|
||||
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.",
|
||||
});
|
||||
|
||||
const result = streamText({
|
||||
model: openai("gpt-4o"),
|
||||
tools: {
|
||||
bash: tool({
|
||||
description: toolDescription,
|
||||
parameters: z.object({ cmd: z.string() }),
|
||||
execute: async ({ cmd }) => bash.exec(cmd),
|
||||
}),
|
||||
},
|
||||
maxSteps: 10,
|
||||
system: "You have persistent memory via the bash tool. Read /profile.md for context.",
|
||||
messages,
|
||||
});
|
||||
|
||||
return result.toDataStreamResponse();
|
||||
for await (const chunk of result.textStream) {
|
||||
process.stdout.write(chunk);
|
||||
}
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
- **One `createBash` per user.** Use the user's ID as the container tag. Each user gets their own isolated memory.
|
||||
- **Read `profile.md` first.** Tell the model to `cat /profile.md` at the start of each conversation. It's a live digest of everything in the container.
|
||||
- **`sgrep` for semantic search.** Inside the bash tool, `sgrep "query"` searches by meaning, not just text. Regular `grep` does literal matching.
|
||||
- **Cache the bash instance.** `createBash` warms the path index on startup. Reuse the instance across requests for the same user.
|
||||
- **Works with any LLM provider.** Swap `openai("gpt-4o")` for `anthropic("claude-sonnet-4-20250514")`, `google("gemini-2.0-flash")`, or any AI SDK-compatible provider.
|
||||
|
||||
## Resources
|
||||
|
||||
- [Vercel AI SDK docs](https://sdk.vercel.ai/docs)
|
||||
- [SMFS Bash Tool reference](/smfs/bash-tool)
|
||||
- [Supermemory quickstart](/quickstart)
|
||||
- Mount SMFS once when your server starts, not per-request
|
||||
- Use `smfs grep 'query'` for semantic search across all files in the container
|
||||
- Use `--ephemeral` if you don't need a local cache on the server
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue