Autonomous AI agent powered by Ollama + OpenCode. Runs locally, creates its own tools, searches the web, executes code, and delegates complex tasks — all through a chat UI or REST API.
localclaw/
├── src/
│ ├── index.ts # Express server + HTTP server entry point
│ ├── ws.ts # WebSocket server (replaces SSE for chat streaming)
│ ├── agent.ts # Agent loop — Ollama function calling, tool execution, persistence
│ ├── api.ts # REST API routes (sessions, messages, uploads, knowledge)
│ ├── db.ts # SQLite sessions, messages, memories, FTS5, background tasks
│ ├── plugins.ts # Plugin scanner (JS/ESM files + npm packages)
│ ├── log.ts # Structured logger with levels (debug/info/warn/error)
│ ├── ollama.ts # Ollama API client (streaming + non-streaming)
│ ├── opencode.ts # OpenCode subprocess delegation
│ ├── embeddings.ts # Ollama embeddings API for RAG memory
│ ├── scheduler.ts # Background task scheduler (polls every 30s)
│ └── tools/
│ ├── types.ts # Tool type definitions + AgentEvent
│ ├── registry.ts # Tool registry — builtins + dynamic create_tool
│ └── builtin/
│ ├── web-fetch.ts # Web search (SearXNG / DuckDuckGo) + URL fetch
│ ├── fetch-news.ts # News search (SearXNG news + RSS feeds)
│ ├── read-file.ts # Local file reading (path traversal protected)
│ ├── write-file.ts # Local file writing (path traversal protected)
│ ├── run-bash.ts # Bash command execution (streaming output)
│ ├── opencode-task.ts # OpenCode delegation
│ ├── generate-image.ts # Image generation via Ollama
│ ├── schedule-task.ts # Background task scheduling
│ ├── send-email.ts # Email delivery via Mailgun
│ ├── send-telegram.ts # Telegram bot messaging
│ ├── weather.ts # Weather forecast (wttr.in / Open-Meteo)
│ ├── search-knowledge.ts # RAG knowledge base search
│ └── browser-automation.ts # Headless Chromium browser control
├── client/ # Angular 20 frontend (SPA)
├── searxng/
│ └── settings.yml # SearXNG config (JSON API + image proxy)
├── plugins/ # Bundled plugin directory
├── docker-compose.yml # Ollama + SearXNG + localclaw stack
├── Dockerfile # Production build
└── .env # Configuration (gitignored)
Prerequisites: Node.js 22+, Ollama with a model pulled (e.g. qwen2.5:7b-instruct-q3_K_M), Docker (optional, for SearXNG).
# 1. Start SearXNG (optional — falls back to DuckDuckGo)
docker compose up -d
# 2. Install dependencies
npm install && cd client && npm install && cd ..
# 3. Configure OpenCode for Ollama
npm run setup:opencode
# 4. Start development server
npm run devOllama supports GPU acceleration via CUDA (NVIDIA) or ROCm/Vulkan (AMD). localclaw inherits whatever backend Ollama is configured with.
AMD GPUs (RX 580, etc.) — Vulkan path (recommended):
# 1. Install Mesa Vulkan drivers (usually pre-installed)
sudo apt install mesa-vulkan-drivers
# 2. Upgrade Ollama to a Vulkan-capable build
sudo systemctl stop ollama
curl -L https://github.com/ollama/ollama/releases/latest/download/ollama-linux-amd64 -o /usr/local/bin/ollama
sudo chmod +x /usr/local/bin/ollama
sudo systemctl start ollama
# 3. Set Vulkan environment in Ollama service
# Add to /etc/systemd/system/ollama.service.d/override.conf:
# Environment=OLLAMA_VULKAN=1Verify GPU is active:
ollama ps
# Look for "100% GPU" in the PROCESSOR columnNote: Models must fit in VRAM to run entirely on GPU. With a 4GB RX 580, use quantized models like
qwen2.5:7b-instruct-q3_K_M(~3.6GB) instead of the full q4_K_M variant (~4.7GB).
All settings via .env:
| Variable | Default | Description |
|---|---|---|
LOCALCLAW_PORT |
4173 |
Server port |
LOCALCLAW_DATA_DIR |
~/.localclaw |
Data directory (sessions, tools, downloads) |
LOCALCLAW_OLLAMA_URL |
http://localhost:11434 |
Ollama API URL |
LOCALCLAW_MODEL |
ollama/qwen2.5:7b-instruct-q3_K_M |
Default model |
LOCALCLAW_SEARXNG_URL |
http://localhost:8888 |
SearXNG search URL (empty = DuckDuckGo fallback) |
LOCALCLAW_OPENCODE_BIN |
opencode |
OpenCode binary path |
LOCALCLAW_LOG_LEVEL |
info |
Log level: debug, info, warn, error |
LOCALCLAW_SANDBOX_ENABLED |
false |
Wrap code execution in Docker containers |
LOCALCLAW_SANDBOX_IMAGE |
ubuntu:22.04 |
Docker image for sandboxed execution |
LOCALCLAW_EMBEDDING_MODEL |
nomic-embed-text |
Embedding model for RAG memory |
LOCALCLAW_MAILGUN_API_KEY |
— | Mailgun API key for email delivery |
LOCALCLAW_MAILGUN_DOMAIN |
— | Mailgun verified domain |
LOCALCLAW_MAILGUN_FROM |
— | Sender email address |
LOCALCLAW_TELEGRAM_BOT_TOKEN |
— | Telegram bot token for messaging |
LOCALCLAW_TELEGRAM_CHAT_ID |
— | Default Telegram chat ID for notifications |
- User sends a message via the Angular UI or REST API
- The agent loop sends the conversation + available tools to Ollama
- Ollama responds with either text or tool calls
- Tool calls are executed (web search, file ops, bash, etc.)
- Results are fed back to Ollama for the next reasoning step
- The loop continues until the agent produces a final answer (up to 15 iterations)
- web_fetch — Search the web (SearXNG → DuckDuckGo) or fetch a specific URL. Supports
text,images, anddownloadmodes. Validates domain existence before fetching. - fetch_news — Fetch the latest news articles on any topic. Uses SearXNG news search or RSS feeds (BBC, TechCrunch, Hacker News) as fallback.
- generate_image — Generate images using Ollama image models (flux, sd, stable-diffusion). Saves to downloads directory.
- read_file / write_file — Read and write files on the local filesystem.
- run_bash — Execute any bash command with real-time output streaming (120s timeout). Respects sandbox mode when enabled.
- opencode_task — Delegate complex multi-step coding tasks to OpenCode.
- send_email — Send email via Mailgun API. Supports plain text and HTML. Combine with background tasks for recurring delivery.
- send_telegram — Send Telegram messages via bot. Includes a
get_chat_idhelper to discover chat IDs. Combine with background tasks for recurring notifications. - browser_automation — Control a headless Chromium browser (via Puppeteer). Navigate URLs, click elements, extract page content, take screenshots, and fill forms.
- weather — Get current weather and forecasts for any location. Uses wttr.in with Open-Meteo fallback.
- search_knowledge — Search the local RAG knowledge base (uploaded documents + past tool results). Supports keyword and semantic search modes.
- schedule_task — Schedule, unschedule, and list background tasks. Supports
every Xm,every Xh,daily at HH:MM,daily,weeklyschedules. - create_tool — Dynamically create new reusable tools in JavaScript, Python, or Bash. Execution respects sandbox mode.
Tool results are automatically embedded (via Ollama embeddings API) and stored in SQLite. At the start of each conversation turn, the agent retrieves semantically relevant past tool results and injects them into the system prompt — enabling cross-session memory without filling the context window.
When LOCALCLAW_SANDBOX_ENABLED=true, run_bash and create_tool executions are wrapped in Docker containers with --network none, --security-opt no-new-privileges, and --cap-drop ALL for safe code execution.
The generate_image tool calls Ollama's /api/generate with image models (flux, sd, etc.). Generated images are saved to the downloads directory and returned as URLs.
The agent re-prompts itself when:
- A tool returns weak results (
No results,not found,Error:,< 30 chars) - The model gives up with phrases like
cannot find,does not contain,pas directement - The model responds with advisory text instead of using tools
Long-running and recurring tasks are handled by BackgroundScheduler (src/scheduler.ts), which polls the database every 30 seconds for due tasks. The agent can schedule tasks using the schedule_task tool:
schedule_task({ action: "schedule", name: "Morning news", schedule: "daily at 08:00", tool: "fetch_news", args: '{"topic":"technology"}' })
schedule_task({ action: "schedule", name: "Email report", schedule: "every 24h", tool: "send_email", args: '{"to":"user@example.com","subject":"Daily report","body":"..."}' })
schedule_task({ action: "list" })
schedule_task({ action: "unschedule", task_id: "..." })
When a background task completes, the result is stored in the database and injected as a system message into the session — the agent sees it on the next conversation turn.
The send_email tool uses the Mailgun API to send emails. Configure in .env:
LOCALCLAW_MAILGUN_API_KEY=your-api-key
LOCALCLAW_MAILGUN_DOMAIN=your-domain.mailgun.org
LOCALCLAW_MAILGUN_FROM=localclaw <mailgun@your-domain.mailgun.org>
Sandbox domains require authorized recipients — add the target email in Mailgun Dashboard → Sending → Authorized Recipients.
The send_telegram tool sends messages via a Telegram bot. Configure in .env:
LOCALCLAW_TELEGRAM_BOT_TOKEN=your-bot-token
LOCALCLAW_TELEGRAM_CHAT_ID=your-chat-id # optional — used as fallback
Usage:
- Message your bot on Telegram
- Call
send_telegram({ action: "get_chat_id" })to discover your chat ID - Call
send_telegram({ action: "send", chat_id: "123456", text: "Hello!" })to send messages
If LOCALCLAW_TELEGRAM_CHAT_ID is set in .env, the chat_id argument can be omitted — the tool falls back to the env var automatically.
Primary backend is SearXNG (Docker container on port 8888) with custom settings.yml that enables JSON API and image-focused engines (Pixabay, Flickr, DeviantArt, Getty, Openverse). Falls back to DuckDuckGo HTML search if SearXNG is not configured.
| Method | Path | Description |
|---|---|---|
GET |
/api/health |
Health check |
GET |
/api/tools |
List registered tools |
GET |
/api/sessions |
List sessions |
POST |
/api/sessions |
Create session |
GET |
/api/sessions/:id |
Get session |
DELETE |
/api/sessions/:id |
Delete session |
PATCH |
/api/sessions/:id |
Rename session |
GET |
/api/sessions/:id/messages |
Get messages |
PATCH |
/api/sessions/:id/messages/:msgId |
Edit message (truncates conversation after it) |
POST |
/api/sessions/:id/upload |
Upload file (txt/pdf/docx) for chat context |
GET |
/api/background-tasks |
List background tasks (optional ?session_id=...) |
GET |
/api/background-tasks/:id |
Get a background task |
DELETE |
/api/background-tasks/:id |
Delete a background task |
PATCH |
/api/background-tasks/:id |
Enable/disable a background task |
GET |
/api/knowledge |
List uploaded knowledge documents |
DELETE |
/api/knowledge/:id |
Delete a knowledge document |
Connect to ws://host/ws and send a JSON message:
{"type":"chat","sessionId":"<uuid>","message":"Hello!"}The server streams events back as JSON messages:
{"type":"text","content":"thinking..."}
{"type":"tool_start","toolName":"web_fetch","toolRunId":"<uuid>","toolArgs":{...}}
{"type":"tool_chunk","toolName":"web_fetch","toolRunId":"<uuid>","content":"stdout line 1..."}
{"type":"tool_end","toolName":"web_fetch","toolRunId":"<uuid>","toolResult":"..."}
{"type":"tool_error","toolName":"web_fetch","toolRunId":"<uuid>","error":"..."}
{"type":"text","content":"final answer"}
{"type":"done"}
Each tool invocation gets a unique toolRunId so concurrent or repeated tool calls are matched correctly in the UI. Tool execution output is streamed in real-time via tool_chunk events.
Development proxy: When using ng serve, the Angular dev server proxies /ws to the backend. Configured in client/proxy.conf.js.
Stop generation: Close the WebSocket from the client side — the backend detects req.on('close') and aborts the agent loop.
npm run build # Builds both server (tsc) and client (ng build)
npm start # Start production server
docker build -t localclaw . # Or build Docker imageThe fastest way to run everything (Ollama + localclaw + SearXNG):
# 1. Clone and enter the repo
cd localclaw
# 2. Start the full stack
docker compose up -d
# 3. Pull a model in Ollama
docker exec localclaw-ollama ollama pull qwen2.5:7b-instruct-q3_K_M
docker exec localclaw-ollama ollama pull nomic-embed-text
# 4. Open http://localhost:4173GPU support: The Ollama container is configured with NVIDIA GPU reservations. For AMD GPUs, remove the deploy.resources block and set OLLAMA_VULKAN=1 in the ollama service environment — requires runtime: runc or native Vulkan support on the host.
Configuration: Pass variables via .env in the project root — values are picked up by docker compose:
| Variable | Default | Description |
|---|---|---|
LOCALCLAW_PORT |
4173 |
Host port for the web UI |
LOCALCLAW_MODEL |
ollama/qwen2.5:7b-instruct-q3_K_M |
Default chat model |
LOCALCLAW_EMBEDDING_MODEL |
nomic-embed-text |
Embedding model for RAG |
SEARXNG_SECRET_KEY |
change_me |
SearXNG secret key |
To update the stack after pulling new code:
docker compose down
git pull
docker compose build localclaw
docker compose up -dAngular 20 single-page application (signals-based) with:
- WebSocket-based real-time chat streaming with tool event matching per
toolRunId - Markdown rendering with syntax highlighting (highlight.js, atom-one-dark theme)
- 2 themes: Light and Dark with auto-detection via
prefers-color-scheme - Collapsible tool event cards showing real-time agent activity (streaming chunks via
tool_chunk) - Stop generation button (closes WebSocket, aborts agent loop)
- Message editing — edit any user message, conversation is truncated after the edit point
- File upload — attach text/PDF/docx files via paperclip icon, content extracted and added as user message
- Session management — create, rename, delete sessions
- 120s timeout fallback to reset loading state if no response received
