mimiclaw Agent Skills & AI Rules

GitHub

MimiClaw: Personal Agent on a $5 chip. No OS(Linux). No Node.js. No Mac mini. No Raspberry Pi. No VPS. Hardware agents OS. On device Muse.

RAW Rules
4 Discovered Rules β€’ ~6,551 Estimated Tokens
Quick:
Loading skill content...

docs - System Architecture & Module Boundaries

docs/ARCHITECTURE.md project-spec ~4,108 tokens
Raw
# MimiClaw Architecture

> ESP32-S3 AI Agent firmware β€” C/FreeRTOS implementation running on bare metal (no Linux).

---

## System Overview

```
Telegram App (User)
    β”‚
    β”‚  HTTPS Long Polling
    β”‚
    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚               ESP32-S3 (MimiClaw)                β”‚
β”‚                                                  β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚
β”‚   β”‚  Telegram    │──────▢│   Inbound Queue  β”‚     β”‚
β”‚   β”‚  Poller      β”‚       β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚
β”‚   β”‚  (Core 0)    β”‚               β”‚                β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β–Ό                β”‚
β”‚                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚     Agent Loop          β”‚    β”‚
β”‚   β”‚  WebSocket   │─▢│     (Core 1)           β”‚    β”‚
β”‚   β”‚  Server      β”‚  β”‚                        β”‚    β”‚
β”‚   β”‚  (:18789)    β”‚  β”‚  Context ──▢ LLM Proxy β”‚    β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  Builder      (HTTPS)   β”‚    β”‚
β”‚                     β”‚       β–²          β”‚      β”‚    β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚       β”‚     tool_use?   β”‚    β”‚
β”‚   β”‚  Serial CLI  β”‚  β”‚       β”‚          β–Ό      β”‚    β”‚
β”‚   β”‚  (Core 0)    β”‚  β”‚  Tool Results ◀─ Tools  β”‚    β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚              (web_search)β”‚    β”‚
β”‚                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                                β”‚                  β”‚
β”‚                         β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”          β”‚
β”‚                         β”‚ Outbound Queueβ”‚          β”‚
β”‚                         β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜          β”‚
β”‚                                β”‚                  β”‚
β”‚                         β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”          β”‚
β”‚                         β”‚  Outbound    β”‚          β”‚
β”‚                         β”‚  Dispatch    β”‚          β”‚
β”‚                         β”‚  (Core 0)    β”‚          β”‚
β”‚                         β””β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”˜          β”‚
β”‚                            β”‚        β”‚             β”‚
β”‚                     Telegram    WebSocket          β”‚
β”‚                     sendMessage  send              β”‚
β”‚                                                   β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚   β”‚  SPIFFS (12 MB)                          β”‚    β”‚
β”‚   β”‚  /spiffs/config/  SOUL.md, USER.md       β”‚    β”‚
β”‚   β”‚  /spiffs/memory/  MEMORY.md, YYYY-MM-DD  β”‚    β”‚
β”‚   β”‚  /spiffs/sessions/ tg_<chat_id>.jsonl    β”‚    β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β”‚  Anthropic Messages API (HTTPS)
         β”‚  + Brave Search API (HTTPS)
         β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚ Claude API β”‚   β”‚ Brave Search β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

---

## Data Flow

```
1. User sends message on Telegram (or WebSocket)
2. Channel poller receives message, wraps in mimi_msg_t
3. Message pushed to Inbound Queue (FreeRTOS xQueue)
4. Agent Loop (Core 1) pops message:
   a. Load session history from SPIFFS (JSONL)
   b. Build system prompt (SOUL.md + USER.md + MEMORY.md + recent notes + tool guidance)
   c. Build cJSON messages array (history + current message)
   d. ReAct loop (max 10 iterations):
      i.   Call Claude API via HTTPS (non-streaming, with tools array)
      ii.  Parse JSON response β†’ text blocks + tool_use blocks
      iii. If stop_reason == "tool_use":
           - Execute each tool (e.g. web_search β†’ Brave Search API)
           - Append assistant content + tool_result to messages
           - Continue loop
      iv.  If stop_reason == "end_turn": break with final text
   e. Save user message + final assistant text to session file
   f. Push response to Outbound Queue
5. Outbound Dispatch (Core 0) pops response:
   a. Route by channel field ("telegram" β†’ sendMessage, "websocket" β†’ WS frame)
