feat: config-driven architecture, install wizard, live runtime switching, usage tracking, auto-failover

Major changes:
- Config-driven adapters: all channels (Slack, Discord, Telegram, WebChat, Webhooks) controlled via config.json with enabled flags and token auto-detection, no CLI flags required
- Runtime engine field: runtime.engine selects opencode/claude from config
- Interactive install script: 8-phase setup wizard with AI runtime detection/installation, token setup, identity file personalization (personality presets), aetheel CLI command, background service (launchd/systemd)
- Live runtime switching: /engine, /model, /provider commands hot-swap the AI runtime from chat without restart, changes persisted to config.json
- Usage tracking: per-request cost extraction from Claude Code JSON output, cumulative stats via /usage command
- Auto-failover: rate limit detection on both runtimes, automatic switch to other engine on quota errors with user notification
- Chat commands work without / prefix (Slack intercepts / in channels), commands: engine, model, provider, config, usage, reload, cron, subagents, status, help
- /config set for editing config.json from chat with dotted key notation
- Security audit saved to docs/security-audit.md
- Full command reference in docs/commands.md
- Future changes doc with NanoClaw agent teams analysis
- Logo added to README and WebChat UI
- README fully rewritten with all features documented
This commit is contained in:
2026-02-18 01:07:12 -05:00
parent 41b2f9a593
commit 6d73f74e0b
41 changed files with 11363 additions and 437 deletions

428
README.md
View File

