Clawlet is a small self-hosted AI assistant. It runs a fast reply path for chat, an async deep worker for tool-backed tasks, and a periodic consolidation loop for schedules and memory.
It is intentionally compact: 10 TypeScript source files, SQLite state, a file-backed message queue, and one Docker service.
Full design notes: docs/SPEC.md
- Fast chat triage with cheap model calls and heuristic replies for simple greetings or cancellations.
- Async deep work with file, shell, search, and web-fetch tools.
- SQLite memory split into episodic messages, semantic facts, personality, agenda, work log, meta, and schedules.
- File queue under
data/queue, so pending and processed messages are inspectable with normal shell tools. - Scheduled tasks using 5-field cron expressions.
- Single-file dashboard with chat, agenda, memory, work log, and schedule views.
- Docker-first deployment with persistent data volume.
- Node.js 20+ for local development. Node.js 22 is recommended.
- Docker and Docker Compose for containerized use.
- An OpenRouter API key.
Clone the repository and create local configuration:
git clone <repo-url>
cd clawlet
cp .env.example .envEdit .env and set OPENROUTER_API_KEY.
docker compose up -d --buildOpen http://localhost:3000.
Data is stored in the clawlet-data Docker volume. Config is mounted read-only from ./config.
Useful Docker commands:
docker compose logs -f
docker compose down
docker compose down -vnpm install
npm run devOpen http://localhost:3000. Local state is written to ./data.
Copy .env.example to .env:
OPENROUTER_API_KEY=sk-or-...
CLAWLET_MODEL=anthropic/claude-sonnet-4
CLAWLET_FAST_MODEL=meta-llama/llama-3.1-8b-instruct
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
CLAWLET_URL=http://localhost:3000
PORT=3000
DB_PATH=./data/clawlet.db
QUEUE_DIR=./data/queue
PERSONALITY_PATH=./config/personality.yaml
RULES_PATH=./config/rules.yamlBehavior is controlled by config/rules.yaml. These fields can also be overridden with environment variables:
| Rule | Env var |
|---|---|
active_hours |
CLAWLET_ACTIVE_HOURS |
think_interval_minutes |
CLAWLET_THINK_INTERVAL |
max_daily_tokens |
CLAWLET_MAX_TOKENS |
working_memory_cap |
CLAWLET_MEMORY_CAP |
deep_work_timeout_minutes |
CLAWLET_DEEP_TIMEOUT |
confidence_decay_cycles |
CLAWLET_DECAY_CYCLES |
confidence_decay_rate |
CLAWLET_DECAY_RATE |
confidence_archive_below |
CLAWLET_ARCHIVE_BELOW |
config/personality.yaml defines the assistant name, tone, interests, values, quirks, and hard behavioral constraints. Personality entries are seeded into SQLite on first startup.
The dashboard is served at / and connects over WebSocket.
| Tab | Purpose |
|---|---|
| Chat | Send messages and receive replies |
| Agenda | View pending, active, blocked, done, and cancelled tasks |
| Memory | Inspect semantic facts and confidence |
| Thinking | Follow deep-worker work log events |
| Schedules | Create, toggle, and delete scheduled tasks |
Clawlet is designed for trusted, self-hosted use. The deep worker can read files, write files, execute shell commands, search local paths, and fetch HTTP(S) URLs. Do not expose it directly to the public internet without adding authentication, network controls, and tool sandboxing appropriate for your environment.
All endpoints are served on PORT, default 3000.
| Method | Path | Description |
|---|---|---|
POST |
/api/chat |
Enqueue a chat message. Body: {"message":"...","channel":"dashboard"} |
GET |
/api/stats |
Memory, agenda, and token counters |
GET |
/api/memory |
Semantic facts |
GET |
/api/personality |
Personality rows |
GET |
/api/agenda |
Agenda items |
GET |
/api/worklog/:id |
Work log for one agenda item |
GET |
/api/schedules |
Schedules |
POST |
/api/schedules |
Create a schedule |
PUT |
/api/schedules/:id |
Update a schedule |
DELETE |
/api/schedules/:id |
Delete a schedule |
PATCH |
/api/schedules/:id/toggle |
Enable or disable a schedule |
POST |
/api/schedules/parse |
Ask the fast model to parse natural language into cron |
WebSocket events are JSON objects with type, data, and time.
| Event | Data |
|---|---|
snapshot |
Current agenda and schedules on connect |
reply |
Assistant reply |
status |
Deep-worker state change |
thought |
Consolidation thought |
worklog |
Deep-worker step output |
chat request
-> file queue
-> fast path
-> immediate reply
-> optional agenda item
-> deep worker
-> tools
-> async reply
consolidation loop
-> due schedules
-> fact extraction
-> confidence decay
-> episodic compaction
-> agenda review
-> personality reflection
Source layout:
src/
├── agent.ts Fast path, deep worker, consolidation
├── memory.ts SQLite schema, memory helpers, JSONL audit log
├── schedule.ts Cron schedules
├── agenda.ts Working memory and work logs
├── queue.ts File-backed message queue
├── server.ts Express, API, WebSocket wiring
├── llm.ts OpenRouter client
├── tools.ts Deep-worker tools
├── types.ts Shared types
└── index.ts Startup
npm run typecheck
npm run lint
npm test
npm run buildRun a single test:
npx vitest run src/schedule.test.ts
npx vitest run -t "creates a schedule"Current source budget:
| Metric | Current |
|---|---|
| Runtime source files | 10 |
| Runtime source lines | ~1,500 |
| Runtime dependencies | 8 |
- Replace
<repo-url>in this README after creating the GitHub repository. - Confirm
.env,data/,dist/,coverage/, andnode_modules/are untracked. - Run
npm run typecheck && npm run lint && npm test && npm run build. - Build the image with
docker compose build. - Add repository owner details to
LICENSEif desired.
MIT