Skip to main content

Directory Tree

nanobot/
├── agent/              # 🧠 Core agent logic
│   ├── loop.py         #    Agent loop (LLM ↔ tool execution)
│   ├── context.py      #    Prompt builder
│   ├── memory.py       #    Persistent memory (MEMORY.md)
│   ├── skills.py       #    Skills loader
│   ├── subagent.py     #    Background task execution
│   └── tools/          #    Built-in tools
│       ├── base.py     #      Tool interface
│       ├── registry.py #      Tool registry
│       ├── shell.py    #      Execute shell commands
│       ├── filesystem.py #    Read/write/edit/list files
│       ├── web.py      #      Web search and fetch
│       ├── spawn.py    #      Spawn subagents
│       ├── cron.py     #      Schedule tasks
│       ├── message.py  #      Send messages to channels
│       └── mcp.py      #      MCP server integration

├── skills/             # 🎯 Bundled skills
│   ├── github.md       #    GitHub operations
│   ├── weather.md      #    Weather queries
│   ├── tmux.md         #    Tmux management
│   └── ...             #    Other skills

├── channels/           # 📱 Chat channel integrations
│   ├── base.py         #    BaseChannel interface
│   ├── manager.py      #    Channel lifecycle management
│   ├── telegram.py     #    Telegram bot
│   ├── discord.py      #    Discord bot
│   ├── whatsapp.py     #    WhatsApp bridge
│   ├── feishu.py       #    Feishu/Lark
│   ├── email.py        #    Email (IMAP/SMTP)
│   ├── slack.py        #    Slack (Socket Mode)
│   ├── qq.py           #    QQ bot
│   ├── dingtalk.py     #    DingTalk
│   ├── matrix.py       #    Matrix/Element
│   └── mochat.py       #    Mochat/Claw IM

├── bus/                # 🚌 Message routing
│   ├── queue.py        #    MessageBus implementation
│   └── events.py       #    InboundMessage, OutboundMessage

├── cron/               # ⏰ Scheduled tasks
│   ├── service.py      #    Cron job scheduler
│   └── types.py        #    Job data structures

├── heartbeat/          # 💓 Proactive wake-up
│   └── service.py      #    Periodic task execution (HEARTBEAT.md)

├── providers/          # 🤖 LLM providers
│   ├── base.py         #    LLMProvider interface
│   ├── registry.py     #    Provider registry (ProviderSpec)
│   ├── litellm_provider.py # LiteLLM wrapper
│   ├── custom_provider.py  # Direct OpenAI-compatible
│   ├── openai_codex_provider.py # OAuth providers
│   └── transcription.py    # Voice transcription (Groq)

├── session/            # 💬 Conversation sessions
│   └── manager.py      #    Session storage and retrieval

├── config/             # ⚙️ Configuration
│   ├── schema.py       #    Pydantic models (Config classes)
│   └── loader.py       #    Load and validate config.json

├── cli/                # 🖥️ Command-line interface
│   └── commands.py     #    CLI commands (agent, gateway, status, etc.)

├── templates/          # 📄 Template files
│   └── memory/         #    MEMORY.md, HEARTBEAT.md templates

├── utils/              # 🔧 Utilities
│   └── helpers.py      #    Helper functions

├── __main__.py         # 🚪 Entry point (nanobot command)
└── __init__.py         # 📦 Package initialization

Core Components

Agent (agent/)

The heart of nanobot. Contains the main agent loop and all tool implementations. Key Files:
  • loop.py (400 lines) - Main processing loop
  • context.py (200 lines) - Prompt building and context management
  • memory.py (100 lines) - Persistent memory interface
  • skills.py (150 lines) - Load skills from ~/.nanobot/skills/
  • subagent.py (200 lines) - Background task spawning
Tools Breakdown:
  • shell.py - Execute shell commands, supports timeouts and PATH customization
  • filesystem.py - 4 tools: read, write, edit (diff-based), list
  • web.py - Web search (Brave) and fetch (HTTP GET)
  • spawn.py - Create independent subagent tasks
  • cron.py - Schedule/list/delete periodic jobs
  • message.py - Send messages to specific channels
  • mcp.py - Model Context Protocol server integration

Channels (channels/)

Chat platform integrations. Each channel is ~300-500 lines. Design Pattern: All implement BaseChannel with start(), send(), stop(). Transport Methods:
  • Long Polling: Telegram
  • WebSocket: Discord, Feishu, QQ, DingTalk, Matrix, Mochat
  • Socket Mode: Slack
  • Bridge: WhatsApp (Node.js bridge)
  • IMAP/SMTP: Email
Common Features:
  • Access control (allowFrom lists)
  • Media handling (images, voice, files)
  • Typing indicators (Telegram, Discord)
  • Thread support (Discord, Slack)
  • Markdown formatting conversion

Bus (bus/)

Decouples channels from agent logic. Simple publish/subscribe pattern. Key Classes:
  • InboundMessage - User message → Agent
  • OutboundMessage - Agent response → User
  • MessageBus - Routes messages between channels and agent
