//! Matrix bot integration for Story Kit. //! //! When a `.huskies/bot.toml` file is present with `enabled = true`, the //! server spawns a Matrix bot that: //! //! 1. Connects to the configured homeserver and joins the configured room. //! 2. Listens for messages from other users in the room. //! 3. Passes each message to Claude Code (the same provider as the web UI), //! which has native access to Story Kit MCP tools. //! 4. Posts Claude Code's response back to the room. //! //! The bot is optional — if `bot.toml` is missing or `enabled = false`, the //! server starts normally with no Matrix connection. //! //! Multi-room support: configure `room_ids = ["!room1:…", "!room2:…"]` in //! `bot.toml`. Each room maintains its own independent conversation history. /// Auto-assign handler — listens for pipeline events and assigns stories to free agents. pub mod assign; mod bot; /// Cleanup worktrees command — removes stale worktrees for completed or archived stories. pub mod cleanup_worktrees; /// Matrix bot command handlers — parses and routes bot commands from Matrix messages. pub mod commands; /// `compact` command — distills the session transcript into a seed and resets, Matrix-specific glue. pub mod compact; pub(crate) mod config; /// Story deletion command — handles `!delete` bot commands to remove work items. pub mod delete; /// `health` chat command — surface gateway, sled, matrix, creds, and build-hash status. pub mod health; /// htop-style agent monitor command — renders a live process table in Matrix. pub mod htop; /// `new project ` chat command — Phase 1 gateway project bootstrap. pub mod new_project; /// `project-rebuild ` chat command — rebuild Docker image, swap container, preserve state. pub mod project_rebuild; /// `projects` chat command — list all registered gateway projects. pub mod projects; /// `pull ` gateway chat command — fetch, verify, and install a /// signed release-channel artifact into the gateway's artifact store. pub mod pull; /// `rebuild gateway` command parsing (gateway self-rebuild). pub mod rebuild; /// `release` gateway chat command — build the sled binary and publish it. pub mod release; /// Reset command — handles `!reset` bot commands to restart the server state. pub mod reset; /// rmtree command — handles `!rmtree` bot commands to remove worktrees. pub mod rmtree; /// `upgrade []` gateway chat command — streaming per-sled binary upgrade. pub mod sled_upgrade; /// Start command — handles `!start` bot commands to launch agents on stories. pub mod start; /// Stop/halt/abort command — cancels the in-flight LLM turn for this room /// and clears any coalesced/pending messages. pub mod stop; /// Matrix `ChatTransport` implementation wrapping the Matrix SDK client. pub mod transport_impl; pub use bot::{ConversationEntry, ConversationRole, RoomConversation}; pub use config::BotConfig; use crate::io::watcher::WatcherEvent; use crate::rebuild::ShutdownReason; use crate::service::timer::TimerStore; use crate::services::Services; use std::path::Path; use std::sync::Arc; use tokio::sync::{RwLock, broadcast, watch}; /// Attempt to start the Matrix bot. /// /// Reads the bot configuration from `.huskies/bot.toml`. If the file is /// absent or `enabled = false`, this function returns immediately without /// spawning anything — the server continues normally. /// /// When the bot is enabled, a notification listener is also spawned that /// posts stage-transition messages to all configured rooms whenever a work /// item moves between pipeline stages. /// /// `services` is the shared services bundle containing the agent pool, /// permission plumbing, and bot identity. The bot accesses these via /// `Arc` rather than holding its own copies. /// /// `shutdown_rx` is a watch channel that delivers a `ShutdownReason` when the /// server is about to stop (SIGINT/SIGTERM or rebuild). The bot uses this to /// announce the shutdown to all configured rooms before the process exits. /// /// Must be called from within a Tokio runtime context (e.g., from `main`). /// /// Returns an [`tokio::task::AbortHandle`] if the bot was actually spawned (Matrix/Discord /// transports), or `None` if the config is absent, disabled, or uses a webhook-based /// transport (Slack/WhatsApp) that does not require a persistent background task. #[allow(clippy::too_many_arguments)] pub fn spawn_bot( project_root: &Path, watcher_tx: broadcast::Sender, services: Arc, shutdown_rx: watch::Receiver>, gateway_active_project: Option>>, gateway_projects_store: Option< Arc< RwLock< std::collections::BTreeMap, >, >, >, timer_store: Arc, gateway_event_rx: Option< tokio::sync::broadcast::Receiver, >, gateway_port: Option, gateway_channels_store: Option< Arc< RwLock< std::collections::BTreeMap< String, crate::service::gateway::config::ReleaseChannelConfig, >, >, >, >, ) -> Option { let config = match BotConfig::load(project_root) { Some(c) => c, None => { crate::slog!("[matrix-bot] bot.toml absent or disabled; Matrix integration skipped"); return None; } }; // WhatsApp and Slack transports are handled via HTTP webhooks, not the Matrix sync loop. if config.transport == "whatsapp" || config.transport == "slack" { crate::slog!( "[bot] transport={} — skipping Matrix bot; webhooks handle this transport", config.transport ); return None; } crate::slog!( "[matrix-bot] Starting Matrix bot → homeserver={} rooms={:?}", config.homeserver.as_deref().unwrap_or("(none)"), config.effective_room_ids() ); let watcher_rx = watcher_tx.subscribe(); let watcher_rx_auto = watcher_tx.subscribe(); let handle = tokio::spawn(async move { if let Err(e) = bot::run_bot( config, services, watcher_rx, watcher_rx_auto, watcher_tx, shutdown_rx, gateway_active_project, gateway_projects_store, timer_store, gateway_event_rx, gateway_port, gateway_channels_store, ) .await { crate::slog!("[matrix-bot] Fatal error: {e}"); } }); Some(handle.abort_handle()) }