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
18 KiB
Aetheel Phase 3 Feature Spec
Specification for implementing heartbeat, CLI, webchat, self-modification, agent-to-agent communication, and tool/MCP/web search integration.
Table of Contents
- Runtime Capabilities Audit
- Tool System, MCP & Web Search
- Heartbeat / Proactive System
- CLI Interface
- WebChat Interface
- Self-Modification
- Agent-to-Agent Communication
- Implementation Order
1. Runtime Capabilities Audit
Before building anything, here's what OpenCode and Claude Code already provide natively that Aetheel can leverage without custom implementation.
OpenCode Built-in Tools
OpenCode ships with these tools enabled by default (no config needed):
| Tool | Description | Aetheel Status |
|---|---|---|
bash |
Execute shell commands | ✅ Available via runtime |
read |
Read file contents | ✅ Available via runtime |
write |
Create/overwrite files | ✅ Available via runtime |
edit |
Modify files via string replacement | ✅ Available via runtime |
grep |
Regex search across files | ✅ Available via runtime |
glob |
Find files by pattern | ✅ Available via runtime |
list |
List directory contents | ✅ Available via runtime |
websearch |
Web search via Exa AI (no API key needed) | ✅ Available — no setup required |
webfetch |
Fetch and read web pages | ✅ Available via runtime |
skill |
Load SKILL.md files | ✅ Available via runtime |
todowrite / todoread |
Task tracking | ✅ Available via runtime |
lsp |
Code intelligence (experimental) | ✅ Available via runtime |
patch |
Apply diffs | ✅ Available via runtime |
| Custom tools | User-defined JS/TS tools | 🟡 Available but not wired |
| MCP servers | External tool servers | 🟡 Available but not configured |
Claude Code Built-in Tools
Claude Code provides these tools natively:
| Tool | Description | Aetheel Status |
|---|---|---|
Bash |
Shell execution | ✅ Available via runtime |
Read, Write, Edit |
File operations | ✅ Available via runtime |
Glob, Grep |
File search | ✅ Available via runtime |
WebSearch, WebFetch |
Web access | ✅ Available via runtime |
Task, TaskOutput, TaskStop |
Subagent spawning | ✅ Available via runtime |
TeamCreate, TeamDelete, SendMessage |
Agent teams | ✅ Available via runtime |
Skill |
Load skills | ✅ Available via runtime |
| MCP servers | Via .mcp.json config |
🟡 Available but not configured |
Key Insight
Both runtimes already have web search, file ops, bash, and subagent tools built in. Aetheel doesn't need to implement these — it just needs to stop blocking them. Currently Aetheel's system prompt doesn't tell the agent about these capabilities, and Claude Code's --allowedTools "" (no_tools=true) actively disables them.
2. Tool System, MCP & Web Search
What Needs to Change
Problem: Aetheel currently runs both runtimes in "pure chat" mode — tools are disabled or not mentioned in the system prompt.
Solution: Enable tool access and configure MCP servers.
2.1 Enable Runtime Tools
For OpenCode (CLI mode), tools are enabled by default. No change needed.
For OpenCode (SDK mode), tools are enabled by default on the server. No change needed.
For Claude Code, change no_tools default from true to false in config, and set a sensible allowedTools list:
# config.py — ClaudeConfig
@dataclass
class ClaudeConfig:
no_tools: bool = False # Changed from True
allowed_tools: list[str] = field(default_factory=lambda: [
"Bash", "Read", "Write", "Edit", "Glob", "Grep",
"WebSearch", "WebFetch",
"Task", "Skill",
])
2.2 Update System Prompt
Add tool awareness to build_aetheel_system_prompt():
# Your Tools
- You have access to shell commands, file operations, and web search
- Use web search to look up current information when needed
- You can read and write files in the workspace (~/.aetheel/workspace/)
- You can execute shell commands for system tasks
2.3 MCP Server Configuration
Create an opencode.json in the workspace for OpenCode MCP config, or a .mcp.json for Claude Code. This lets users add external tool servers.
Add to ~/.aetheel/config.json:
{
"mcp": {
"servers": {
"example": {
"command": "uvx",
"args": ["some-mcp-server"],
"env": {}
}
}
}
}
Aetheel writes this to the appropriate config file (opencode.json or .mcp.json) in the workspace directory before launching the runtime.
2.4 Implementation Tasks
- Change
ClaudeConfig.no_toolsdefault toFalse - Add
allowed_toolstoClaudeConfigwith sensible defaults - Update
_build_cli_args()inclaude_runtime.pyto pass--allowedToolsfrom config - Update
build_aetheel_system_prompt()to mention available tools - Add
mcpsection toconfig.pyandconfig.json - Write MCP config files to workspace before runtime launch
- Add
TOOLS.mdto workspace identity files (like PicoClaw/OpenClaw)
Inspiration
- NanoClaw: Passes
allowedToolslist toquery()call, runs custom MCP server for IPC - PicoClaw: Built-in tool registry in
pkg/tools/,TOOLS.mddescribes tools to agent - OpenClaw: Tool registry with streaming,
TOOLS.mdin workspace
3. Heartbeat / Proactive System
How Inspiration Repos Do It
PicoClaw: Reads HEARTBEAT.md every 30 minutes. Quick tasks run directly, long tasks spawn subagents. The heartbeat file contains prompts like "Check if any cron jobs need attention" or "Review MEMORY.md for stale entries."
Nanobot: Has a heartbeat/ module that triggers periodic agent runs.
NanoClaw: Uses scheduled tasks via MCP tools — the agent itself schedules recurring tasks using schedule_task with cron expressions. No separate heartbeat file.
OpenClaw: Cron system + wakeups. Heartbeat runs are suppressed from WebChat broadcast when showOk: false.
Design for Aetheel
Combine PicoClaw's HEARTBEAT.md approach (simple, file-based) with Aetheel's existing scheduler:
3.1 HEARTBEAT.md
Create ~/.aetheel/workspace/HEARTBEAT.md as a user-editable file:
# Heartbeat Tasks
Tasks that run periodically. Each section is a task prompt.
## Every 30 minutes
- Check if any scheduled reminders need attention
- Review recent session logs for anything worth remembering
## Every morning (9:00 AM)
- Summarize yesterday's conversations
- Check for any pending follow-ups in MEMORY.md
## Every evening (6:00 PM)
- Update MEMORY.md with today's key learnings
3.2 Heartbeat Runner
New module: heartbeat/heartbeat.py
class HeartbeatRunner:
def __init__(self, runtime, send_fn, workspace_dir, scheduler):
...
def start(self):
"""Register heartbeat tasks with the scheduler."""
# Parse HEARTBEAT.md
# Register cron jobs for each section
# Jobs route through ai_handler with synthetic messages
def _parse_heartbeat_md(self) -> list[HeartbeatTask]:
"""Parse HEARTBEAT.md into structured tasks."""
...
def _on_heartbeat(self, task: HeartbeatTask):
"""Called when a heartbeat fires. Routes to AI handler."""
...
3.3 Integration
- Parse
HEARTBEAT.mdat startup, register tasks with existingScheduler - Heartbeat tasks create synthetic
IncomingMessagewithsource="heartbeat" - Results can be sent to a configured channel or logged silently
- Add
heartbeatsection to config:
{
"heartbeat": {
"enabled": true,
"default_channel": "slack",
"default_channel_id": "C123456",
"silent": false
}
}
3.4 Implementation Tasks
- Create
heartbeat/module withHeartbeatRunner - Create default
HEARTBEAT.mdin workspace (like SOUL.md bootstrap) - Parse markdown sections into cron expressions + prompts
- Register with existing
Schedulerat startup - Add heartbeat config section
- Wire into
main.pyinitialization
4. CLI Interface
How Inspiration Repos Do It
Nanobot: nanobot agent -m "...", nanobot gateway, nanobot status, nanobot cron add/list/remove
OpenClaw: openclaw gateway, openclaw agent --message "...", openclaw doctor, openclaw pairing approve
PicoClaw: Binary with subcommands via Go's cobra library
Design for Aetheel
Use Python's click library for a clean CLI with subcommands.
4.1 CLI Structure
aetheel # Start with default adapters (same as current main.py)
aetheel start # Start with all configured adapters
aetheel start --discord # Start with specific adapters
aetheel chat "message" # One-shot chat (no adapter, just AI)
aetheel status # Show runtime status
aetheel cron list # List scheduled jobs
aetheel cron remove <id> # Remove a job
aetheel sessions list # List active sessions
aetheel sessions clear # Clear stale sessions
aetheel config show # Print current config
aetheel config edit # Open config in $EDITOR
aetheel config init # Reset to defaults
aetheel memory search "q" # Search memory
aetheel memory sync # Force memory re-index
aetheel doctor # Diagnostics (check runtime, tokens, etc.)
4.2 Implementation
New file: cli.py
import click
@click.group()
def cli():
"""Aetheel — AI-Powered Personal Assistant"""
pass
@cli.command()
@click.option("--discord", is_flag=True)
@click.option("--telegram", is_flag=True)
@click.option("--claude", is_flag=True)
@click.option("--model", default=None)
@click.option("--test", is_flag=True)
@click.option("--log", default="INFO")
def start(discord, telegram, claude, model, test, log):
"""Start Aetheel with configured adapters."""
# Current main() logic moves here
...
@cli.command()
@click.argument("message")
def chat(message):
"""One-shot chat with the AI (no adapter needed)."""
...
@cli.group()
def cron():
"""Manage scheduled jobs."""
pass
@cron.command("list")
def cron_list():
...
@cron.command("remove")
@click.argument("job_id")
def cron_remove(job_id):
...
# ... etc
4.3 Entry Point
Add to pyproject.toml:
[project.scripts]
aetheel = "cli:cli"
4.4 Implementation Tasks
- Add
clickdependency topyproject.toml - Create
cli.pywith command groups - Move
main()logic intostartcommand - Add
chat,status,cron,sessions,config,memory,doctorcommands - Add entry point to
pyproject.toml - Keep
main.pyas backward-compatible wrapper
5. WebChat Interface
How Inspiration Repos Do It
OpenClaw: Full WebSocket-based WebChat as an internal channel. Messages routed through the gateway with messageChannel: "webchat". Has a web UI built with React.
Nanobot/NanoClaw/PicoClaw: No webchat.
Design for Aetheel
A lightweight HTTP server with WebSocket support, served as a new adapter.
5.1 Architecture
Browser ←→ WebSocket ←→ WebChatAdapter ←→ ai_handler ←→ Runtime
↕
Static HTML/JS
5.2 Implementation
New adapter: adapters/webchat_adapter.py
Use aiohttp for both the static file server and WebSocket handler:
GET /— Serves the chat UI (single HTML file with embedded JS/CSS)WS /ws— WebSocket for real-time chat- Each WebSocket connection = one conversation (session isolation)
- Messages flow through the same
BaseAdapter._dispatch→ai_handlerpath
5.3 Chat UI
Single self-contained HTML file at static/chat.html:
- Minimal chat interface (message list + input box)
- WebSocket connection to the local server
- Markdown rendering for AI responses
- No build step, no npm, no framework — just vanilla HTML/JS/CSS
5.4 Config
{
"webchat": {
"enabled": false,
"port": 8080,
"host": "127.0.0.1"
}
}
5.5 Implementation Tasks
- Add
aiohttpdependency - Create
adapters/webchat_adapter.pyextendingBaseAdapter - Create
static/chat.html— self-contained chat UI - Add webchat config section
- Wire into
main.py/ CLIstartcommand with--webchatflag - Add to
_adaptersdict like other adapters
6. Self-Modification
Can the Runtime Do This?
Yes. Both OpenCode and Claude Code have Write, Edit, and Bash tools that can modify any file the process has access to. The agent can already:
- Edit
~/.aetheel/config.json(via file write tools) - Create new skill files in
~/.aetheel/workspace/skills/<name>/SKILL.md - Update
SOUL.md,USER.md,MEMORY.md - Modify
HEARTBEAT.mdto add/remove periodic tasks
What Aetheel Needs to Do
The agent just needs to be told it can do this. Update the system prompt:
# Self-Modification
- You can edit your own config at ~/.aetheel/config.json
- You can create new skills by writing SKILL.md files to ~/.aetheel/workspace/skills/<name>/SKILL.md
- You can update your identity files (SOUL.md, USER.md, MEMORY.md)
- You can modify HEARTBEAT.md to change your periodic tasks
- After editing config, tell the user to restart for changes to take effect
- After adding a skill, it will be available on next restart (or use /reload if implemented)
6.1 Hot Reload (Optional Enhancement)
Add a /reload command that re-reads config and skills without restart:
if text_lower in ("reload", "/reload"):
cfg = load_config()
_skills.reload()
return "🔄 Config and skills reloaded."
6.2 Implementation Tasks
- Update system prompt to mention self-modification capabilities
- Ensure tools are enabled (see section 2)
- Add
/reloadcommand toai_handler - Add workspace path to system prompt so agent knows where files are
7. Agent-to-Agent Communication
Can the Runtime Do This?
OpenCode: Has Task tool for spawning subagents. Subagents run in child sessions. No direct inter-session messaging.
Claude Code: Has Task, TaskOutput, TaskStop for subagent management, plus TeamCreate, TeamDelete, SendMessage for agent teams. SendMessage allows agents to communicate within a team.
How Inspiration Repos Do It
NanoClaw: Uses Claude Code's TeamCreate + SendMessage tools. The allowedTools list includes both. Agent teams allow multiple agents to work together with message passing.
OpenClaw: sessions_send tool allows one session to send a message to another session. Supports fire-and-forget or wait-for-reply modes. Visibility config controls which sessions can see each other.
Design for Aetheel
Aetheel already has SubagentManager for spawning background tasks. What's missing is the ability for subagents to communicate back to the main agent or to each other.
7.1 Approach: Leverage Runtime's Native Tools
Since both OpenCode and Claude Code have subagent/team tools built in, the simplest approach is to enable them:
For Claude Code, add to allowed_tools:
["Task", "TaskOutput", "TaskStop", "TeamCreate", "TeamDelete", "SendMessage"]
For OpenCode, these are enabled by default.
7.2 Aetheel-Level Inter-Subagent Messaging
For Aetheel's own SubagentManager, add a message bus:
class SubagentBus:
"""Simple pub/sub for subagent communication."""
def __init__(self):
self._channels: dict[str, list[Callable]] = {}
def subscribe(self, channel: str, callback: Callable):
self._channels.setdefault(channel, []).append(callback)
def publish(self, channel: str, message: str, sender: str):
for cb in self._channels.get(channel, []):
cb(message, sender)
Wire into SubagentManager so subagents can:
- Publish results to a channel
- Subscribe to messages from other subagents
- Send messages to the originating user via
_send_fn
7.3 Implementation Tasks
- Enable
TeamCreate,SendMessage,Tasktools in Claude Code config - Update system prompt to mention subagent capabilities
- Add
SubagentBustoagent/subagent.py - Wire bus into
SubagentManager.spawn()so subagents can communicate - Add
/subagentscommand to list active subagents and their status
8. Implementation Order
Ordered by dependency and impact:
Phase 3A: Enable What Already Exists (1-2 days)
- Tool enablement — Change
no_toolsdefault, update allowed tools, update system prompt - Self-modification — Just system prompt changes +
/reloadcommand - Agent-to-agent — Enable runtime tools + system prompt
Phase 3B: New Modules (3-5 days)
- CLI interface —
cli.pywith click, move main() logic - Heartbeat system —
heartbeat/module,HEARTBEAT.md, scheduler integration
Phase 3C: WebChat (3-5 days)
- WebChat adapter — aiohttp server, WebSocket handler, static HTML UI
Dependencies
Tool enablement ──→ Self-modification (needs tools enabled)
──→ Agent-to-agent (needs tools enabled)
──→ Heartbeat (agent needs tools for heartbeat tasks)
CLI ──→ WebChat (webchat flag in CLI)
Heartbeat ──→ (standalone, uses existing scheduler)
New Dependencies to Add
[project.dependencies]
# ... existing ...
click = ">=8.1.0"
aiohttp = ">=3.9.0"
New Files
Aetheel/
├── cli.py # CLI entry point
├── heartbeat/
│ ├── __init__.py
│ └── heartbeat.py # HeartbeatRunner
├── adapters/
│ └── webchat_adapter.py # WebChat adapter
├── static/
│ └── chat.html # WebChat UI
└── ~/.aetheel/workspace/
├── HEARTBEAT.md # Periodic task prompts
└── TOOLS.md # Tool descriptions for agent