Yoclaw is a Telegram-first LLM agent written in Rust. It talks to OpenAI-compatible chat and embedding APIs, supports tool calling, keeps a persistent task queue, and stores semantic memories in SQLite with sqlite-vec.
- Telegram bot interface with
/help,/tools, and/skills - OpenAI-compatible chat completion backend for the main agent
- OpenAI-compatible embeddings backend for semantic memory search
- Built-in tools for shell commands, file I/O, URL fetches, scheduling, and memory operations
- Persistent scheduled tasks in
tasks.json - Persistent task routing in
routes.json - Optional skill loading from
~/.yoclaw/skillsorCONFIG_PATH/skills
- Build and run once to generate the default config template:
cargo runThis creates ~/.yoclaw/config.toml by default if it does not already exist.
- Edit the config:
[agent]
openai_api_base_url = "http://localhost:11434/v1"
openai_api_key = "ollama"
openai_model = "qwen3-4b"
system_prompt = "Your name is YoClaw, and you are a helpful assistant."
debug_mode = false
[embedding]
openai_api_base_url = "http://localhost:11434/v1"
openai_api_key = "ollama"
openai_model = "qwen3-embedding:8b"
[channels]
telegram_token = "<telegram bot token>"
allowed_users = ["<telegram user id>"]
recv_confirm = "👍"- Start the bot:
cargo run --releaseYou can manage local skills without starting the bot runtime:
cargo run -- skill add dir:/path/to/skill
cargo run -- skill add zip:/path/to/skill.zip
cargo run -- skill add zip:https://example.com/skill.zipInstalled skills are copied into CONFIG_PATH/skills (or ~/.yoclaw/skills by default).
-
Create the config first, either by running
cargo runonce or by writing~/.yoclaw/config.tomlmanually. -
Build the image:
docker build -t yoclaw:latest -f Containerfile .Or with Podman:
podman build -t localhost/yoclaw:latest -f Containerfile .- Run the container:
docker run --rm \
-v "$HOME/.yoclaw:/root/.yoclaw" \
yoclaw:latestWith Podman:
podman run --rm \
--network host \
-v /etc/localtime:/etc/localtime:ro \
-v "$HOME/.yoclaw:/root/.yoclaw:Z" \
localhost/yoclaw:latestThe repo includes yoclaw.container, which generates yoclaw.service.
- Build the image:
podman build -t localhost/yoclaw:latest -f Containerfile .- Install the Quadlet file:
mkdir -p ~/.config/containers/systemd
cp yoclaw.container ~/.config/containers/systemd/- Reload and start the user service:
systemctl --user daemon-reload
systemctl --user enable --now yoclaw.serviceYoclaw reads its config directory from CONFIG_PATH. If CONFIG_PATH is unset, it defaults to ~/.yoclaw.
The main config file is:
~/.yoclaw/config.toml
The default template comes from src/config/template/config.toml.
[agent]openai_api_base_url: base URL for the chat API, for examplehttp://localhost:11434/v1openai_api_key: bearer token sent to the chat APIopenai_model: chat model namesystem_prompt: initial system promptdebug_mode: whentrue, tool-call progress and usage data are sent back in responses
[embedding]openai_api_base_url: base URL for the embeddings APIopenai_api_key: bearer token sent to the embeddings APIopenai_model: embedding model name
[channels]telegram_token: Telegram bot tokenallowed_users: list of allowed Telegram user IDs as stringsrecv_confirm: optional emoji reaction added to accepted incoming messages
[environment]- optional environment variables exposed to the
generic_shelltool
- optional environment variables exposed to the
allowed_users = []blocks everyone. The bot replies with a warning that includes the sender's Telegram user ID.- Chat completions are sent to
{openai_api_base_url}/chat/completions. - Embeddings are sent to
{embedding.openai_api_base_url}/embeddings.
The runtime in src/main.rs is split into four long-lived async flows:
- shutdown signal listener
- Telegram listener
- Telegram sender
TaskProcessor
The agent itself stays on the main thread. This keeps agent state single-owner and avoids deadlocks between tool calls and task management.
Inbound Telegram messages are turned into tasks, executed by the agent, and routed back to the originating chat through the sender loop.
The built-in tool definitions live in src/agent/tools/mod.rs. The current tool surface is:
get_current_timegeneric_shelluse_skillread_filewrite_fileget_urlschedule_taskcancel_tasklist_tasksadd_memoryremove_memorysearch_memory
Tasks are managed by src/tasks/processor.rs and src/tasks/manager.rs.
- Immediate messages and scheduled jobs share the same task pipeline.
- Repeating tasks support
dailyandweekly. - Repeating tasks reschedule from the original deadline anchor, not from completion time.
- Pending tasks are persisted in
tasks.jsonin the config directory.
- Semantic memory is stored in
memory.dbunder the config directory. - Skills are loaded from
skills/under the config directory. - A skill can be either a subdirectory containing
SKILL.mdor a direct Markdown file. yoclaw skill add ...installs managed skills into that sameskills/directory.
The Telegram command registry is defined in src/channels/command.rs:
/help/tools/skills
By default, Yoclaw stores runtime state under ~/.yoclaw:
config.toml: main configurationmemory.db: SQLite memory storetasks.json: persisted scheduled tasksroutes.json: task-to-chat routing tableskills/: optional skill definitions
Build:
cargo buildRun tests:
cargo testRead the detailed architecture notes in STRUCTURE.md.