6. User receives reply
```

---

## Module Map

```
main/
β”œβ”€β”€ mimi.c                  Entry point β€” app_main() orchestrates init + startup
β”œβ”€β”€ mimi_config.h           All compile-time constants + build-time secrets include
β”œβ”€β”€ mimi_secrets.h          Build-time credentials (gitignored, highest priority)
β”œβ”€β”€ mimi_secrets.h.example  Template for mimi_secrets.h
β”‚
β”œβ”€β”€ bus/
β”‚   β”œβ”€β”€ message_bus.h       mimi_msg_t struct, queue API
β”‚   └── message_bus.c       Two FreeRTOS queues: inbound + outbound
β”‚
β”œβ”€β”€ wifi/
β”‚   β”œβ”€β”€ wifi_manager.h      WiFi STA lifecycle API
β”‚   └── wifi_manager.c      Event handler, exponential backoff
β”‚
β”œβ”€β”€ telegram/
β”‚   β”œβ”€β”€ telegram_bot.h      Bot init/start, send_message API
β”‚   └── telegram_bot.c      Long polling loop, JSON parsing, message splitting
β”‚
β”œβ”€β”€ llm/
β”‚   β”œβ”€β”€ llm_proxy.h         llm_chat() + llm_chat_tools() API, tool_use types
β”‚   └── llm_proxy.c         Anthropic Messages API (non-streaming), tool_use parsing
β”‚
β”œβ”€β”€ agent/
β”‚   β”œβ”€β”€ agent_loop.h        Agent task init/start
β”‚   β”œβ”€β”€ agent_loop.c        ReAct loop: LLM call β†’ tool execution β†’ repeat
β”‚   β”œβ”€β”€ context_builder.h   System prompt + messages builder API
β”‚   └── context_builder.c   Reads bootstrap files + memory + tool guidance
β”‚
β”œβ”€β”€ tools/
β”‚   β”œβ”€β”€ tool_registry.h     Tool definition struct, register/dispatch API
β”‚   β”œβ”€β”€ tool_registry.c     Tool registration, JSON schema builder, dispatch by name
β”‚   β”œβ”€β”€ tool_web_search.h   Web search tool API
β”‚   └── tool_web_search.c   Brave Search API via HTTPS (direct + proxy)
β”‚
β”œβ”€β”€ memory/
β”‚   β”œβ”€β”€ memory_store.h      Long-term + daily memory API
β”‚   β”œβ”€β”€ memory_store.c      MEMORY.md read/write, daily .md append/read
β”‚   β”œβ”€β”€ session_mgr.h       Per-chat session API
β”‚   └── session_mgr.c       JSONL session files, ring buffer history
β”‚
β”œβ”€β”€ gateway/
β”‚   β”œβ”€β”€ ws_server.h         WebSocket server API
β”‚   └── ws_server.c         ESP HTTP server with WS upgrade, client tracking
β”‚
β”œβ”€β”€ proxy/
β”‚   β”œβ”€β”€ http_proxy.h        Proxy connection API
β”‚   └── http_proxy.c        HTTP CONNECT tunnel + TLS via esp_tls
β”‚
β”œβ”€β”€ cli/
β”‚   β”œβ”€β”€ serial_cli.h        CLI init API
β”‚   └── serial_cli.c        esp_console REPL with debug/maintenance commands
β”‚
└── ota/
    β”œβ”€β”€ ota_manager.h       OTA update API
    └── ota_manager.c       esp_https_ota wrapper