@@ -1,13 +1,16 @@
<p align="center">
<img src="static/logo.jpeg" alt="Aetheel Logo" width="180" />
<h1 align="center">⚔️ Aetheel</h1>
<p align="center">
<strong>A personal AI assistant that lives in Slack — with persistent memory, dual runtimes, and zero cloud dependencies.</strong>
<strong>A personal AI assistant that lives in your chat — with persistent memory, dual runtimes, auto-failover, and zero cloud dependencies.</strong>
</p>
<p align="center">
<a href="#quick-start">Quick Start</a> •
<a href="#features">Features</a> •
<a href="#chat-commands">Commands</a> •
<a href="#architecture">Architecture</a> •
<a href="#configuration">Configuration</a>
<a href="#configuration">Configuration</a>
<a href="#docs">Docs</a>
</p>
</p>
@@ -18,190 +21,332 @@
### One-line install
```bash
curl -fsSL http://10.0.0.59:3051/tanmay/Aetheel/raw/branch/main/install.sh | sh
curl -fsSL http://10.0.0.59:3051/tanmay/Aetheel/raw/branch/main/install.sh | bash
```
This will clone the repo, set up a Python virtual environment, install dependencies, and walk you through configuration.
The interactive installer handles everything:
- Checks prerequisites (Python 3.12+, uv)
- Installs dependencies
- Detects or installs AI runtimes (OpenCode / Claude Code)
- Walks you through token setup for Slack, Discord, Telegram
- Installs the `aetheel` shell command
- Sets up a background service (launchd on macOS, systemd on Linux)
### After install
```bash
aetheel start # Start the bot
aetheel stop # Stop the background service
aetheel restart # Restart the service
aetheel status # Check status
aetheel logs # Tail live logs
aetheel setup # Re-run setup wizard
aetheel update # Pull latest + update deps
aetheel doctor # Run diagnostics
aetheel config # Edit config.json
aetheel help # All options
```
### Manual install
```bash
git clone http://10.0.0.59:3051/tanmay/Aetheel.git
cd Aetheel
uv sync # or: pip install -r requirements.txt
cp .env.example .env
# Edit .env with your Slack tokens
uv sync # or: pip install -r requirements.txt
cp .env.example .env # edit with your tokens
uv run python main.py # start
```
### Run
```bash
# Default — OpenCode runtime
uv run python main.py
# Claude Code runtime
uv run python main.py --claude
# Test mode (echo handler, no AI)
uv run python main.py --test
# Custom model
uv run python main.py --model anthropic/claude-sonnet-4-20250514
# Debug logging
uv run python main.py --log DEBUG
```
Everything is config-driven — no flags required. See [Configuration](#configuration).
---
## Features
### 💬 Multi-Channel
| Channel | Connection | Auth | Setup |
|---------|-----------|------|-------|
| Slack | Socket Mode (no public URL) | Bot + App tokens | [docs/slack-setup.md](docs/slack-setup.md) |
| Discord | Gateway (no public URL) | Bot token | [docs/discord-setup.md](docs/discord-setup.md) |
| Telegram | Bot API polling | Bot token | @BotFather |
| WebChat | HTTP + WebSocket on localhost | None (localhost only) | Config: `webchat.enabled: true` |
| Webhooks | HTTP POST endpoints | Bearer token | Config: `webhooks.enabled: true` |
All adapters are config-driven. Set a token in `.env` and the adapter auto-enables — no flags needed.
### 🤖 Dual AI Runtimes with Live Switching
| Runtime | Engine | Providers | Session Continuity |
|---------|--------|-----------|-------------------|
| OpenCode | `opencode` | Anthropic, OpenAI, Google, any | `--continue --session` |
| Claude Code | `claude` | Anthropic | `--continue --session-id` |
Switch engines, models, and providers live from chat — no restart needed:
```
engine claude
model claude-sonnet-4-20250514
engine opencode
model openai/gpt-4o
provider openai
```
### 🔄 Auto-Failover & Usage Tracking
When a rate limit or quota error is detected:
1. You get notified in the channel
2. Aetheel automatically switches to the other engine and retries
3. If failover succeeds, the response is delivered seamlessly
Claude Code returns per-request cost (`cost_usd`), which is tracked and viewable via the `usage` command. Rate limit hits and failover counts are also tracked.
### 🧠 Persistent Memory System
- **Identity files** — `SOUL.md` (personality), `USER.md` (user profile), `MEMORY.md` (long-term notes)
- **Hybrid search** — 0.7 vector + 0.3 BM25 keyword scoring over all memory files
- **Local embeddings** — `fastembed` (ONNX, BAAI/bge-small-en-v1.5, 384-dim), zero API calls
- **Hybrid search** — 0.7 vector + 0.3 BM25 keyword scoring
- **Local embeddings** — fastembed (ONNX, BAAI/bge-small-en-v1.5, 384-dim), zero API calls
- **SQLite storage** — FTS5 full-text search + JSON vector embeddings
- **Session logs** — Conversations auto-saved to `daily/YYYY-MM-DD.md`
- **File watching** — Memory re-indexes when you edit `.md` files
- **File watching** — Auto re-indexes when `.md` files change
### 🤖 Dual AI Runtimes
| Runtime | Flag | System Prompt | Session Continuity |
|---------|------|---------------|--------------------|
| **OpenCode** (default) | `--cli` / `--sdk` | XML-injected into message | `--continue --session <id>` |
| **Claude Code** | `--claude` | Native `--system-prompt` flag | `--continue --session-id <id>` |
### ⏰ Scheduler & Action Tags
Both return the same `AgentResponse` — zero code changes needed to switch.
The AI can trigger actions by including tags in its response:
### 💬 Slack Integration
- **Socket Mode** — no public URL or webhook needed
- **DMs + @mentions** — responds in both
- **Thread isolation** — each thread gets its own AI session
- **Chunked replies** — auto-splits long responses at Slack's 4000-char limit
| Tag | Effect |
|-----|--------|
| `[ACTION:remind\|5\|Drink water!]` | One-shot reminder in 5 minutes |
| `[ACTION:cron\|0 9 * * *\|Good morning!]` | Recurring cron job |
| `[ACTION:spawn\|Research Python 3.14]` | Background subagent task |
### ⏰ Action Tags
The AI can perform actions by including tags in its response:
Jobs are persisted in SQLite and survive restarts.
```
[ACTION:remind|2|Time to drink water! 💧]
```
### 🔧 More
The system strips the tag from the visible response and schedules the action.
- **Skills** — Teach the AI domain-specific behavior via `SKILL.md` files
- **Hooks** — Event-driven lifecycle hooks (startup, shutdown, commands)
- **Heartbeat** — Periodic proactive tasks via `HEARTBEAT.md`
- **Subagents** — Background AI tasks with results sent back to the channel
- **MCP Servers** — Extend the agent with external tool servers
- **WebChat** — Browser-based chat UI at `http://localhost:8080`
- **Webhooks** — HTTP endpoints for external systems to trigger the agent
### 🔒 Local-First
- Embeddings run locally (no OpenAI/Gemini API for memory)
- Memory stored in `~/.aetheel/` — your data stays on your machine
- Only the AI runtime (OpenCode/Claude) makes external API calls
- Embeddings run locally (no API calls for memory)
- All data stored in `~/.aetheel/` — your data stays on your machine
- Only the AI runtime makes external API calls
---
## Chat Commands
Type these as regular messages in any channel or DM. No `/` prefix needed.
> In Slack channels, Slack intercepts `/` as native slash commands. Use the prefix-free form (e.g. `engine claude` not `/engine claude`). In DMs and other adapters, both forms work.
### General
| Command | Description |
|---------|-------------|
| `status` | Bot status, engine, model, sessions |
| `help` | All available commands |
| `time` | Server time |
| `sessions` | Active session count |
| `reload` | Reload config and skills |
| `subagents` | List active background tasks |
### Runtime (live, no restart)
| Command | Description |
|---------|-------------|
| `engine` | Show current engine |
| `engine opencode` | Switch to OpenCode |
| `engine claude` | Switch to Claude Code |
| `model` | Show current model |
| `model <name>` | Switch model |
| `provider` | Show current provider (OpenCode only) |
| `provider <name>` | Switch provider |
| `usage` | LLM usage stats, costs, rate limits |
### Config
| Command | Description |
|---------|-------------|
| `config` | Config summary |
| `config show` | Full config.json |
| `config set <key> <value>` | Edit config (e.g. `config set runtime.timeout_seconds 300`) |
### Scheduler
| Command | Description |
|---------|-------------|
| `cron list` | List scheduled jobs |
| `cron remove <id>` | Remove a job |
See [docs/commands.md](docs/commands.md) for the full reference including terminal commands.
---
## Architecture
```
┌──────────────┐
│ Slack │ Socket Mode (no public URL)
Workspace
└──────┬───────┘
Events (DM, @mention)
┌──────────────┐ ┌──────────────────┐
Slack │────▶│ main.py │
Adapter │ │ (ai_handler)
└──────────────┘ └────────┬─────────┘
┌─────────┴─────────┐
┌──────────────┐ ┌──────────────┐
Memory AI Runtime
│ Manager │ │ (OpenCode │
or Claude)
│ • SOUL.md │ │ │
• USER.md │ │ subprocess
• MEMORY.md │ │ execution
│ • search └──────────────┘
│ • session
│ logs
└──────────────┘
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Slack Discord │ │ Telegram │ │ WebChat │
(Socket) │ │ (Gateway) │ │ (Polling) │ │ (WebSocket)
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└────────────────┴────────────────┴────────────────┘
┌───────────┴───────────┐
│ main.py
│ (ai_handler) │
│ • Command routing │
• Action tag parsing │
│ • Usage tracking │
• Auto-failover
└───┬──────────┬────────┘
┌─────────┴──┐ ┌───┴──────────┐
│ Memory │ │ AI Runtime
│ Manager │ │
OpenCode ◄──►│ Auto-failover
│ • SOUL.md │ │ Claude ◄──►
│ • search │ │
│ • logs │ │ subprocess │
└────────────┘ └──────────────┘
┌─────┴──────┐
│ Scheduler │ Skills, Hooks, Heartbeat,
│ Subagents │ Webhooks, MCP Servers
└────────────┘
```
### Project Structure
```
aetheel/
├── main.py # Entry point — handler, memory init, action tags
├── main.py # Entry point, command routing, failover
├── config.py # Config loading (config.json + .env)
├── cli.py # Click CLI (aetheel command)
├── install.sh # Interactive installer + setup wizard
├── adapters/
── slack_adapter.py # Slack Socket Mode adapter
── base.py # BaseAdapter + IncomingMessage
│ ├── slack_adapter.py # Slack Socket Mode
│ ├── discord_adapter.py # Discord Gateway
│ ├── telegram_adapter.py # Telegram Bot API
│ └── webchat_adapter.py # Browser WebSocket chat
├── agent/
│ ├── opencode_runtime.py # OpenCode CLI/SDK runtime
── claude_runtime.py # Claude Code CLI runtime
│ ├── opencode_runtime.py # OpenCode CLI/SDK + rate limit detection
── claude_runtime.py # Claude Code CLI + usage extraction
│ └── subagent.py # Background subagent manager
├── memory/
│ ├── manager.py # MemoryManager — sync, search, identity files
│ ├── embeddings.py # Local embeddings via fastembed
│ ├── manager.py # Sync, search, identity files, session logs
│ ├── embeddings.py # Local embeddings (fastembed)
│ ├── hybrid.py # Hybrid search (vector + BM25)
│ ├── schema.py # SQLite schema (files, chunks, FTS5)
│ ├── schema.py # SQLite schema
│ ├── internal.py # Hashing, chunking, file discovery
│ └── types.py # Config, result types
│ └── types.py # Config and result types
├── scheduler/
│ ├── scheduler.py # APScheduler with SQLite persistence
│ └── store.py # Job store
├── skills/ # Skill discovery and context injection
├── hooks/ # Lifecycle hook system
├── heartbeat/ # Periodic proactive tasks
├── webhooks/ # HTTP webhook receiver
├── static/
│ └── chat.html # WebChat browser UI
├── docs/
│ ├── memory-system.md # Memory architecture docs
│ ├── opencode-setup.md # OpenCode install & config guide
│ ├── slack-setup.md # Slack app setup guide
── opencode-integration-summary.md
├── .env.example # Template for secrets
├── pyproject.toml # Dependencies (uv/pip)
└── install.sh # One-click installer
│ ├── commands.md # Full command reference
│ ├── setup.md # Detailed setup guide
│ ├── security-audit.md # Security audit findings
── configuration.md # Config deep dive
│ ├── memory-system.md # Memory architecture
│ ├── features-guide.md # Feature walkthrough
│ ├── slack-setup.md # Slack app creation guide
│ └── discord-setup.md # Discord bot setup guide
├── tests/ # Test suite
├── .env.example # Secrets template
└── pyproject.toml # Dependencies
```
---
## Configuration
### Required: Slack Tokens
All settings live in `~/.aetheel/config.json`. Secrets (tokens, API keys) live in `.env`.
| Variable | Where to get it |
|----------|----------------|
| `SLACK_BOT_TOKEN` | [api.slack.com/apps](https://api.slack.com/apps) → OAuth & Permissions → Bot Token (`xoxb-...`) |
| `SLACK_APP_TOKEN` | [api.slack.com/apps](https://api.slack.com/apps) → Basic Info → App-Level Tokens (`xapp-...`) |
The install script writes both files during setup. You can also edit them from chat using `config set`.
See [`docs/slack-setup.md`](docs/slack-setup.md) for full Slack app creation instructions.
### Config File
### AI Runtime
```jsonc
{
"runtime": {
"engine": "opencode", // "opencode" or "claude"
"mode": "cli", // "cli" or "sdk"
"model": null, // e.g. "anthropic/claude-sonnet-4-20250514"
"provider": null, // e.g. "anthropic", "openai", "google"
"timeout_seconds": 120
},
"claude": {
"model": null, // e.g. "claude-sonnet-4-20250514"
"max_turns": 3,
"no_tools": false
},
"slack": { "enabled": true },
"telegram": { "enabled": false },
"discord": { "enabled": false, "listen_channels": [] },
"webchat": { "enabled": false, "port": 8080, "host": "127.0.0.1" },
"webhooks": { "enabled": false, "port": 8090, "token": "" },
"memory": { "workspace": "~/.aetheel/workspace", "db_path": "~/.aetheel/memory.db" },
"heartbeat": { "enabled": true, "default_channel": "slack" },
"hooks": { "enabled": true },
"mcp": { "servers": {} }
}
```
#### OpenCode (default)
Adapters auto-enable when their token is set in `.env`, even without `enabled: true`.
| Variable | Default | Description |
|----------|---------|-------------|
| `OPENCODE_MODE` | `cli` | `cli` (subprocess) or `sdk` (server API) |
| `OPENCODE_MODEL` | auto | Model, e.g. `anthropic/claude-sonnet-4-20250514` |
| `OPENCODE_TIMEOUT` | `120` | CLI timeout in seconds |
| `OPENCODE_SERVER_URL` | `http://localhost:4096` | SDK mode server URL |
| `OPENCODE_WORKSPACE` | `.` | Working directory for OpenCode |
### Secrets (.env)
#### Claude Code (`--claude`)
```bash
# Slack (required for Slack adapter)
SLACK_BOT_TOKEN=xoxb-...
SLACK_APP_TOKEN=xapp-...
| Variable | Default | Description |
|----------|---------|-------------|
| `CLAUDE_MODEL` | auto | Model, e.g. `claude-sonnet-4-20250514` |
| `CLAUDE_TIMEOUT` | `120` | CLI timeout in seconds |
| `CLAUDE_MAX_TURNS` | `3` | Max agentic tool-use turns |
| `CLAUDE_NO_TOOLS` | `true` | Disable tools for pure chat |
# Discord (required for Discord adapter)
DISCORD_BOT_TOKEN=...
### Memory System
# Telegram (required for Telegram adapter)
TELEGRAM_BOT_TOKEN=...
| Variable | Default | Description |
|----------|---------|-------------|
| `AETHEEL_WORKSPACE` | `~/.aetheel/workspace` | Path to memory files (SOUL.md, etc.) |
| `AETHEEL_MEMORY_DB` | `~/.aetheel/memory.db` | SQLite database path |
# Anthropic API key (for Claude Code runtime)
ANTHROPIC_API_KEY=sk-ant-...
```
### General
### Environment Variable Overrides
| Variable | Default | Description |
|----------|---------|-------------|
| `LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR` |
| Variable | Overrides |
|----------|-----------|
| `AETHEEL_ENGINE` | `runtime.engine` |
| `OPENCODE_MODE` | `runtime.mode` |
| `OPENCODE_MODEL` | `runtime.model` |
| `OPENCODE_PROVIDER` | `runtime.provider` |
| `CLAUDE_MODEL` | `claude.model` |
| `CLAUDE_TIMEOUT` | `claude.timeout_seconds` |
| `CLAUDE_MAX_TURNS` | `claude.max_turns` |
| `LOG_LEVEL` | `log_level` |
---
## Memory Files
Aetheel auto-creates three identity files in `~/.aetheel/workspace/`:
Auto-created in `~/.aetheel/workspace/` (or personalized during setup):
| File | Purpose |
|------|---------|
@@ -209,35 +354,58 @@ Aetheel auto-creates three identity files in `~/.aetheel/workspace/`:
| `USER.md` | User preferences, context, background |
| `MEMORY.md` | Long-term notes, facts, things to remember |
Edit these files directly — changes are picked up automatically via file watching.
All agents share the same identity files — the main agent and any background subagents it spawns all read from the same `SOUL.md`, `USER.md`, and `MEMORY.md`. This is by design: subagents are workers for the same assistant, not separate personalities.
Session logs are saved to `~/.aetheel/workspace/daily/YYYY-MM-DD.md` and indexed for search.
The installer lets you choose a personality preset (default, professional, casual, or custom) and fills in `USER.md` with your name, role, timezone, and preferences interactively.
Edit these files directly anytime — changes are picked up automatically via file watching.
Session logs: `~/.aetheel/workspace/daily/YYYY-MM-DD.md`
---
## Prerequisites
- **Python 3.14+** (managed via [uv](https://docs.astral.sh/uv/))
- **Slack workspace** with bot + app tokens
- **One of:**
- [OpenCode](https://opencode.ai) CLI`curl -fsSL https://opencode.ai/install | bash`
- Python 3.12+ (managed via [uv](https://docs.astral.sh/uv/))
- At least one chat platform token (Slack, Discord, Telegram, or WebChat)
- At least one AI runtime:
- [OpenCode](https://opencode.ai) — `curl -fsSL https://opencode.ai/install | bash`
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) — `npm install -g @anthropic-ai/claude-code`
The installer handles all of this interactively.
---
## Development
```bash
# Run tests
uv run python test_memory.py # Memory system smoke test
uv run python test_slack.py # Slack adapter unit tests
# Run full test suite
uv run python test_all.py
# Check help
uv run python main.py --help
# Run pytest
uv run python -m pytest tests/ -v
# Diagnostics
uv run python cli.py doctor
```
---
## Docs
| Document | Description |
|----------|-------------|
| [commands.md](docs/commands.md) | Full command reference (chat + terminal) |
| [setup.md](docs/setup.md) | Detailed setup guide |
| [configuration.md](docs/configuration.md) | Config deep dive |
| [security-audit.md](docs/security-audit.md) | Security audit findings |
| [memory-system.md](docs/memory-system.md) | Memory architecture |
| [features-guide.md](docs/features-guide.md) | Feature walkthrough |
| [slack-setup.md](docs/slack-setup.md) | Slack app creation |
| [discord-setup.md](docs/discord-setup.md) | Discord bot setup |
---
## Inspired By
Built with inspiration from [OpenClaw](https://github.com/nichochar/openclaw) — a TypeScript AI agent framework. Aetheel reimplements the core concepts (memory, hybrid search, session management) in Python with a local-first philosophy.