Aetheel — AI Agent Runtime with Mission Control
An event-driven AI agent runtime that connects Discord to your choice of coding agent CLI — Claude Code, Codex, Gemini CLI, OpenCode, or Pi — with an embedded Mission Control dashboard for real-time monitoring, second brain knowledge management, task tracking, and content curation. Inspired by OpenClaw's architecture — a gateway in front of an agent runtime with markdown-based personality, memory, scheduled behaviors, and proactive messaging.
How It Works
Discord Users ──► Discord Bot ──► Event Queue ──► Agent Runtime ──► Backend Adapter
▲ │
Heartbeats ─────────┤ ┌────────────────┘
Cron Jobs ──────────┤ ▼
Hooks ──────────────┘ Claude | Codex | Gemini
IPC Watcher ────────── OpenCode | Pi (CLI)
│
Markdown Config Files
(CLAUDE.md, memory.md, etc.)
│
Mission Control Dashboard ◄───────────┘
(http://localhost:3100) JSON Stores
(brain, tasks, content)
All inputs — Discord messages, heartbeat timers, cron jobs, lifecycle hooks — enter a unified event queue. The agent runtime reads your markdown config files fresh on each event, assembles a dynamic system prompt, and calls your configured backend CLI (Claude Code, Codex, Gemini, OpenCode, or Pi). The agent can write back to memory.md to persist facts across sessions, and send proactive messages via the IPC system.
The Mission Control dashboard runs on an embedded HTTP server, providing real-time visibility into agent activity, a knowledge store (Second Brain), productivity task tracking, content curation, and full configuration management — all backed by local JSON files with zero external dependencies.
Uses your existing subscription or API key for whichever backend you choose — no extra configuration needed.
Prerequisites
- Node.js 18+
- At least one supported coding agent CLI installed and signed in (see Multi-Backend Support)
- A Discord bot token (create one here) with Message Content Intent enabled
Quick Start
git clone <your-repo-url>
cd aetheel-2
npm install
cp .env.example .env # Edit with your Discord bot token
mkdir -p config
# Create config/CLAUDE.md with your persona (see Setup section)
npm run dev
The dashboard will be available at http://localhost:3100 once the gateway starts.
Or run the interactive setup:
bash scripts/setup.sh
Configuration
Environment Variables
Create a .env file in the project root (auto-loaded via dotenv):
| Variable | Required | Default | Description |
|---|---|---|---|
DISCORD_BOT_TOKEN |
Yes | — | Discord bot token |
OUTPUT_CHANNEL_ID |
No | — | Discord channel for heartbeat/cron/hook output |
AGENT_BACKEND |
No | claude |
Backend runtime: claude, codex, gemini, opencode, or pi |
BACKEND_CLI_PATH |
No | (backend name) | Path to the backend CLI binary |
BACKEND_MODEL |
No | — | Model override for the active backend |
BACKEND_MAX_TURNS |
No | 25 |
Max agentic turns per query |
CLAUDE_CLI_PATH |
No | claude |
(Deprecated) Use BACKEND_CLI_PATH instead |
CONFIG_DIR |
No | ./config |
Path to markdown config directory |
DASHBOARD_PORT |
No | 3100 |
Port for the Mission Control dashboard |
ALLOWED_TOOLS |
No | Read,Write,Edit,Glob,Grep,WebSearch,WebFetch |
Comma-separated tools (Claude only) |
PERMISSION_MODE |
No | bypassPermissions |
Claude Code permission mode |
QUERY_TIMEOUT_MS |
No | 120000 |
Max time per query (ms) |
MAX_CONCURRENT_QUERIES |
No | 5 |
Max simultaneous queries |
MAX_QUEUE_DEPTH |
No | 100 |
Max events in the queue |
IDLE_SESSION_TIMEOUT_MS |
No | 1800000 |
Session idle timeout (30 min) |
LOG_LEVEL |
No | info |
Log level: debug, info, warn, error |
Markdown Config Files
Place these in CONFIG_DIR (default: ./config/):
| File | Purpose | Required |
|---|---|---|
CLAUDE.md |
Persona: identity, personality, user context, tools | Yes |
agents.md |
Operating rules, cron jobs, hooks (parsed at startup) | No |
memory.md |
Long-term memory (agent-writable) | No (auto-created) |
heartbeat.md |
Proactive check definitions (parsed at startup) | No |
The gateway reads CLAUDE.md and memory.md fresh on every event — edit them anytime. agents.md and heartbeat.md are parsed at startup for timers, so restart after editing those.
Skills
Drop skill files into config/skills/{name}/SKILL.md and they're automatically loaded into the system prompt:
config/skills/
├── web-research/
│ └── SKILL.md → "When asked to research, use WebSearch..."
└── code-review/
└── SKILL.md → "When reviewing code, focus on..."
Multi-Backend Support
Aetheel supports five coding agent CLI backends. Switch between them via the AGENT_BACKEND environment variable — no code changes required.
| Backend | AGENT_BACKEND |
CLI | Install | Session Resume | Tool Filtering |
|---|---|---|---|---|---|
| Claude Code | claude (default) |
claude |
npm i -g @anthropic-ai/claude-code |
--resume <id> |
✅ --allowedTools |
| Codex | codex |
codex |
npm i -g @openai/codex |
exec resume <id> |
❌ |
| Gemini CLI | gemini |
gemini |
npm i -g @anthropic-ai/gemini-cli |
--resume <id> |
❌ |
| OpenCode | opencode |
opencode |
See OpenCode docs | --session <id> --continue |
❌ |
| Pi | pi |
pi |
npm i -g @mariozechner/pi-coding-agent |
--session <id> --continue |
❌ |
Switching Backends
Edit your .env file:
# Use Pi as the backend
AGENT_BACKEND=pi
BACKEND_CLI_PATH=pi
# BACKEND_MODEL=anthropic/sonnet # Optional model override
# Use Codex as the backend
AGENT_BACKEND=codex
BACKEND_CLI_PATH=codex
Each backend adapter normalizes the CLI's output into the same internal format (BackendEventResult), so all features — sessions, streaming, retries, error handling — work identically regardless of which CLI you use.
Note:
ALLOWED_TOOLSonly works with the Claude backend (it's the only CLI that supports--allowedToolsflags). Other backends will log a warning ifALLOWED_TOOLSis configured.
Features
🎮 Discord Integration
- Mention the bot (
@Aetheel hi) or use/claudeslash command /claude-resetto start a fresh conversation- Responses auto-split at 2000 chars with code block preservation
- Typing indicators while processing
💬 Session Management
- Per-channel conversation sessions (each backend uses its own resume mechanism)
- Sessions persist to
config/sessions.json(survive restarts) - Auto-cleanup of idle sessions after 30 minutes (configurable)
💚 Heartbeats (Timer Events)
Define in config/heartbeat.md:
## check-email
Interval: 1800
Instruction: Check my inbox for anything urgent.
🕐 Cron Jobs (Scheduled Events)
Define in config/agents.md:
## Cron Jobs
### morning-briefing
Cron: 0 8 * * *
Instruction: Good morning! Search for the latest AI news and post a summary.
🪝 Lifecycle Hooks
Define in config/agents.md:
## Hooks
### startup
Instruction: Say hello, you just came online.
### shutdown
Instruction: Save important context to memory.md.
📨 Proactive Messaging (IPC)
The agent can send messages to any Discord channel by writing JSON files to config/ipc/outbound/:
{"channelId": "123456789", "text": "Hey, found something interesting!"}
The gateway polls every 2 seconds and delivers the message.
📊 Message History
All inbound/outbound messages stored per channel in config/messages/{channelId}.json. Max 100 messages per channel, auto-trimmed.
📝 Conversation Archiving
Every exchange saved as readable markdown in config/conversations/{channelId}/{YYYY-MM-DD}.md.
🔁 Retry with Backoff
Backend CLI calls retry 3 times with exponential backoff (5s, 10s, 20s) on transient errors. Session corruption errors fail immediately.
📋 Structured Logging
Pino-based structured JSON logging. Set LOG_LEVEL=debug for verbose output. Pretty-printed in dev, JSON in production (NODE_ENV=production).
Mission Control Dashboard
An embedded web dashboard for real-time agent monitoring and data management. Accessible at http://localhost:3100 when the gateway is running.
Pages
| Page | Description |
|---|---|
| Command Center | Real-time stats (messages, heartbeats, cron runs, uptime), live activity feed, agent configuration |
| Activity | Full event history with real-time SSE updates |
| Sessions | Active Discord channel sessions and message history viewer |
| 🧠 Second Brain | Knowledge store with facts, notes, URLs, and file references — searchable with categories and tags. Also includes memory and persona editors, and a skills viewer |
| 📋 Productivity | Kanban-style task board (To Do → In Progress → Done) with priorities, projects, and due dates |
| 📰 Content Intel | Content curation and tracking — save articles, videos, papers, repos with status workflow (queued → read → archived) |
| ⏱️ Scheduler | View all active cron jobs, heartbeat checks, and lifecycle hooks |
| 🔌 Connections | Status of all integrations (Discord, Claude, heartbeat, cron, IPC, skills) |
| ⚙️ Settings | Agent configuration overview and quick todos |
Local Storage
All dashboard data is persisted as JSON files in the config/ directory — zero external dependencies:
| Store | File | Description |
|---|---|---|
| Brain facts | config/brain.json |
Notes, URLs, file references with categories and tags |
| Tasks | config/tasks.json |
Productivity tasks with status, priority, project, due dates |
| Content items | config/content-items.json |
Curated articles, videos, papers with read-status tracking |
| Activity log | config/activity-log.json |
Last 2000 events — survives restarts |
| Bot config | config/bot-config.json |
Dashboard-editable settings |
Data is flushed on shutdown and debounced during writes for performance.
Dashboard Development
You can run the dashboard standalone with mock data (no Discord bot or Claude CLI needed):
npm run dashboard
This starts a dev server at http://localhost:3100 with simulated activity events and sample data.
Dashboard API
All dashboard APIs are available at the same port:
| Endpoint | Method | Description |
|---|---|---|
/api/stats |
GET | Agent statistics (uptime, messages, heartbeats, cron runs) |
/api/activity |
GET | Recent activity log entries |
/api/config |
GET | Agent configuration |
/api/sessions |
GET | Active sessions |
/api/messages |
GET | Message history (per channel) |
/api/cron |
GET | Cron job definitions |
/api/heartbeats |
GET | Heartbeat definitions |
/api/hooks |
GET | Lifecycle hook definitions |
/api/connections |
GET | Integration statuses |
/api/skills |
GET | Loaded skills |
/api/memory |
GET/POST | Read/write memory.md |
/api/persona |
GET/POST | Read/write CLAUDE.md |
/api/brain |
GET/POST/DELETE | CRUD for brain facts |
/api/brain?q=... |
GET | Search brain facts |
/api/tasks |
GET/POST/DELETE | CRUD for productivity tasks |
/api/tasks/update |
POST | Update task fields (e.g. status) |
/api/content |
GET/POST/DELETE | CRUD for content items |
/api/content/update |
POST | Update content fields (e.g. status) |
/api/bot-config |
GET/POST | Read/write bot configuration |
/events |
SSE | Real-time event stream |
Deployment
systemd (recommended for Linux servers)
sudo bash scripts/setup.sh # Creates .env, config/, and systemd service
Or manually:
sudo cp scripts/aetheel.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable aetheel
sudo systemctl start aetheel
# View logs
sudo journalctl -u aetheel -f
PM2
npm run build
pm2 start dist/index.js --name aetheel
pm2 save
Dev mode
npm run dev # Full gateway + dashboard
npm run dashboard # Dashboard only (mock data)
Project Structure
src/
├── index.ts # Entry point (loads .env)
├── gateway-core.ts # Main orchestrator
├── config.ts # Environment variable loader
├── logger.ts # Pino structured logger
├── discord-bot.ts # Discord.js wrapper
├── event-queue.ts # Unified FIFO event queue
├── agent-runtime.ts # Core engine: reads configs, delegates to backend
├── backends/ # Pluggable CLI backend adapters
│ ├── types.ts # BackendAdapter interface & BackendName type
│ ├── registry.ts # resolveBackendName() & createBackend() factory
│ ├── index.ts # Barrel exports
│ ├── claude-backend.ts # Claude Code CLI adapter
│ ├── codex-backend.ts # OpenAI Codex CLI adapter
│ ├── gemini-backend.ts # Google Gemini CLI adapter
│ ├── opencode-backend.ts # OpenCode CLI adapter
│ └── pi-backend.ts # Pi Coding Agent CLI adapter
├── markdown-config-loader.ts # Reads CLAUDE.md, agents.md, memory.md
├── system-prompt-assembler.ts # Assembles system prompt with sections
├── skills-loader.ts # Loads skills from config/skills/*/SKILL.md
├── session-manager.ts # Channel → session ID (persisted, idle cleanup)
├── message-history.ts # Per-channel message storage
├── conversation-archiver.ts # Markdown conversation logs
├── ipc-watcher.ts # Polls ipc/outbound/ for proactive messages
├── response-formatter.ts # Splits long text for Discord's 2000 char limit
├── error-formatter.ts # Sanitizes errors (strips keys, paths, stacks)
├── heartbeat-scheduler.ts # Recurring timer events
├── cron-scheduler.ts # node-cron scheduled events
├── hook-manager.ts # Lifecycle hooks
├── bootstrap-manager.ts # First-run file validation/creation
├── channel-queue.ts # Per-channel sequential processing
├── shutdown-handler.ts # Graceful SIGTERM/SIGINT handling
├── dashboard-server.ts # Embedded HTTP server for Mission Control
├── dashboard-dev.ts # Standalone dashboard dev server (mock data)
├── activity-log.ts # In-memory + persisted activity event log
└── local-store.ts # JSON file persistence layer (brain, tasks, content)
dashboard/ # Mission Control frontend
├── index.html # SPA shell with sidebar navigation
├── styles.css # Dark-theme design system (~1700 lines)
└── app.js # Client-side SPA (routing, API, rendering)
config/ # Agent workspace (gitignored)
├── CLAUDE.md # Persona
├── agents.md # Rules, cron, hooks
├── memory.md # Long-term memory (agent-writable)
├── heartbeat.md # Heartbeat checks
├── sessions.json # Session persistence (auto)
├── brain.json # Second Brain facts (auto)
├── tasks.json # Productivity tasks (auto)
├── content-items.json # Content Intel items (auto)
├── activity-log.json # Persisted activity log (auto)
├── bot-config.json # Dashboard bot config (auto)
├── messages/ # Message history (auto)
├── conversations/ # Conversation archives (auto)
├── ipc/outbound/ # Proactive message queue (auto)
├── skills/ # Skill definitions
└── news/ # Example: agent-created content
docs/
├── PROCESS-FLOW.md # Step-by-step event processing walkthrough
└── FUTURE-FEATURES.md # Feature roadmap and ideas
Development
npm test # Run tests (vitest)
npm run dev # Full gateway + embedded dashboard
npm run dashboard # Dashboard only with mock data
npm run build # Compile TypeScript
npm start # Run compiled JS
License
MIT