Flow:
Channel → InboundMessage → Bus → AgentLoop
AgentLoop → OutboundMessage → Bus → Channel

Providers (providers/)

Abstracts LLM API differences. Most providers use LiteLLM wrapper. Registry Pattern:
  • ProviderSpec tuples in registry.py define all metadata
  • No if-elif chains needed
  • Auto-detection by model name, API key prefix, or base URL
Special Providers:
  • custom_provider.py - Direct OpenAI-compatible (bypasses LiteLLM)
  • openai_codex_provider.py - OAuth-based authentication
  • transcription.py - Groq Whisper for voice-to-text

Session (session/)

Conversation history storage. One session per user (or thread). Storage: ~/.nanobot/sessions/{session_id}.json Format: OpenAI message format
{
  "history": [
    {"role": "user", "content": "Hello"},
    {"role": "assistant", "content": "Hi there!"}
  ]
}
Features:
  • Auto-save after each turn
  • /new command clears history
  • Thread-scoped sessions (Discord, Slack)

Config (config/)

Pydantic-based configuration validation. Key Files:
  • schema.py - All config classes (Providers, Channels, Tools, Agents)
  • loader.py - Load from ~/.nanobot/config.json
Config Structure:
{
  "providers": { ... },
  "channels": { ... },
  "tools": { ... },
  "agents": { ... }
}

Cron (cron/)

Scheduled task execution using cron expressions. Storage: ~/.nanobot/workspace/cron/jobs.json Features:
  • Natural language parsing (“every day at 9am”)
  • Persistent across restarts
  • Agent can manage via CronTool

Heartbeat (heartbeat/)

Proactive task execution. Gateway wakes up every 30 minutes. How it works:
  1. Read ~/.nanobot/workspace/HEARTBEAT.md
  2. If tasks found, spawn agent to execute
  3. Send results to most recent chat channel
Example HEARTBEAT.md:
## Periodic Tasks
- [ ] Check weather
- [ ] Review emails

CLI (cli/)

Command-line interface using click. Commands:
  • nanobot onboard - Initialize workspace and config
  • nanobot agent - Interactive chat or one-off message
  • nanobot gateway - Start channel gateway
  • nanobot status - Show configuration status
  • nanobot provider login - OAuth login for providers
  • nanobot channels login - WhatsApp QR code login
  • nanobot channels status - Show channel status
Options:
  • -w, --workspace - Custom workspace directory
  • -c, --config - Custom config file
  • -p, --port - Gateway port
  • -m, --message - Single message mode
  • --logs - Show runtime logs
  • --no-markdown - Plain text output

Skills (skills/)

Markdown files that augment the agent’s capabilities. Bundled Skills:
  • github.md - GitHub operations (create PR, issue, etc.)
  • weather.md - Weather query instructions
  • tmux.md - Tmux session management
  • clawhub.md - ClawHub skill search/install
  • mochat.md - Mochat registration
  • moltbook.md - Moltbook community
  • clawdchat.md - ClawdChat community
Format:
# Skill Name

Description of what this skill does.

## Usage

When the user asks about X, do Y.

## Example

User: "Create a GitHub issue"
Agent: Uses GitHub API via web_fetch...
Loading: Auto-loaded from ~/.nanobot/skills/ on startup.

Templates (templates/)

Template files for workspace initialization. Files:
  • memory/MEMORY.md - Empty memory template
  • memory/HEARTBEAT.md - Empty heartbeat template
Usage: Copied to ~/.nanobot/workspace/ on nanobot onboard.

Utils (utils/)

Utility functions and helpers. Current: Minimal (project prefers inline code over abstractions).

File Size Distribution

By Component (Approximate Lines)

ComponentLinesDescription
agent/~2,500Core agent logic + tools
channels/~3,50010 channel integrations
providers/~1,500LLM provider abstractions
bus/~200Message routing
session/~150Session management
config/~800Configuration schemas
cron/~300Task scheduling
heartbeat/~100Proactive tasks
cli/~400CLI commands
skills/~500Bundled skills (markdown)
templates/~50Workspace templates
utils/~100Utilities
Total~10,000Complete codebase
Core Agent Lines: ~4,000 (agent/ + bus/ + session/ + providers/base.py)

Workspace Directory

User data stored in ~/.nanobot/:
~/.nanobot/
├── config.json              # User configuration
├── workspace/               # Agent workspace
│   ├── MEMORY.md            # Persistent memory
│   ├── HEARTBEAT.md         # Periodic tasks
│   ├── cron/
│   │   └── jobs.json        # Scheduled jobs
│   └── ...                  # Agent working files
├── sessions/                # Conversation history
│   ├── telegram_123.json
│   ├── discord_456.json
│   └── ...
├── skills/                  # User-installed skills
│   └── custom_skill.md
├── media/                   # Downloaded media files
│   ├── image_123.jpg
│   └── voice_456.ogg
└── logs/                    # Log files (if configured)