```

---

## FreeRTOS Task Layout

| Task               | Core | Priority | Stack  | Description                          |
|--------------------|------|----------|--------|--------------------------------------|
| `tg_poll`          | 0    | 5        | 12 KB  | Telegram long polling (30s timeout)  |
| `agent_loop`       | 1    | 6        | 12 KB  | Message processing + Claude API call |
| `outbound`         | 0    | 5        | 8 KB   | Route responses to Telegram / WS     |
| `serial_cli`       | 0    | 3        | 4 KB   | USB serial console REPL              |
| httpd (internal)   | 0    | 5        | β€”      | WebSocket server (esp_http_server)   |
| wifi_event (IDF)   | 0    | 8        | β€”      | WiFi event handling (ESP-IDF)        |

**Core allocation strategy**: Core 0 handles I/O (network, serial, WiFi). Core 1 is dedicated to the agent loop (CPU-bound JSON building + waiting on HTTPS).

---

## Memory Budget

| Purpose                            | Location       | Size     |
|------------------------------------|----------------|----------|
| FreeRTOS task stacks               | Internal SRAM  | ~40 KB   |
| WiFi buffers                       | Internal SRAM  | ~30 KB   |
| TLS connections x2 (Telegram + Claude) | PSRAM      | ~120 KB  |
| JSON parse buffers                 | PSRAM          | ~32 KB   |
| Session history cache              | PSRAM          | ~32 KB   |
| System prompt buffer               | PSRAM          | ~16 KB   |
| LLM response stream buffer         | PSRAM          | ~32 KB   |
| Remaining available                | PSRAM          | ~7.7 MB  |

Large buffers (32 KB+) are allocated from PSRAM via `heap_caps_calloc(1, size, MALLOC_CAP_SPIRAM)`.

---

## Flash Partition Layout

```
Offset      Size      Name        Purpose
─────────────────────────────────────────────
0x009000    24 KB     nvs         ESP-IDF internal use (WiFi calibration etc.)
0x00F000     8 KB     otadata     OTA boot state
0x011000     4 KB     phy_init    WiFi PHY calibration
0x020000     2 MB     ota_0       Firmware slot A
0x220000     2 MB     ota_1       Firmware slot B
0x420000    12 MB     spiffs      Markdown memory, sessions, config
0xFF0000    64 KB     coredump    Crash dump storage
```

Total: 16 MB flash.

---

## Storage Layout (SPIFFS)

SPIFFS is a flat filesystem β€” no real directories. Files use path-like names.

```
/spiffs/config/SOUL.md          AI personality definition
/spiffs/config/USER.md          User profile
/spiffs/memory/MEMORY.md        Long-term persistent memory
/spiffs/memory/2026-02-05.md    Daily notes (one file per day)
/spiffs/sessions/tg_12345.jsonl Session history (one file per Telegram chat)
```

Session files are JSONL (one JSON object per line):
```json
{"role":"user","content":"Hello","ts":1738764800}
{"role":"assistant","content":"Hi there!","ts":1738764802}
```

---

## Configuration

All configuration is done exclusively through `mimi_secrets.h` at build time. There is no runtime configuration β€” changing any setting requires `idf.py fullclean && idf.py build`.

| Define                       | Description                             |
|------------------------------|-----------------------------------------|
| `MIMI_SECRET_WIFI_SSID`     | WiFi SSID                               |
| `MIMI_SECRET_WIFI_PASS`     | WiFi password                           |
| `MIMI_SECRET_TG_TOKEN`      | Telegram Bot API token                  |
| `MIMI_SECRET_API_KEY`       | Anthropic API key                       |
| `MIMI_SECRET_MODEL`         | Model ID (default: claude-opus-4-6)     |
| `MIMI_SECRET_PROXY_HOST`    | HTTP proxy hostname/IP (optional)       |
| `MIMI_SECRET_PROXY_PORT`    | HTTP proxy port (optional)              |
| `MIMI_SECRET_SEARCH_KEY`    | Brave Search API key (optional)         |

NVS is still initialized (required by ESP-IDF WiFi internals) but is not used for application configuration.

---

## Message Bus Protocol

The internal message bus uses two FreeRTOS queues carrying `mimi_msg_t`:

```c
typedef struct {
    char channel[16];   // "telegram", "websocket", "cli"
    char chat_id[32];   // Telegram chat ID or WS client ID
    char *content;      // Heap-allocated text (ownership transferred)
} mimi_msg_t;
```

- **Inbound queue**: channels β†’ agent loop (depth: 8)
- **Outbound queue**: agent loop β†’ dispatch β†’ channels (depth: 8)
- Content string ownership is transferred on push; receiver must `free()`.

---

## WebSocket Protocol

Port: **18789**. Max clients: **4**.

**Client β†’ Server:**
```json
{"type": "message", "content": "Hello", "chat_id": "ws_client1"}
```

**Server β†’ Client:**
```json
{"type": "response", "content": "Hi there!", "chat_id": "ws_client1"}
```

Client `chat_id` is auto-assigned on connection (`ws_<fd>`) but can be overridden in the first message.

---

## Claude API Integration

Endpoint: `POST https://api.anthropic.com/v1/messages`

Request format (Anthropic-native, non-streaming, with tools):
```json
{
  "model": "claude-opus-4-6",
  "max_tokens": 4096,
  "system": "<system prompt>",
  "tools": [
    {
      "name": "web_search",
      "description": "Search the web for current information.",
      "input_schema": {"type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"]}
    }
  ],
  "messages": [
    {"role": "user", "content": "Hello"},
    {"role": "assistant", "content": "Hi!"},
    {"role": "user", "content": "What's the weather today?"}
  ]
}
```

Key difference from OpenAI: `system` is a top-level field, not inside the `messages` array.

