diff --git a/apps/docs/smfs/providers/cloudflare.mdx b/apps/docs/smfs/providers/cloudflare.mdx index bc027d86..93ee0d4b 100644 --- a/apps/docs/smfs/providers/cloudflare.mdx +++ b/apps/docs/smfs/providers/cloudflare.mdx @@ -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 -``` - - - 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. - +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 { - 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 { - 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 + ``` diff --git a/apps/docs/smfs/providers/daytona.mdx b/apps/docs/smfs/providers/daytona.mdx index 97ad1bd4..decdfce1 100644 --- a/apps/docs/smfs/providers/daytona.mdx +++ b/apps/docs/smfs/providers/daytona.mdx @@ -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: - - - - 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. - - - Install the `smfs` binary inside the sandbox and mount a container directly. Requires unrestricted outbound HTTPS from the sandbox. - - - - - 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. - +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 - - - You can only view a Daytona API key once. Store it somewhere safe immediately after creation. - - -## 2. Install the SDKs +## Quick start ```bash - npm install @supermemory/bash @daytonaio/sdk + npm install @anthropic-ai/claude-agent-sdk @daytonaio/sdk ``` - - - ```bash - pip install daytona-sdk - ``` - - - -## 3. Build an agent with memory + code execution - - - - 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 ``` - In Python, install and mount SMFS inside the Daytona sandbox. The agent reads and writes memory via standard shell commands. - - - 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. - + ```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()) ``` + + 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. + + ## 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 diff --git a/apps/docs/smfs/providers/e2b.mdx b/apps/docs/smfs/providers/e2b.mdx index 9d5c9034..fbd363b1 100644 --- a/apps/docs/smfs/providers/e2b.mdx +++ b/apps/docs/smfs/providers/e2b.mdx @@ -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: - - - - Install the `smfs` binary inside the sandbox and mount a container. The agent uses standard Unix commands to read and write memory. - - - Use `@supermemory/bash` in the code that **orchestrates** the sandbox. Memory lives in your agent code; code execution lives in E2B. - - +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 ```bash - npm install @supermemory/bash @e2b/code-interpreter + npm install @anthropic-ai/claude-agent-sdk @e2b/code-interpreter ``` - - - ```bash - pip install e2b-code-interpreter - ``` - - - -## 3. Build an agent with memory + code execution - - - - 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 ``` - 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()) ``` -## 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: - - 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. -```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() -``` - - 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. -## 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 diff --git a/apps/docs/smfs/providers/vercel.mdx b/apps/docs/smfs/providers/vercel.mdx index 615ac953..5295021c 100644 --- a/apps/docs/smfs/providers/vercel.mdx +++ b/apps/docs/smfs/providers/vercel.mdx @@ -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. + + The Vercel AI SDK runs in your server process (not in a sandbox). SMFS mounts + directly on the host where your server runs. + ## 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