A full-featured AI assistant platform in ~11K lines of TypeScript.
Includes a CLI coding agent, TUI mode, gateway server with WebSocket + HTTP API, embedded WebChat UI, messaging channels (WhatsApp, Telegram, Discord, Slack), plugin system, Docker sandboxing, DM pairing security, session durability with file locking and crash repair, auth resilience with persistent cooldowns, block streaming with per-channel limits, message deduplication, queue/collect mode, exec approval with auto-allowlist, SOUL.md personality, bootstrap files, hybrid vector + BM25 memory search, web search and web fetch tools, local model auto-detection (Ollama, LM Studio, vLLM, LiteLLM), presence system, webhook endpoint, full cron expressions, config hot-reload, hook transforms, skill commands, and multi-agent orchestration.
- CLI Agent — Interactive REPL and single-shot mode with streaming output
- CLI Subcommands —
config get/set,status,doctor,cron list/add/remove,logs,sessions - TUI Mode — Rich terminal UI via pi-tui with markdown rendering and tool panels
- Setup Wizard —
tinyclaw initinteractive onboarding with provider, channel, and security setup - Gateway Server — HTTP + WebSocket server with JSON-RPC 2.0 protocol, 23 RPC methods
- WebChat UI — Embedded dark-theme chat interface served from the gateway at
/or/chat - OpenAI-Compatible API —
/v1/chat/completions,/v1/responses,/v1/models - Webhook Endpoint — Generic webhook with Bearer token auth, wake and agent modes
- Message Pipeline — Inbound debouncing, message deduplication (60s TTL), queue/collect mode, directives (
++think,++model), slash commands, paragraph-aware chunking, delivery with typing indicators, envelope context - Block Streaming — Coalescer with per-channel text limits (WhatsApp 1600, Telegram 4096, Discord 2000), code block awareness, dedup
- Channels — WhatsApp, Telegram, Discord, Slack with full adapter support (text, image, audio, video, documents, reactions, threads)
- Docker Sandbox — Isolated code execution in containers with configurable memory/CPU/network limits
- DM Pairing — Unknown sender security with pairing codes and allow-list management
- Plugin System — 10 registration methods, 4-origin discovery (bundled, config, directory, workspace), 33 plugin slots (4 core channels implemented, 29 TODO stubs)
- Security — 10-layer tool policy engine, SSRF guard, prompt injection detection, path traversal prevention, pairing gate, exec approval with allowlist
- Session Durability — Advisory file locking, crash repair, tool result truncation, token/usage tracking, auto-reset policies (daily/idle/manual)
- Auth Resilience — Multi-key rotation with persistent cooldowns, failure classification (auth/rate_limit/billing/timeout), backoff persistence across restarts
- Memory — SQLite + FTS5 full-text search + vector search (sqlite-vec + OpenAI embeddings), hybrid scoring (0.7 cosine + 0.3 BM25)
- Web Tools — Web search via Brave Search API and URL fetching with HTML-to-text extraction
- Personality — SOUL.md persona loading, agent identity (name/emoji/prefix), group chat context and style
- Bootstrap Files — Supports SOUL.md, IDENTITY.md, USER.md, TOOLS.md, TINYCLAW.md, CLAUDE.md, AGENTS.md, BOOTSTRAP.md
- Browser — Chrome/CDP automation via playwright-core (navigate, click, type, screenshot, accessibility snapshot)
- Cron — Job scheduler with full 5-field cron expressions, intervals, one-time jobs, catch-up on missed runs
- TTS — Three providers (Edge TTS, OpenAI, ElevenLabs) with auto-summarize
- Media — MIME detection, image processing (sharp), AI vision (Anthropic/OpenAI), audio format detection
- Multi-Agent — Session key routing, agent-channel bindings, subagent spawning, agent-to-agent messaging
- Model Flexibility — Anthropic, OpenAI, Google, custom providers, model aliases, fallback chains, multi-key rotation
- Local Models — Auto-detection for Ollama, LM Studio, vLLM, and LiteLLM with default endpoints
- Presence System — Real-time client tracking with TTL sweep and heartbeat broadcasts
- Config Hot-Reload — Automatic config file watching with debounce, selective reload, restart warnings
- Hook Transforms — Hooks can transform data or abort pipeline, sequential execution with priority ordering
- Skill Commands —
/skillname argsdispatches to SKILL.md files with prompt-based or tool-based execution
# Install
npm install
# Interactive setup wizard
npx tinyclaw init
# Set your API key
export ANTHROPIC_API_KEY=sk-ant-...
# Single-shot
npx tinyclaw "What is 2+2?"
# Interactive REPL (tries TUI first, falls back to bare REPL)
npx tinyclaw
# Start gateway server
npx tinyclaw serve# Model override
tinyclaw -m anthropic/claude-opus-4-6 "Explain quantum computing"
# Named session
tinyclaw -s myproject "Add error handling to auth.ts"
# Thinking mode
tinyclaw --thinking high "Design a database schema for a blog"
# Ephemeral session (no persistence)
tinyclaw --ephemeral "Quick question"
# Custom working directory
tinyclaw --cwd /path/to/project "Fix the failing tests"
# JSON output
tinyclaw --json "List all TODO items"
# Disable TUI, use bare REPL
tinyclaw --no-tui| Command | Description |
|---|---|
/new |
Clear session, start fresh |
/compact |
Compact context to free token space |
/status |
Show session info, model, and token usage |
/model [name] |
Show or switch the current model |
/stop |
Stop current generation |
/quit |
Exit REPL |
| Command | Description |
|---|---|
tinyclaw init |
Interactive setup wizard |
tinyclaw serve |
Start the gateway server |
tinyclaw pair list |
Show pending pairing requests and allowed senders |
tinyclaw pair approve <code> |
Approve a pairing code |
tinyclaw pair revoke <channelId/peerId> |
Revoke access for a sender |
tinyclaw config get [key] |
Read config values (dot-notation) |
tinyclaw config set <key> <value> |
Write config values |
tinyclaw status |
Show gateway health (uptime, model, sessions, heap) |
tinyclaw sessions |
List active sessions via gateway |
tinyclaw doctor |
Check config, API keys, gateway, channels, Brave Search |
tinyclaw cron list |
List scheduled cron jobs |
tinyclaw cron add <expr> <body> |
Add a cron job |
tinyclaw cron remove <id> |
Remove a cron job |
tinyclaw logs |
Tail the gateway log file |
Full Cloud API integration with webhook verification, signature validation, and all message types.
{
"channels": {
"whatsapp": {
"enabled": true,
"webhookPath": "/webhook/whatsapp",
"accounts": {
"main": {
"phoneNumberId": "123456789",
"accessTokenEnv": "WHATSAPP_ACCESS_TOKEN",
"verifyToken": "my-verify-token"
}
}
}
}
}grammY-based bot with long-polling (default) or webhook mode. Supports forum topics, media, and inline editing.
{
"channels": {
"telegram": {
"enabled": true,
"botTokenEnv": "TELEGRAM_BOT_TOKEN",
"mode": "polling"
}
}
}discord.js client with guild mentions, DM support, and thread awareness.
{
"channels": {
"discord": {
"enabled": true,
"botTokenEnv": "DISCORD_BOT_TOKEN",
"mentionOnly": true,
"dmEnabled": true
}
}
}Bolt framework with Socket Mode. Thread replies, file uploads, and mention gating.
{
"channels": {
"slack": {
"enabled": true,
"botTokenEnv": "SLACK_BOT_TOKEN",
"appTokenEnv": "SLACK_APP_TOKEN",
"mentionOnly": true,
"threadReplies": true
}
}
}Isolate code execution in Docker containers for channel sessions.
# Build the sandbox image
docker build -f Dockerfile.sandbox -t tinyclaw-sandbox .{
"sandbox": {
"enabled": true,
"image": "tinyclaw-sandbox",
"scope": "session",
"memoryLimit": "512m",
"cpuLimit": "1",
"networkMode": "none"
}
}When enabled, all bash tool calls from channel sessions execute inside isolated containers with configurable memory, CPU, and network restrictions.
Run TinyClaw as a production container:
# Build the production image
docker build -t tinyclaw .
# Run the gateway server
docker run -d \
-p 18789:18789 \
-e ANTHROPIC_API_KEY=sk-ant-... \
-v tinyclaw-data:/home/tinyclaw/.config/tinyclaw \
tinyclaw
# Run with custom config
docker run -d \
-p 18789:18789 \
-e ANTHROPIC_API_KEY=sk-ant-... \
-v ./config.json5:/home/tinyclaw/.config/tinyclaw/config.json5:ro \
tinyclawThe image uses a non-root tinyclaw user, includes git for workspace operations, and exposes port 18789 for the gateway server.
Require unknown senders to be approved before they can interact with the agent.
{
"security": {
"pairingRequired": true
}
}When an unknown sender messages the bot, they receive a pairing code. The admin approves it:
tinyclaw pair list # See pending requests
tinyclaw pair approve ABCD1234 # Approve a code
tinyclaw pair revoke telegram:default/12345 # Revoke accessSessions are protected against corruption and data loss:
- File Locking — Advisory locks via exclusive file creation prevent concurrent writes to JSONL transcripts. Stale lock detection (>30min or dead PID) with exponential backoff retry.
- Crash Repair — On startup, unparseable JSONL lines are dropped and a backup is created. Sessions recover automatically from mid-write crashes.
- Tool Result Truncation — Oversized tool results (>30% of context window) are automatically truncated before causing permanent context overflow.
- Token Tracking — Input/output/cache tokens are tracked per session. View with
/status. - Auto-Reset — Sessions can reset automatically based on time policies.
{
"session": {
"resetMode": "daily",
"resetAtHour": 6
}
}Reset modes: "manual" (default), "daily" (resets at configured hour), "idle" (resets after N minutes of inactivity).
TinyClaw supports persona customization via SOUL.md and config:
SOUL.md — Place a SOUL.md file in your workspace root to define the agent's personality and tone. It's loaded first and prepended to the system prompt.
Agent Identity — Set a custom name, emoji, and response prefix in config:
{
"agent": {
"identity": { "name": "Jarvis", "emoji": "🤖" },
"responsePrefix": "Sir, "
}
}Group Chat — When responding in group channels, the system prompt adapts with group context, natural writing style, and sender-specific addressing.
Multi-key rotation with persistent cooldowns that survive process restarts:
- Failure Classification — Errors are classified as
auth,rate_limit,billing,timeout, orformatwith per-reason retry behavior - Cooldown Persistence — State saved to
~/.config/tinyclaw/auth-state.json. Rate limits: 1min → 5min → 25min → 1hr. Billing: 5hr → 10hr → 20hr → 24hr - Smart Retry — Rate limits backoff + rotate key. Timeouts retry same key. Auth/billing rotate key. Format errors don't retry
Control shell command execution from channel sessions:
{
"security": {
"execApproval": "interactive"
}
}When set to "interactive", bash commands from channel sessions require admin approval via the gateway:
// List pending approvals
ws.send(JSON.stringify({ jsonrpc: "2.0", id: 1, method: "exec.pending" }));
// Approve
ws.send(JSON.stringify({ jsonrpc: "2.0", id: 2, method: "exec.approve", params: { id: "approval_..." } }));After 3 approvals of the same command pattern, it's auto-allowed. The allowlist persists at ~/.config/tinyclaw/exec-allowlist.json.
# Start with defaults (port 18789, localhost only)
tinyclaw serve
# Custom port
tinyclaw serve --port 3000
# With config file
tinyclaw serve --config ./tinyclaw.jsonconst ws = new WebSocket("ws://localhost:18789");
// Send a message
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "chat.send",
params: { message: "Hello!", sessionKey: "my-session" }
}));
// Stream responses
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 2,
method: "chat.stream",
params: { message: "Write a poem" }
}));| Method | Description |
|---|---|
chat.send |
Send message, get complete response |
chat.stream |
Send message, stream chunks via events |
sessions.list |
List active sessions |
sessions.get |
Get session details |
sessions.clear |
Clear a specific session |
sessions.clearAll |
Clear all sessions |
config.get |
Get current config (sanitized) |
config.reload |
Hot-reload config file |
health |
System health status |
channels.list |
List connected channels |
channels.send |
Send message via channel |
models.list |
List available models |
memory.search |
Query long-term memory |
memory.store |
Add a memory entry |
cron.list |
List scheduled jobs |
cron.add |
Create a scheduled job |
cron.remove |
Delete a scheduled job |
exec.pending |
List pending exec approvals |
exec.approve |
Approve a pending exec |
exec.deny |
Deny a pending exec |
presence.list |
List connected clients with roles and timestamps |
presence.upsert |
Update or register a presence entry |
system.shutdown |
Graceful shutdown |
# Chat completions
curl -X POST http://localhost:18789/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Hello"}]}'
# Streaming
curl -X POST http://localhost:18789/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Hello"}], "stream": true}'
# List models
curl http://localhost:18789/v1/models
# Health check
curl http://localhost:18789/healthTinyClaw looks for config at ~/.config/tinyclaw/config.json5 (or $XDG_CONFIG_HOME/tinyclaw/config.json5).
Run tinyclaw init for an interactive setup wizard, or create the file manually:
{
"agent": {
"provider": "anthropic",
"model": "claude-sonnet-4-5-20250929",
"thinkingLevel": "off",
"fallbacks": ["openai/gpt-4o"],
"identity": { "name": "MyBot", "emoji": "🤖" },
"responsePrefix": ""
},
"session": {
"resetMode": "manual",
"resetAtHour": 0,
"idleMinutes": 120
},
"gateway": {
"port": 18789,
"bind": "loopback",
"auth": { "mode": "token", "token": "my-secret" },
"reload": { "mode": "auto", "debounceMs": 2000 },
"webhook": {
"enabled": false,
"path": "/webhook/generic",
"tokenEnv": "WEBHOOK_SECRET"
}
},
"channels": {
"defaults": {
"groupIsolation": "per-group"
},
"whatsapp": {
"enabled": true,
"accounts": {
"main": {
"phoneNumberId": "123456789",
"accessTokenEnv": "WHATSAPP_ACCESS_TOKEN"
}
}
},
"telegram": { "enabled": true, "botTokenEnv": "TELEGRAM_BOT_TOKEN" },
"discord": { "enabled": true, "botTokenEnv": "DISCORD_BOT_TOKEN" },
"slack": { "enabled": true, "botTokenEnv": "SLACK_BOT_TOKEN", "appTokenEnv": "SLACK_APP_TOKEN" }
},
"sandbox": {
"enabled": false,
"image": "tinyclaw-sandbox",
"networkMode": "none"
},
"security": {
"toolPolicy": "auto",
"ssrfProtection": true,
"execApproval": "auto",
"pairingRequired": false,
"maxToolCallsPerTurn": 50
},
"memory": {
"backend": "builtin",
"embeddingProvider": "openai",
"embeddingModel": "text-embedding-3-small"
},
"tts": {
"enabled": false,
"provider": "edge",
"auto": "off"
},
"pipeline": {
"inboundDebounceMs": 1500,
"typingIndicator": true,
"envelope": true,
"collectMode": false,
"collectWindowMs": 2000,
"chunkSize": { "min": 800, "max": 1200 },
"deliveryDelayMs": { "min": 800, "max": 2500 }
},
"plugins": {
"enabled": true
}
}| Tool | Description |
|---|---|
bash |
Execute shell commands (sandboxed when enabled) |
write / edit / read |
File operations |
glob / grep |
File search |
browser_navigate / browser_click / browser_type / browser_screenshot / browser_snapshot |
Browser automation |
web_search |
Web search via Brave Search API (requires BRAVE_API_KEY) |
web_fetch |
Fetch URL and extract text (HTML stripped to plain text) |
memory_search / memory_store |
Persistent memory |
cron_list / cron_set / cron_delete |
Job scheduling |
tts_speak |
Text-to-speech |
message_send / message_react / message_typing |
Channel messaging |
image_generate |
DALL-E image generation |
apply_patch |
Git patch application |
session_list / session_history / session_send / session_spawn |
Session management |
session_status |
System status |
agents_list |
List configured agents |
canvas_* |
Canvas operations |
nodes_* |
Node management |
gateway_* |
Gateway control |
Place .ts or .js files in ~/.config/tinyclaw/plugins/ or .tinyclaw/plugins/ (workspace-local) for auto-discovery.
import type { TinyClawPluginApi } from "tinyclaw";
export default function init(api: TinyClawPluginApi) {
Object.assign(api.meta, { id: "my-plugin", name: "My Plugin" });
api.registerTool(/* AgentTool */);
api.registerHook("message_inbound", async (event, data) => { /* ... */ });
api.registerChannel(/* ChannelDef */);
api.registerHttpRoute("/my-endpoint", "POST", handler);
api.registerService("my-service", startFn, stopFn);
}The plugin system has 33 plugin slots defined in src/plugin/. 4 core channel adapters are fully implemented and live in src/channel/ (WhatsApp, Telegram, Discord, Slack). The remaining 29 slots are scaffold stubs for future implementation:
Channel stubs (14): Signal, iMessage, Instagram, Messenger, Twitter/X, Matrix, Teams, LINE, WeChat, Viber, Rocket.Chat, Zulip, Webex, Google Chat
Non-channel stubs (15): Memory Core, Memory LanceDB, Copilot Proxy, TTS Manager, Canvas Renderer, Cron Scheduler, Media Processor, Browser Manager, Analytics, Rate Limiter, Audit Logger, Webhook Relay, Vector Search, Notification Hub, Backup Manager
Place .md files in ~/.config/tinyclaw/skills/ to create custom slash commands. Each file becomes a /command:
---
description: Summarize a pull request
tags: [git, review]
---
Review the pull request and provide:
1. A one-paragraph summary
2. Key changes
3. Potential issuesUsage: /summarize-pr #123 — The skill content is injected as context for the agent.
| Skill | Description |
|---|---|
weather |
Check weather via wttr.in (no API key) |
github |
GitHub operations via gh CLI (PRs, issues, CI) |
healthcheck |
System and security audit |
summarize |
Summarize URLs, files, or content |
web-search |
Multi-source web research |
notes |
Markdown note-taking using file tools |
Hooks can now transform pipeline data or abort message processing:
api.registerHook("message_inbound", async (event, data) => {
// Transform: modify data for downstream hooks
return { transform: { body: data.body.toUpperCase() } };
// Or abort: stop pipeline entirely
return { abort: true, abortMessage: "Message blocked" };
});Hooks execute sequentially by priority. Each hook sees transforms from previous hooks.
When the gateway is running, config file changes are detected automatically:
{
"gateway": {
"reload": { "mode": "auto", "debounceMs": 2000 }
}
}Changes to channels, hooks, and cron are applied immediately. Changes to gateway.* or plugins.* log a restart warning. Connected WebSocket clients receive a config.reload event.
The gateway serves an embedded chat interface at / or /chat. No frontend build step required — it's a self-contained HTML page with dark theme, WebSocket connection, auto-reconnect, and streaming support.
# Start gateway, then open browser
tinyclaw serve
open http://localhost:18789Accept external triggers via a generic webhook:
{
"gateway": {
"webhook": {
"enabled": true,
"path": "/webhook/generic",
"tokenEnv": "WEBHOOK_SECRET"
}
}
}Supports two modes via the mode query parameter:
wake(default) — Dispatches the body text through the pipelineagent— Runs the body as an agent prompt and returns the result
Authenticated via Authorization: Bearer <token> header.
Real-time tracking of connected clients (UI, CLI, WebChat, nodes, backends):
// List all connected clients
ws.send(JSON.stringify({ jsonrpc: "2.0", id: 1, method: "presence.list" }));
// Register/update presence
ws.send(JSON.stringify({
jsonrpc: "2.0", id: 2, method: "presence.upsert",
params: { id: "my-client", role: "ui" }
}));Entries expire after 5 minutes without a heartbeat. The gateway broadcasts heartbeats every 60 seconds.
TinyClaw auto-detects local model providers by name:
| Provider | Default Endpoint |
|---|---|
ollama |
http://127.0.0.1:11434/v1 |
lmstudio |
http://127.0.0.1:1234/v1 |
vllm |
http://127.0.0.1:8000/v1 |
litellm |
http://127.0.0.1:4000/v1 |
{
"agent": {
"provider": "ollama",
"model": "llama3.1"
}
}No additional configuration needed — the base URL is inferred from the provider name.
The pipeline automatically deduplicates messages using a 60-second TTL window. Messages from the same channel with the same message ID are silently dropped. This prevents double-processing when channels retry delivery.
Batch rapid sequential messages into a single agent turn:
{
"pipeline": {
"collectMode": true,
"collectWindowMs": 2000
}
}When enabled, messages arriving within the collect window are concatenated with newlines and dispatched as a single message. Useful for channels where users send multiple short messages in quick succession.
TinyClaw loads the first matching file from the workspace root to inject into the system prompt:
SOUL.md → IDENTITY.md → USER.md → TOOLS.md → TINYCLAW.md → CLAUDE.md → AGENTS.md → BOOTSTRAP.md → .tinyclaw → .claude
Use these to define personality, custom instructions, tool guidance, or any persistent context for the agent.
Two built-in tools for web access:
web_search— Search the web via Brave Search API. RequiresBRAVE_API_KEYorBRAVE_SEARCH_API_KEYenvironment variable. Returns titles, URLs, and snippets.web_fetch— Fetch any URL and extract text content. HTML is stripped to plain text (scripts/styles removed, entities decoded). Supports JSON, HTML, and plain text responses.
Run diagnostics to verify your setup:
tinyclaw doctorChecks: config file validity, API key presence, gateway connectivity, channel token availability, and Brave Search API key.
src/
├── index.ts Public API exports (barrel)
├── agent/ Session, runner, tools, system prompt, compact, pruning, multi-agent
├── auth/ Multi-key rotation with backoff + persistent cooldowns
├── browser/ Chrome/CDP automation via playwright-core
├── channel/ Channel adapter interface, registry, lifecycle + adapters (WhatsApp, Telegram, Discord, Slack)
├── cli/ CLI + REPL + serve + subcommands, interactive setup wizard (init)
├── config/ Zod schemas, loader, paths, watcher
├── cron/ Job scheduler with 5-field cron expression parser
├── exec/ Shell execution (with sandbox routing)
├── gateway/ HTTP + WebSocket server, JSON-RPC methods, OpenAI-compatible endpoints, WebChat UI
├── hooks/ 14 event types, hook runner with transform/abort, bundled hooks
├── media/ Image/audio processing, AI vision, MIME detection
├── memory/ SQLite + FTS5 + vector search, OpenAI embeddings
├── model/ Aliases, fallback chains, local provider auto-detection
├── pipeline/ Message dispatch, dedup, collect mode, directives, chunking, coalescer
├── plugin/ Plugin API, registry, 4-origin loader, 33 plugin slots
├── sandbox/ Docker container management
├── security/ 10-layer policy, SSRF, injection detection, exec allowlist, DM pairing
├── skills/ Skill discovery, formatting, and command execution
├── tools/ Web search/fetch + 17 agent tool implementations
├── tts/ Edge/OpenAI/ElevenLabs TTS
├── tui/ TUI mode via pi-tui
└── utils/ Logger, errors
tests/ All test files (mirroring src/ structure)
# Install dependencies
npm install
# Type check
npm run typecheck
# Build
npm run build
# Run from source
npx tsx src/cli/cli.ts "Hello"
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverage- Runtime:
zod,commander,chalk,ws,json5,better-sqlite3 - AI:
@mariozechner/pi-agent-core,@mariozechner/pi-ai,@mariozechner/pi-coding-agent - Channels:
grammy(Telegram),discord.js(Discord),@slack/bolt+@slack/web-api(Slack) - UI:
@inquirer/prompts(init wizard),@mariozechner/pi-tui(TUI mode) - Optional (lazy-loaded):
playwright-core(browser),sharp(images),edge-tts(TTS),sqlite-vec(vector search) — all gracefully fall back if unavailable
MIT
