Files
huskies/server/src/services.rs
T

68 lines
3.2 KiB
Rust

//! Shared services bundle — common state threaded through HTTP handlers and chat transports.
//!
//! `Services` bundles the fields that every transport (Matrix, Slack, Discord,
//! WhatsApp) and the HTTP/MCP layer need. A single `Arc<Services>` is
//! constructed once in `main.rs` and cloned into `AppContext` and each
//! transport's context struct.
use crate::agents::AgentPool;
use crate::http::context::{PermissionDecision, PermissionForward};
use crate::service::status::StatusBroadcaster;
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::{Mutex as TokioMutex, mpsc, oneshot};
/// Shared state bundle constructed once at startup and cloned (via `Arc`) into
/// every context that needs access to the project root, agent pool, bot
/// identity, ambient-room set, or permission plumbing.
pub struct Services {
/// Absolute path to the project root directory.
pub project_root: PathBuf,
/// Agent pool for starting, stopping, and querying coding agents.
pub agents: Arc<AgentPool>,
/// Display name the bot uses to identify itself (from `bot.toml`).
pub bot_name: String,
/// String representation of the bot's user ID (e.g. `"@timmy:hs.local"`
/// for Matrix, `"slack-bot"` for Slack).
pub bot_user_id: String,
/// Set of room/channel IDs where ambient mode is active.
pub ambient_rooms: Arc<std::sync::Mutex<HashSet<String>>>,
/// Receiver for permission requests from the MCP `prompt_permission` tool.
pub perm_rx: Arc<TokioMutex<mpsc::UnboundedReceiver<PermissionForward>>>,
/// Per-room pending permission reply senders, keyed by room/channel ID
/// as a plain string.
pub pending_perm_replies: Arc<TokioMutex<HashMap<String, oneshot::Sender<PermissionDecision>>>>,
/// Seconds to wait for a user to respond to a permission prompt before
/// auto-denying (fail-closed).
pub permission_timeout_secs: u64,
/// Project-scoped status broadcaster.
///
/// Consumers (chat transports, Web UI, agent context) call
/// [`StatusBroadcaster::subscribe`] to receive pipeline status events.
/// The broadcaster is project-scoped: events published here are delivered
/// only to subscribers of this instance, providing natural multi-project
/// isolation.
pub status: Arc<StatusBroadcaster>,
}
#[cfg(test)]
impl Services {
/// Build a minimal `Services` for testing with the given project root and
/// bot display name.
pub fn new_test(project_root: std::path::PathBuf, bot_name: String) -> std::sync::Arc<Self> {
let (_perm_tx, perm_rx) = mpsc::unbounded_channel();
std::sync::Arc::new(Self {
project_root,
agents: std::sync::Arc::new(crate::agents::AgentPool::new_test(3000)),
bot_name,
bot_user_id: String::new(),
ambient_rooms: std::sync::Arc::new(std::sync::Mutex::new(HashSet::new())),
perm_rx: std::sync::Arc::new(TokioMutex::new(perm_rx)),
pending_perm_replies: std::sync::Arc::new(TokioMutex::new(HashMap::new())),
permission_timeout_secs: 120,
status: std::sync::Arc::new(StatusBroadcaster::new()),
})
}
}