Import Structure

Internal Imports

from nanobot.agent.loop import AgentLoop
from nanobot.bus.events import InboundMessage, OutboundMessage
from nanobot.bus.queue import MessageBus
from nanobot.channels.base import BaseChannel
from nanobot.config.loader import load_config
from nanobot.providers.base import LLMProvider
from nanobot.session.manager import SessionManager

External Dependencies

Core:
  • loguru - Logging
  • pydantic - Config validation
  • click - CLI framework
  • litellm - LLM provider abstraction
Channels:
  • python-telegram-bot - Telegram
  • discord.py - Discord
  • whatsapp-bridge - WhatsApp
  • feishu-api - Feishu
  • slack-sdk - Slack
  • matrix-nio - Matrix
  • botpy - QQ
  • dingtalk-stream - DingTalk
  • python-socketio - Mochat
Tools:
  • aiohttp - HTTP client (web_fetch)
  • beautifulsoup4 - HTML parsing
  • croniter - Cron expression parsing
Optional:
  • groq - Voice transcription
  • brave-search - Web search

Code Style

Principles

  1. Explicit over implicit
  2. Simple over clever
  3. Readable over compact
  4. Flat over nested

Example

Good ✅:
if not self.config.api_key:
    logger.error("API key not configured")
    return

await self.client.connect()
logger.info("Connected successfully")
Bad ❌:
self.client.connect() if self.config.api_key else logger.error("No key")
logger.info("Connected") if self.client.connected else None

Docstrings

Use simple docstrings for public APIs:
def process_message(content: str, sender_id: str) -> dict:
    """Process an incoming message.
    
    Args:
        content: The message text.
        sender_id: The sender's identifier.
    
    Returns:
        A dict with processing results.
    """

Error Handling

Log and continue (don’t crash):
try:
    result = await risky_operation()
except Exception as e:
    logger.error("Operation failed: {}", e)
    return default_value

Design Patterns

1. Registry Pattern

Used in providers and tools:
PROVIDERS: tuple[ProviderSpec, ...] = (
    ProviderSpec(name="openai", ...),
    ProviderSpec(name="anthropic", ...),
)

def find_by_name(name: str) -> ProviderSpec | None:
    for spec in PROVIDERS:
        if spec.name == name:
            return spec
    return None

2. Message Bus Pattern

Decouples channels from agent logic:
# Channel publishes
await bus.publish_inbound(InboundMessage(...))

# Agent subscribes
msg = await bus.get_inbound()
await agent.process(msg)

# Agent publishes
await bus.publish_outbound(OutboundMessage(...))

# Channel subscribes
msg = await bus.get_outbound()
await channel.send(msg)

3. Strategy Pattern

Tools implement common interface:
class BaseTool(ABC):
    @abstractmethod
    async def execute(self, **kwargs) -> str:
        pass

class ExecTool(BaseTool):
    async def execute(self, command: str) -> str:
        # Implementation
        pass

4. Template Method Pattern

Channels implement base lifecycle:
class BaseChannel(ABC):
    async def start(self):
        pass
    
    async def send(self, msg):
        pass
    
    async def stop(self):
        pass

Testing Approach

Current State

Manual testing focused:
  1. CLI testing: nanobot agent -m "..."
  2. Gateway testing: Real channel integration
  3. Multi-provider testing: Try different LLMs
  4. Tool testing: Execute each tool manually

Future Improvements

  • Unit tests for core logic (agent loop, tools)
  • Integration tests for channels
  • Mock LLM responses for deterministic testing
  • CI/CD pipeline with automated tests

Performance Characteristics

Startup Time

  • CLI mode: Less than 1 second
  • Gateway mode: 2-5 seconds (channel connections)

Memory Usage

  • Base: ~50MB (Python runtime)
  • Per session: ~1KB (message history)
  • Per channel: ~5-10MB (WebSocket connections)

Response Latency

  • Network: 10-50ms (channel → bus → agent)
  • LLM: 0.5-3 seconds (provider-dependent)
  • Tools: Varies (shell: less than 1s, web: 1-5s)

Security Model

Access Control Layers

  1. Channel-level: allowFrom lists
  2. Tool-level: restrictToWorkspace sandbox
  3. File-level: Path validation and restrictions

Workspace Isolation

When restrictToWorkspace: true:
  • File operations restricted to ~/.nanobot/workspace/
  • Path traversal attempts blocked
  • Shell commands allowed but file access controlled

API Key Storage

  • Stored in ~/.nanobot/config.json (600 permissions)
  • Never logged or exposed
  • Environment variables set per-request

Extension Points

Easy places to add functionality:
  1. New Channel: Implement BaseChannel, add to manager.py
  2. New Provider: Add ProviderSpec, add config field
  3. New Tool: Implement BaseTool, register in AgentLoop
  4. New Skill: Drop markdown file in ~/.nanobot/skills/
  5. New Command: Add to cli/commands.py with @click.command()
See the respective guides for details:

Build docs developers (and LLMs) love