mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-05-17 12:20:04 +00:00
Co-authored-by: Dhravya <63950637+Dhravya@users.noreply.github.com> Co-authored-by: docs <docs@supermemory.ai>
252 lines
8.3 KiB
Text
252 lines
8.3 KiB
Text
---
|
|
title: "Bash Tool (Python)"
|
|
sidebarTitle: "Bash Tool (Python)"
|
|
description: "supermemory-bash. The SMFS idea wrapped as a single agent tool, for Python agents and serverless runtimes."
|
|
icon: "terminal"
|
|
---
|
|
|
|
`supermemory-bash` is the SMFS idea wrapped as a single agent tool: `run_bash(command)`. The "filesystem" is your Supermemory container. Runs anywhere Python runs. AWS Lambda, Modal, Fly Machines, Cloud Run, your laptop. No mount, no FUSE, no local disk.
|
|
|
|
Reach for the Bash Tool when your agent runs somewhere it can't mount a real filesystem.
|
|
|
|
## Install
|
|
|
|
```bash
|
|
pip install supermemory-bash
|
|
```
|
|
|
|
Or with uv:
|
|
|
|
```bash
|
|
uv add supermemory-bash
|
|
```
|
|
|
|
## Quickstart
|
|
|
|
```python
|
|
import asyncio
|
|
import os
|
|
from supermemory_bash import create_bash
|
|
|
|
|
|
async def main() -> None:
|
|
result = await create_bash(
|
|
api_key=os.environ["SUPERMEMORY_API_KEY"],
|
|
container_tag="user_42",
|
|
)
|
|
bash = result.bash
|
|
r = await bash.exec("ls /")
|
|
print(r.stdout)
|
|
|
|
|
|
asyncio.run(main())
|
|
```
|
|
|
|
`create_bash` returns a `CreateBashResult` with:
|
|
|
|
- `bash`: a `Shell` instance with `.exec(cmd)`
|
|
- `tool_description`: a pre-written tool description ready to hand to the model
|
|
- `configure_memory_paths(paths)`: scope which paths get extracted into Supermemory
|
|
- `refresh()`: re-prime the path index after external writes
|
|
|
|
## Use it as a model tool
|
|
|
|
### Anthropic SDK
|
|
|
|
Pass `tool_description` straight into Claude's tool definition and run a normal agent loop. Each `tool_use` block calls `bash.exec` and the result goes back as a `tool_result`.
|
|
|
|
```python
|
|
import asyncio
|
|
import os
|
|
|
|
import anthropic
|
|
from supermemory_bash import create_bash
|
|
|
|
|
|
async def run_agent(user_message: str) -> str:
|
|
result = await create_bash(
|
|
api_key=os.environ["SUPERMEMORY_API_KEY"],
|
|
container_tag="user_42",
|
|
)
|
|
bash = result.bash
|
|
|
|
client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
|
|
tools = [
|
|
{
|
|
"name": "bash",
|
|
"description": result.tool_description,
|
|
"input_schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"cmd": {"type": "string", "description": "The bash command to run."}
|
|
},
|
|
"required": ["cmd"],
|
|
},
|
|
}
|
|
]
|
|
|
|
messages = [{"role": "user", "content": user_message}]
|
|
|
|
for _ in range(10):
|
|
response = client.messages.create(
|
|
model="claude-sonnet-4-20250514",
|
|
max_tokens=4096,
|
|
tools=tools,
|
|
messages=messages,
|
|
)
|
|
|
|
if response.stop_reason == "end_turn":
|
|
for block in response.content:
|
|
if hasattr(block, "text"):
|
|
return block.text
|
|
return ""
|
|
|
|
messages.append({"role": "assistant", "content": response.content})
|
|
tool_results = []
|
|
for block in response.content:
|
|
if block.type == "tool_use":
|
|
cmd = block.input.get("cmd", "")
|
|
r = await bash.exec(cmd)
|
|
output = r.stdout
|
|
if r.stderr:
|
|
output += f"\n[stderr]: {r.stderr}"
|
|
if r.exit_code != 0:
|
|
output += f"\n[exit_code]: {r.exit_code}"
|
|
tool_results.append(
|
|
{
|
|
"type": "tool_result",
|
|
"tool_use_id": block.id,
|
|
"content": output or "(no output)",
|
|
}
|
|
)
|
|
messages.append({"role": "user", "content": tool_results})
|
|
|
|
return "(max steps reached)"
|
|
|
|
|
|
asyncio.run(run_agent("What's in my notes about the Q3 launch?"))
|
|
```
|
|
|
|
### OpenAI SDK
|
|
|
|
Same idea with OpenAI's function-calling format. Define a single `bash` function, dispatch each `tool_calls` entry to `bash.exec`, and feed the output back as a `tool` message.
|
|
|
|
```python
|
|
import asyncio
|
|
import json
|
|
import os
|
|
|
|
from openai import OpenAI
|
|
from supermemory_bash import create_bash
|
|
|
|
|
|
async def run_agent(user_message: str) -> str:
|
|
result = await create_bash(
|
|
api_key=os.environ["SUPERMEMORY_API_KEY"],
|
|
container_tag="user_42",
|
|
)
|
|
bash = result.bash
|
|
|
|
client = OpenAI()
|
|
tools = [
|
|
{
|
|
"type": "function",
|
|
"function": {
|
|
"name": "bash",
|
|
"description": result.tool_description,
|
|
"parameters": {
|
|
"type": "object",
|
|
"properties": {"cmd": {"type": "string"}},
|
|
"required": ["cmd"],
|
|
},
|
|
},
|
|
}
|
|
]
|
|
|
|
messages = [{"role": "user", "content": user_message}]
|
|
|
|
for _ in range(10):
|
|
response = client.chat.completions.create(
|
|
model="gpt-4o",
|
|
messages=messages,
|
|
tools=tools,
|
|
)
|
|
message = response.choices[0].message
|
|
|
|
if not message.tool_calls:
|
|
return message.content or ""
|
|
|
|
messages.append(message.model_dump(exclude_none=True))
|
|
for call in message.tool_calls:
|
|
args = json.loads(call.function.arguments or "{}")
|
|
r = await bash.exec(args.get("cmd", ""))
|
|
output = r.stdout
|
|
if r.stderr:
|
|
output += f"\n[stderr]: {r.stderr}"
|
|
if r.exit_code != 0:
|
|
output += f"\n[exit_code]: {r.exit_code}"
|
|
messages.append(
|
|
{
|
|
"role": "tool",
|
|
"tool_call_id": call.id,
|
|
"content": output or "(no output)",
|
|
}
|
|
)
|
|
|
|
return "(max steps reached)"
|
|
|
|
|
|
asyncio.run(run_agent("List my notes"))
|
|
```
|
|
|
|
### Claude Agent SDK
|
|
|
|
The [Claude Agent SDK](https://docs.claude.com/en/api/agent-sdk/overview) ships with built-in `Bash`, `Read`, and `Write` tools. If your agent runs somewhere SMFS can be mounted (a long-lived process on macOS or Linux), point those built-ins at an SMFS mount and you don't need `supermemory-bash` at all — the agent just sees your container as a directory.
|
|
|
|
See [Mount SMFS](/smfs/mount) for setup, or the [provider guides](/smfs/overview#use-smfs-with-your-sandbox-provider) for sandbox-specific instructions.
|
|
|
|
## Memory
|
|
|
|
The Bash Tool inherits SMFS memory semantics. By default, files named `user.md` or `memory.md` are extracted as memories. Configure additional memory paths after construction:
|
|
|
|
```python
|
|
result = await create_bash(api_key=api_key, container_tag=container_tag)
|
|
bash = result.bash
|
|
await result.configure_memory_paths(["/notes/", "/journal.md"])
|
|
```
|
|
|
|
Trailing `/` matches recursively. No slash matches an exact file. Pass `[]` to disable memory generation.
|
|
|
|
The container also exposes a virtual `profile.md` at the root: a live digest of everything in the container. Read it once at the start of a session to give the model context without walking every file.
|
|
|
|
```python
|
|
r = await bash.exec("cat /profile.md")
|
|
print(r.stdout)
|
|
```
|
|
|
|
## Commands the agent can run
|
|
|
|
The Python tool exposes the same command surface as the TypeScript version: standard Unix builtins (`pwd`, `cd`, `ls`, `cat`, `stat`, `mkdir`, `rm`, `mv`, `cp`, `echo`), search and text utilities (`grep`, `find`, `head`, `tail`, `wc`, `sort`, `sed`, `awk`), plus the custom `sgrep <query> [path]` for semantic search across the container. Pipes, redirects, conditionals, loops, and file tests all work.
|
|
|
|
See the [TypeScript Bash Tool reference](/smfs/bash-tool#commands-the-agent-can-run) for the full list.
|
|
|
|
## Configuration
|
|
|
|
| Option | Default | Purpose |
|
|
| --- | --- | --- |
|
|
| `api_key` | required | Supermemory API key |
|
|
| `container_tag` | required | Container to expose as the filesystem |
|
|
| `base_url` | `None` | Override the API endpoint |
|
|
| `eager_load` | `True` | Warm the path index when the instance starts |
|
|
| `eager_content` | `True` | Also warm the content cache during eager load |
|
|
| `cwd` | `"/home/user"` | Initial working directory |
|
|
| `env` | `None` | Extra environment variables |
|
|
| `cache_ttl_ms` | `150_000` | Content cache TTL in ms. `None` = never expires (single-writer). `0` = no cache. |
|
|
|
|
The container is what defines the filesystem; setting `cwd` or extra `env` from the host doesn't change the files the agent sees.
|
|
|
|
## Limitations
|
|
|
|
- `chmod`, `utimes`, and symlinks (`ln -s`, `readlink`) raise `ENOSYS`.
|
|
- `/dev/null` as a redirect target isn't supported. Write to `/tmp/discard.log` instead.
|
|
- Binary uploads aren't supported. Text is extracted server-side.
|