2026-03-22 19:07:07 +00:00
|
|
|
//! Matrix bot integration for Story Kit.
|
|
|
|
|
//!
|
2026-04-03 16:12:52 +01:00
|
|
|
//! When a `.huskies/bot.toml` file is present with `enabled = true`, the
|
2026-03-22 19:07:07 +00:00
|
|
|
//! 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.
|
|
|
|
|
|
2026-03-24 15:03:17 +00:00
|
|
|
pub mod assign;
|
2026-03-22 19:07:07 +00:00
|
|
|
mod bot;
|
|
|
|
|
pub mod commands;
|
2026-03-25 14:43:28 +00:00
|
|
|
pub(crate) mod config;
|
2026-03-22 19:07:07 +00:00
|
|
|
pub mod delete;
|
|
|
|
|
pub mod htop;
|
2026-04-13 14:07:08 +00:00
|
|
|
pub mod notifications;
|
2026-03-22 19:07:07 +00:00
|
|
|
pub mod rebuild;
|
|
|
|
|
pub mod reset;
|
2026-03-24 15:00:08 +00:00
|
|
|
pub mod rmtree;
|
2026-03-22 19:07:07 +00:00
|
|
|
pub mod start;
|
|
|
|
|
pub mod transport_impl;
|
|
|
|
|
|
2026-03-25 14:43:28 +00:00
|
|
|
pub use bot::{ConversationEntry, ConversationRole, RoomConversation};
|
2026-03-22 19:07:07 +00:00
|
|
|
pub use config::BotConfig;
|
|
|
|
|
|
|
|
|
|
use crate::agents::AgentPool;
|
|
|
|
|
use crate::http::context::PermissionForward;
|
|
|
|
|
use crate::io::watcher::WatcherEvent;
|
2026-03-22 19:08:41 +00:00
|
|
|
use crate::rebuild::ShutdownReason;
|
2026-03-22 19:07:07 +00:00
|
|
|
use std::path::Path;
|
|
|
|
|
use std::sync::Arc;
|
2026-04-14 09:57:11 +00:00
|
|
|
use tokio::sync::{Mutex as TokioMutex, RwLock, broadcast, mpsc, watch};
|
2026-03-22 19:07:07 +00:00
|
|
|
|
|
|
|
|
/// Attempt to start the Matrix bot.
|
|
|
|
|
///
|
2026-04-03 16:12:52 +01:00
|
|
|
/// Reads the bot configuration from `.huskies/bot.toml`. If the file is
|
2026-03-22 19:07:07 +00:00
|
|
|
/// 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.
|
|
|
|
|
///
|
|
|
|
|
/// `perm_rx` is the permission-request receiver shared with the MCP
|
|
|
|
|
/// `prompt_permission` tool. The bot locks it during active chat sessions
|
|
|
|
|
/// to surface permission prompts to the Matrix room and relay user decisions.
|
|
|
|
|
///
|
2026-03-22 19:08:41 +00:00
|
|
|
/// `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.
|
|
|
|
|
///
|
2026-03-22 19:07:07 +00:00
|
|
|
/// Must be called from within a Tokio runtime context (e.g., from `main`).
|
2026-04-14 18:53:41 +00:00
|
|
|
///
|
|
|
|
|
/// 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.
|
2026-03-22 19:07:07 +00:00
|
|
|
pub fn spawn_bot(
|
|
|
|
|
project_root: &Path,
|
|
|
|
|
watcher_tx: broadcast::Sender<WatcherEvent>,
|
|
|
|
|
perm_rx: Arc<TokioMutex<mpsc::UnboundedReceiver<PermissionForward>>>,
|
|
|
|
|
agents: Arc<AgentPool>,
|
2026-03-22 19:08:41 +00:00
|
|
|
shutdown_rx: watch::Receiver<Option<ShutdownReason>>,
|
2026-04-14 09:57:11 +00:00
|
|
|
gateway_active_project: Option<Arc<RwLock<String>>>,
|
|
|
|
|
gateway_projects: Vec<String>,
|
2026-04-14 18:53:41 +00:00
|
|
|
) -> Option<tokio::task::AbortHandle> {
|
2026-03-22 19:07:07 +00:00
|
|
|
let config = match BotConfig::load(project_root) {
|
|
|
|
|
Some(c) => c,
|
|
|
|
|
None => {
|
|
|
|
|
crate::slog!("[matrix-bot] bot.toml absent or disabled; Matrix integration skipped");
|
2026-04-14 18:53:41 +00:00
|
|
|
return None;
|
2026-03-22 19:07:07 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
);
|
2026-04-14 18:53:41 +00:00
|
|
|
return None;
|
2026-03-22 19:07:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
crate::slog!(
|
|
|
|
|
"[matrix-bot] Starting Matrix bot → homeserver={} rooms={:?}",
|
2026-03-24 21:05:35 +00:00
|
|
|
config.homeserver.as_deref().unwrap_or("(none)"),
|
2026-03-22 19:07:07 +00:00
|
|
|
config.effective_room_ids()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let root = project_root.to_path_buf();
|
|
|
|
|
let watcher_rx = watcher_tx.subscribe();
|
2026-03-28 09:18:58 +00:00
|
|
|
let watcher_rx_auto = watcher_tx.subscribe();
|
2026-04-14 18:53:41 +00:00
|
|
|
let handle = tokio::spawn(async move {
|
2026-04-13 14:07:08 +00:00
|
|
|
if let Err(e) = bot::run_bot(
|
|
|
|
|
config,
|
|
|
|
|
root,
|
|
|
|
|
watcher_rx,
|
|
|
|
|
watcher_rx_auto,
|
|
|
|
|
perm_rx,
|
|
|
|
|
agents,
|
|
|
|
|
shutdown_rx,
|
2026-04-14 09:57:11 +00:00
|
|
|
gateway_active_project,
|
|
|
|
|
gateway_projects,
|
2026-04-13 14:07:08 +00:00
|
|
|
)
|
|
|
|
|
.await
|
2026-03-22 19:08:41 +00:00
|
|
|
{
|
2026-03-22 19:07:07 +00:00
|
|
|
crate::slog!("[matrix-bot] Fatal error: {e}");
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-04-14 18:53:41 +00:00
|
|
|
Some(handle.abort_handle())
|
2026-03-22 19:07:07 +00:00
|
|
|
}
|