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:
@@ -59,6 +59,39 @@ SendFunction = Callable[[str, str, str | None, str], None]
|
||||
# send_fn(channel_id, text, thread_id, channel_type)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Subagent Bus (pub/sub for inter-subagent communication)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class SubagentBus:
|
||||
"""Simple pub/sub message bus for inter-subagent communication."""
|
||||
|
||||
def __init__(self):
|
||||
self._channels: dict[str, list[Callable]] = {}
|
||||
self._lock = threading.Lock()
|
||||
|
||||
def subscribe(self, channel: str, callback: Callable[[str, str], None]) -> None:
|
||||
"""Register a callback for messages on a channel."""
|
||||
with self._lock:
|
||||
self._channels.setdefault(channel, []).append(callback)
|
||||
|
||||
def publish(self, channel: str, message: str, sender: str) -> None:
|
||||
"""Publish a message to all subscribers of a channel."""
|
||||
with self._lock:
|
||||
callbacks = list(self._channels.get(channel, []))
|
||||
for cb in callbacks:
|
||||
try:
|
||||
cb(message, sender)
|
||||
except Exception as e:
|
||||
logger.error(f"SubagentBus callback error: {e}")
|
||||
|
||||
def unsubscribe_all(self, channel: str) -> None:
|
||||
"""Remove all subscribers from a channel."""
|
||||
with self._lock:
|
||||
self._channels.pop(channel, None)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Subagent Manager
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -84,6 +117,12 @@ class SubagentManager:
|
||||
self._max_concurrent = max_concurrent
|
||||
self._tasks: dict[str, SubagentTask] = {}
|
||||
self._lock = threading.Lock()
|
||||
self._bus = SubagentBus()
|
||||
|
||||
@property
|
||||
def bus(self) -> SubagentBus:
|
||||
"""The pub/sub message bus for inter-subagent communication."""
|
||||
return self._bus
|
||||
|
||||
def spawn(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user