Non-streaming JSON response:
```json
{
  "id": "msg_xxx",
  "type": "message",
  "role": "assistant",
  "content": [
    {"type": "text", "text": "Let me search for that."},
    {"type": "tool_use", "id": "toolu_xxx", "name": "web_search", "input": {"query": "weather today"}}
  ],
  "stop_reason": "tool_use"
}
```

When `stop_reason` is `"tool_use"`, the agent loop executes each tool and sends results back:
```json
{"role": "assistant", "content": [<text + tool_use blocks>]}
{"role": "user", "content": [{"type": "tool_result", "tool_use_id": "toolu_xxx", "content": "..."}]}
```

The loop repeats until `stop_reason` is `"end_turn"` (max 10 iterations).

---

## Startup Sequence

```
app_main()
  β”œβ”€β”€ init_nvs()                    NVS flash init (erase if corrupted)
  β”œβ”€β”€ esp_event_loop_create_default()
  β”œβ”€β”€ init_spiffs()                 Mount SPIFFS at /spiffs
  β”œβ”€β”€ message_bus_init()            Create inbound + outbound queues
  β”œβ”€β”€ memory_store_init()           Verify SPIFFS paths
  β”œβ”€β”€ session_mgr_init()
  β”œβ”€β”€ wifi_manager_init()           Init WiFi STA mode + event handlers
  β”œβ”€β”€ http_proxy_init()             Load proxy config from build-time secrets
  β”œβ”€β”€ telegram_bot_init()           Load bot token from build-time secrets
  β”œβ”€β”€ llm_proxy_init()              Load API key + model from build-time secrets
  β”œβ”€β”€ tool_registry_init()          Register tools, build tools JSON
  β”œβ”€β”€ agent_loop_init()
  β”œβ”€β”€ serial_cli_init()             Start REPL (works without WiFi)
  β”‚
  β”œβ”€β”€ wifi_manager_start()          Connect using build-time credentials
  β”‚   └── wifi_manager_wait_connected(30s)
  β”‚
  └── [if WiFi connected]
      β”œβ”€β”€ telegram_bot_start()      Launch tg_poll task (Core 0)
      β”œβ”€β”€ agent_loop_start()        Launch agent_loop task (Core 1)
      β”œβ”€β”€ ws_server_start()         Start httpd on port 18789
      └── outbound_dispatch task    Launch outbound task (Core 0)
```

If WiFi credentials are missing or connection times out, the CLI remains available for diagnostics.

---

## Serial CLI Commands

The CLI provides debug and maintenance commands only. All configuration is done via `mimi_secrets.h`.

| Command                        | Description                          |
|--------------------------------|--------------------------------------|
| `wifi_status`                  | Show connection status and IP        |
| `memory_read`                  | Print MEMORY.md contents             |
| `memory_write <CONTENT>`       | Overwrite MEMORY.md                  |
| `session_list`                 | List all session files               |
| `session_clear <CHAT_ID>`      | Delete a session file                |
| `heap_info`                    | Show internal + PSRAM free bytes     |
| `restart`                      | Reboot the device                    |
| `help`                         | List all available commands           |

---

## Nanobot Reference Mapping

| Nanobot Module              | MimiClaw Equivalent            | Notes                        |
|-----------------------------|--------------------------------|------------------------------|
| `agent/loop.py`             | `agent/agent_loop.c`           | ReAct loop with tool use     |
| `agent/context.py`          | `agent/context_builder.c`      | Loads SOUL.md + USER.md + memory + tool guidance |
| `agent/memory.py`           | `memory/memory_store.c`        | MEMORY.md + daily notes      |
| `session/manager.py`        | `memory/session_mgr.c`         | JSONL per chat, ring buffer  |
| `channels/telegram.py`      | `telegram/telegram_bot.c`      | Raw HTTP, no python-telegram-bot |
| `bus/events.py` + `queue.py`| `bus/message_bus.c`            | FreeRTOS queues vs asyncio   |
| `providers/litellm_provider.py` | `llm/llm_proxy.c`         | Direct Anthropic API only    |
| `config/schema.py`          | `mimi_config.h` + `mimi_secrets.h` | Build-time secrets only  |
| `cli/commands.py`           | `cli/serial_cli.c`             | esp_console REPL             |
| `agent/tools/*`             | `tools/tool_registry.c` + `tool_web_search.c` | web_search via Brave API |
| `agent/subagent.py`         | *(not yet implemented)*        | See TODO.md                  |
| `agent/skills.py`           | *(not yet implemented)*        | See TODO.md                  |
| `cron/service.py`           | *(not yet implemented)*        | See TODO.md                  |
| `heartbeat/service.py`      | *(not yet implemented)*        | See TODO.md                  |