storkit: merge 383_refactor_reorganize_chat_system_into_chat_module_with_transport_submodules
This commit is contained in:
@@ -4,6 +4,8 @@
|
||||
//! sending and editing messages, allowing the bot logic (commands, htop,
|
||||
//! notifications) to work against any chat platform — Matrix, WhatsApp, etc.
|
||||
|
||||
pub mod transport;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
/// A platform-agnostic identifier for a sent message.
|
||||
@@ -65,11 +67,11 @@ mod tests {
|
||||
#[test]
|
||||
fn whatsapp_transport_satisfies_trait() {
|
||||
fn assert_transport<T: ChatTransport>() {}
|
||||
assert_transport::<crate::whatsapp::WhatsAppTransport>();
|
||||
assert_transport::<crate::chat::transport::whatsapp::WhatsAppTransport>();
|
||||
|
||||
// Verify it can be wrapped in Arc<dyn ChatTransport>.
|
||||
let _: Arc<dyn ChatTransport> =
|
||||
Arc::new(crate::whatsapp::WhatsAppTransport::new(
|
||||
Arc::new(crate::chat::transport::whatsapp::WhatsAppTransport::new(
|
||||
"test-phone".to_string(),
|
||||
"test-token".to_string(),
|
||||
"pipeline_notification".to_string(),
|
||||
@@ -81,7 +83,7 @@ mod tests {
|
||||
#[test]
|
||||
fn matrix_transport_is_send_sync() {
|
||||
fn assert_send_sync<T: Send + Sync>() {}
|
||||
assert_send_sync::<crate::matrix::transport_impl::MatrixTransport>();
|
||||
assert_send_sync::<crate::chat::transport::matrix::transport_impl::MatrixTransport>();
|
||||
}
|
||||
|
||||
/// Verify that SlackTransport satisfies the ChatTransport trait and
|
||||
@@ -89,10 +91,10 @@ mod tests {
|
||||
#[test]
|
||||
fn slack_transport_satisfies_trait() {
|
||||
fn assert_transport<T: ChatTransport>() {}
|
||||
assert_transport::<crate::slack::SlackTransport>();
|
||||
assert_transport::<crate::chat::transport::slack::SlackTransport>();
|
||||
|
||||
let _: Arc<dyn ChatTransport> =
|
||||
Arc::new(crate::slack::SlackTransport::new("xoxb-test".to_string()));
|
||||
Arc::new(crate::chat::transport::slack::SlackTransport::new("xoxb-test".to_string()));
|
||||
}
|
||||
|
||||
/// Verify that TwilioWhatsAppTransport satisfies the ChatTransport trait
|
||||
@@ -100,10 +102,10 @@ mod tests {
|
||||
#[test]
|
||||
fn twilio_transport_satisfies_trait() {
|
||||
fn assert_transport<T: ChatTransport>() {}
|
||||
assert_transport::<crate::whatsapp::TwilioWhatsAppTransport>();
|
||||
assert_transport::<crate::chat::transport::whatsapp::TwilioWhatsAppTransport>();
|
||||
|
||||
let _: Arc<dyn ChatTransport> =
|
||||
Arc::new(crate::whatsapp::TwilioWhatsAppTransport::new(
|
||||
Arc::new(crate::chat::transport::whatsapp::TwilioWhatsAppTransport::new(
|
||||
"ACtest".to_string(),
|
||||
"authtoken".to_string(),
|
||||
"+14155551234".to_string(),
|
||||
@@ -2,7 +2,7 @@ use crate::agents::AgentPool;
|
||||
use crate::http::context::{PermissionDecision, PermissionForward};
|
||||
use crate::llm::providers::claude_code::{ClaudeCodeProvider, ClaudeCodeResult};
|
||||
use crate::slog;
|
||||
use crate::transport::ChatTransport;
|
||||
use crate::chat::ChatTransport;
|
||||
use matrix_sdk::{
|
||||
Client,
|
||||
config::SyncSettings,
|
||||
@@ -371,14 +371,14 @@ pub async fn run_bot(
|
||||
"whatsapp" => {
|
||||
if config.whatsapp_provider == "twilio" {
|
||||
slog!("[matrix-bot] Using WhatsApp/Twilio transport");
|
||||
Arc::new(crate::whatsapp::TwilioWhatsAppTransport::new(
|
||||
Arc::new(crate::chat::transport::whatsapp::TwilioWhatsAppTransport::new(
|
||||
config.twilio_account_sid.clone().unwrap_or_default(),
|
||||
config.twilio_auth_token.clone().unwrap_or_default(),
|
||||
config.twilio_whatsapp_number.clone().unwrap_or_default(),
|
||||
))
|
||||
} else {
|
||||
slog!("[matrix-bot] Using WhatsApp/Meta transport");
|
||||
Arc::new(crate::whatsapp::WhatsAppTransport::new(
|
||||
Arc::new(crate::chat::transport::whatsapp::WhatsAppTransport::new(
|
||||
config.whatsapp_phone_number_id.clone().unwrap_or_default(),
|
||||
config.whatsapp_access_token.clone().unwrap_or_default(),
|
||||
config
|
||||
@@ -1612,7 +1612,7 @@ mod tests {
|
||||
ambient_rooms: Arc::new(std::sync::Mutex::new(HashSet::new())),
|
||||
agents: Arc::new(AgentPool::new_test(3000)),
|
||||
htop_sessions: Arc::new(TokioMutex::new(HashMap::new())),
|
||||
transport: Arc::new(crate::whatsapp::WhatsAppTransport::new("test-phone".to_string(), "test-token".to_string(), "pipeline_notification".to_string())),
|
||||
transport: Arc::new(crate::chat::transport::whatsapp::WhatsAppTransport::new("test-phone".to_string(), "test-token".to_string(), "pipeline_notification".to_string())),
|
||||
};
|
||||
// Clone must work (required by Matrix SDK event handler injection).
|
||||
let _cloned = ctx.clone();
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Handler for the `ambient` command.
|
||||
|
||||
use super::CommandContext;
|
||||
use crate::matrix::config::save_ambient_rooms;
|
||||
use crate::chat::transport::matrix::config::save_ambient_rooms;
|
||||
|
||||
/// Toggle ambient mode for this room.
|
||||
///
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Handler stub for the `assign` command.
|
||||
//!
|
||||
//! The real implementation lives in `crate::matrix::assign` (async). This
|
||||
//! The real implementation lives in `crate::chat::transport::matrix::assign` (async). This
|
||||
//! stub exists only so that `assign` appears in the help registry — the
|
||||
//! handler always returns `None` so the bot's message loop falls through to
|
||||
//! the async handler in `bot.rs`.
|
||||
@@ -8,7 +8,7 @@
|
||||
use super::CommandContext;
|
||||
|
||||
pub(super) fn handle_assign(_ctx: &CommandContext) -> Option<String> {
|
||||
// Handled asynchronously in bot.rs / crate::matrix::assign.
|
||||
// Handled asynchronously in bot.rs / crate::chat::transport::matrix::assign.
|
||||
None
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ pub struct BotCommand {
|
||||
/// message body.
|
||||
///
|
||||
/// All identifiers are platform-agnostic strings so this struct works with
|
||||
/// any [`ChatTransport`](crate::transport::ChatTransport) implementation.
|
||||
/// any [`ChatTransport`](crate::chat::ChatTransport) implementation.
|
||||
pub struct CommandDispatch<'a> {
|
||||
/// The bot's display name (e.g., "Timmy").
|
||||
pub bot_name: &'a str,
|
||||
@@ -14,7 +14,7 @@ use tokio::sync::{Mutex as TokioMutex, watch};
|
||||
|
||||
use crate::agents::{AgentPool, AgentStatus};
|
||||
use crate::slog;
|
||||
use crate::transport::ChatTransport;
|
||||
use crate::chat::ChatTransport;
|
||||
|
||||
use super::bot::markdown_to_html;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
use crate::io::story_metadata::parse_front_matter;
|
||||
use crate::io::watcher::WatcherEvent;
|
||||
use crate::slog;
|
||||
use crate::transport::ChatTransport;
|
||||
use crate::chat::ChatTransport;
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
@@ -265,7 +265,7 @@ pub fn spawn_notification_listener(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use async_trait::async_trait;
|
||||
use crate::transport::MessageId;
|
||||
use crate::chat::MessageId;
|
||||
|
||||
// ── MockTransport ───────────────────────────────────────────────────────
|
||||
|
||||
@@ -284,7 +284,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::transport::ChatTransport for MockTransport {
|
||||
impl crate::chat::ChatTransport for MockTransport {
|
||||
async fn send_message(&self, room_id: &str, plain: &str, html: &str) -> Result<MessageId, String> {
|
||||
self.calls.lock().unwrap().push((room_id.to_string(), plain.to_string(), html.to_string()));
|
||||
Ok("mock-msg-id".to_string())
|
||||
@@ -5,7 +5,7 @@
|
||||
//! with clean context. File-system memories (auto-memory directory) are not
|
||||
//! affected — only the in-memory/persisted conversation state is cleared.
|
||||
|
||||
use crate::matrix::bot::{ConversationHistory, RoomConversation};
|
||||
use crate::chat::transport::matrix::bot::{ConversationHistory, RoomConversation};
|
||||
use matrix_sdk::ruma::OwnedRoomId;
|
||||
use std::path::Path;
|
||||
|
||||
@@ -52,7 +52,7 @@ pub async fn handle_reset(
|
||||
let conv = guard.entry(room_id.clone()).or_insert_with(RoomConversation::default);
|
||||
conv.session_id = None;
|
||||
conv.entries.clear();
|
||||
crate::matrix::bot::save_history(project_root, &guard);
|
||||
crate::chat::transport::matrix::bot::save_history(project_root, &guard);
|
||||
}
|
||||
crate::slog!("[matrix-bot] reset command: cleared session for room {room_id} (bot={bot_name})");
|
||||
"Session reset. Starting fresh — previous context has been cleared.".to_string()
|
||||
@@ -138,7 +138,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn handle_reset_clears_session_and_entries() {
|
||||
use crate::matrix::bot::{ConversationEntry, ConversationRole};
|
||||
use crate::chat::transport::matrix::bot::{ConversationEntry, ConversationRole};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex as TokioMutex;
|
||||
@@ -356,14 +356,14 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn start_command_is_registered() {
|
||||
use crate::matrix::commands::commands;
|
||||
use crate::chat::transport::matrix::commands::commands;
|
||||
let found = commands().iter().any(|c| c.name == "start");
|
||||
assert!(found, "start command must be in the registry");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn start_command_appears_in_help() {
|
||||
let result = crate::matrix::commands::tests::try_cmd_addressed(
|
||||
let result = crate::chat::transport::matrix::commands::tests::try_cmd_addressed(
|
||||
"Timmy",
|
||||
"@timmy:homeserver.local",
|
||||
"@timmy help",
|
||||
@@ -378,7 +378,7 @@ mod tests {
|
||||
#[test]
|
||||
fn start_command_falls_through_to_none_in_registry() {
|
||||
// The start handler in the registry returns None (handled async in bot.rs).
|
||||
let result = crate::matrix::commands::tests::try_cmd_addressed(
|
||||
let result = crate::chat::transport::matrix::commands::tests::try_cmd_addressed(
|
||||
"Timmy",
|
||||
"@timmy:homeserver.local",
|
||||
"@timmy start 42",
|
||||
@@ -10,7 +10,7 @@ use matrix_sdk::ruma::events::room::message::{
|
||||
ReplacementMetadata, RoomMessageEventContent, RoomMessageEventContentWithoutRelation,
|
||||
};
|
||||
|
||||
use crate::transport::{ChatTransport, MessageId};
|
||||
use crate::chat::{ChatTransport, MessageId};
|
||||
|
||||
/// Matrix-backed [`ChatTransport`] implementation.
|
||||
///
|
||||
3
server/src/chat/transport/mod.rs
Normal file
3
server/src/chat/transport/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod matrix;
|
||||
pub mod slack;
|
||||
pub mod whatsapp;
|
||||
@@ -14,9 +14,9 @@ use std::sync::Arc;
|
||||
use tokio::sync::Mutex as TokioMutex;
|
||||
|
||||
use crate::agents::AgentPool;
|
||||
use crate::matrix::{ConversationEntry, ConversationRole, RoomConversation};
|
||||
use crate::chat::transport::matrix::{ConversationEntry, ConversationRole, RoomConversation};
|
||||
use crate::slog;
|
||||
use crate::transport::{ChatTransport, MessageId};
|
||||
use crate::chat::{ChatTransport, MessageId};
|
||||
|
||||
// ── Slack API base URL (overridable for tests) ──────────────────────────
|
||||
|
||||
@@ -669,7 +669,7 @@ pub async fn slash_command_receive(
|
||||
format!("{} {keyword} {}", ctx.bot_name, payload.text)
|
||||
};
|
||||
|
||||
use crate::matrix::commands::{CommandDispatch, try_handle_command};
|
||||
use crate::chat::transport::matrix::commands::{CommandDispatch, try_handle_command};
|
||||
|
||||
let dispatch = CommandDispatch {
|
||||
bot_name: &ctx.bot_name,
|
||||
@@ -701,7 +701,7 @@ async fn handle_incoming_message(
|
||||
user: &str,
|
||||
message: &str,
|
||||
) {
|
||||
use crate::matrix::commands::{CommandDispatch, try_handle_command};
|
||||
use crate::chat::transport::matrix::commands::{CommandDispatch, try_handle_command};
|
||||
|
||||
let dispatch = CommandDispatch {
|
||||
bot_name: &ctx.bot_name,
|
||||
@@ -721,12 +721,12 @@ async fn handle_incoming_message(
|
||||
}
|
||||
|
||||
// Check for async commands (htop, delete).
|
||||
if let Some(htop_cmd) = crate::matrix::htop::extract_htop_command(
|
||||
if let Some(htop_cmd) = crate::chat::transport::matrix::htop::extract_htop_command(
|
||||
message,
|
||||
&ctx.bot_name,
|
||||
&ctx.bot_user_id,
|
||||
) {
|
||||
use crate::matrix::htop::HtopCommand;
|
||||
use crate::chat::transport::matrix::htop::HtopCommand;
|
||||
slog!("[slack] Handling htop command from {user} in {channel}");
|
||||
match htop_cmd {
|
||||
HtopCommand::Stop => {
|
||||
@@ -738,7 +738,7 @@ async fn handle_incoming_message(
|
||||
HtopCommand::Start { duration_secs } => {
|
||||
// On Slack, htop uses native message editing for live updates.
|
||||
let snapshot =
|
||||
crate::matrix::htop::build_htop_message(&ctx.agents, 0, duration_secs);
|
||||
crate::chat::transport::matrix::htop::build_htop_message(&ctx.agents, 0, duration_secs);
|
||||
let msg_id = match ctx.transport.send_message(channel, &snapshot, "").await {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
@@ -755,7 +755,7 @@ async fn handle_incoming_message(
|
||||
let total_ticks = (duration_secs as usize) / 2;
|
||||
for tick in 1..=total_ticks {
|
||||
tokio::time::sleep(interval).await;
|
||||
let updated = crate::matrix::htop::build_htop_message(
|
||||
let updated = crate::chat::transport::matrix::htop::build_htop_message(
|
||||
&agents,
|
||||
(tick * 2) as u32,
|
||||
duration_secs,
|
||||
@@ -773,15 +773,15 @@ async fn handle_incoming_message(
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(del_cmd) = crate::matrix::delete::extract_delete_command(
|
||||
if let Some(del_cmd) = crate::chat::transport::matrix::delete::extract_delete_command(
|
||||
message,
|
||||
&ctx.bot_name,
|
||||
&ctx.bot_user_id,
|
||||
) {
|
||||
let response = match del_cmd {
|
||||
crate::matrix::delete::DeleteCommand::Delete { story_number } => {
|
||||
crate::chat::transport::matrix::delete::DeleteCommand::Delete { story_number } => {
|
||||
slog!("[slack] Handling delete command from {user}: story {story_number}");
|
||||
crate::matrix::delete::handle_delete(
|
||||
crate::chat::transport::matrix::delete::handle_delete(
|
||||
&ctx.bot_name,
|
||||
&story_number,
|
||||
&ctx.project_root,
|
||||
@@ -789,7 +789,7 @@ async fn handle_incoming_message(
|
||||
)
|
||||
.await
|
||||
}
|
||||
crate::matrix::delete::DeleteCommand::BadArgs => {
|
||||
crate::chat::transport::matrix::delete::DeleteCommand::BadArgs => {
|
||||
format!("Usage: `{} delete <number>`", ctx.bot_name)
|
||||
}
|
||||
};
|
||||
@@ -810,7 +810,7 @@ async fn handle_llm_message(
|
||||
user_message: &str,
|
||||
) {
|
||||
use crate::llm::providers::claude_code::{ClaudeCodeProvider, ClaudeCodeResult};
|
||||
use crate::matrix::drain_complete_paragraphs;
|
||||
use crate::chat::transport::matrix::drain_complete_paragraphs;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use tokio::sync::watch;
|
||||
|
||||
@@ -1408,7 +1408,7 @@ mod tests {
|
||||
fn slash_command_dispatches_through_command_registry() {
|
||||
// Verify that the synthetic message built by the slash handler
|
||||
// correctly dispatches through try_handle_command.
|
||||
use crate::matrix::commands::{CommandDispatch, try_handle_command};
|
||||
use crate::chat::transport::matrix::commands::{CommandDispatch, try_handle_command};
|
||||
|
||||
let agents = test_agents();
|
||||
let ambient_rooms = test_ambient_rooms();
|
||||
@@ -1435,7 +1435,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn slash_command_show_passes_args_through_registry() {
|
||||
use crate::matrix::commands::{CommandDispatch, try_handle_command};
|
||||
use crate::chat::transport::matrix::commands::{CommandDispatch, try_handle_command};
|
||||
|
||||
let agents = test_agents();
|
||||
let ambient_rooms = test_ambient_rooms();
|
||||
@@ -14,9 +14,9 @@ use std::sync::Arc;
|
||||
use tokio::sync::Mutex as TokioMutex;
|
||||
|
||||
use crate::agents::AgentPool;
|
||||
use crate::matrix::{ConversationEntry, ConversationRole, RoomConversation};
|
||||
use crate::chat::transport::matrix::{ConversationEntry, ConversationRole, RoomConversation};
|
||||
use crate::slog;
|
||||
use crate::transport::{ChatTransport, MessageId};
|
||||
use crate::chat::{ChatTransport, MessageId};
|
||||
|
||||
// ── API base URLs (overridable for tests) ────────────────────────────────
|
||||
|
||||
@@ -908,7 +908,7 @@ async fn handle_incoming_message(
|
||||
sender: &str,
|
||||
message: &str,
|
||||
) {
|
||||
use crate::matrix::commands::{CommandDispatch, try_handle_command};
|
||||
use crate::chat::transport::matrix::commands::{CommandDispatch, try_handle_command};
|
||||
|
||||
// Record this inbound message to keep the 24-hour window open.
|
||||
ctx.window_tracker.record_message(sender);
|
||||
@@ -931,12 +931,12 @@ async fn handle_incoming_message(
|
||||
}
|
||||
|
||||
// Check for async commands (htop, delete).
|
||||
if let Some(htop_cmd) = crate::matrix::htop::extract_htop_command(
|
||||
if let Some(htop_cmd) = crate::chat::transport::matrix::htop::extract_htop_command(
|
||||
message,
|
||||
&ctx.bot_name,
|
||||
&ctx.bot_user_id,
|
||||
) {
|
||||
use crate::matrix::htop::HtopCommand;
|
||||
use crate::chat::transport::matrix::htop::HtopCommand;
|
||||
slog!("[whatsapp] Handling htop command from {sender}");
|
||||
match htop_cmd {
|
||||
HtopCommand::Stop => {
|
||||
@@ -951,7 +951,7 @@ async fn handle_incoming_message(
|
||||
// On WhatsApp, send a single snapshot instead of a live-updating
|
||||
// dashboard since we can't edit messages.
|
||||
let snapshot =
|
||||
crate::matrix::htop::build_htop_message(&ctx.agents, 0, duration_secs);
|
||||
crate::chat::transport::matrix::htop::build_htop_message(&ctx.agents, 0, duration_secs);
|
||||
let _ = ctx
|
||||
.transport
|
||||
.send_message(sender, &snapshot, "")
|
||||
@@ -961,15 +961,15 @@ async fn handle_incoming_message(
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(del_cmd) = crate::matrix::delete::extract_delete_command(
|
||||
if let Some(del_cmd) = crate::chat::transport::matrix::delete::extract_delete_command(
|
||||
message,
|
||||
&ctx.bot_name,
|
||||
&ctx.bot_user_id,
|
||||
) {
|
||||
let response = match del_cmd {
|
||||
crate::matrix::delete::DeleteCommand::Delete { story_number } => {
|
||||
crate::chat::transport::matrix::delete::DeleteCommand::Delete { story_number } => {
|
||||
slog!("[whatsapp] Handling delete command from {sender}: story {story_number}");
|
||||
crate::matrix::delete::handle_delete(
|
||||
crate::chat::transport::matrix::delete::handle_delete(
|
||||
&ctx.bot_name,
|
||||
&story_number,
|
||||
&ctx.project_root,
|
||||
@@ -977,7 +977,7 @@ async fn handle_incoming_message(
|
||||
)
|
||||
.await
|
||||
}
|
||||
crate::matrix::delete::DeleteCommand::BadArgs => {
|
||||
crate::chat::transport::matrix::delete::DeleteCommand::BadArgs => {
|
||||
format!("Usage: `{} delete <number>`", ctx.bot_name)
|
||||
}
|
||||
};
|
||||
@@ -997,7 +997,7 @@ async fn handle_llm_message(
|
||||
user_message: &str,
|
||||
) {
|
||||
use crate::llm::providers::claude_code::{ClaudeCodeProvider, ClaudeCodeResult};
|
||||
use crate::matrix::drain_complete_paragraphs;
|
||||
use crate::chat::transport::matrix::drain_complete_paragraphs;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use tokio::sync::watch;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
//! (it clears local session state and message history) and is not routed here.
|
||||
|
||||
use crate::http::context::{AppContext, OpenApiResult};
|
||||
use crate::matrix::commands::CommandDispatch;
|
||||
use crate::chat::transport::matrix::commands::CommandDispatch;
|
||||
use poem::http::StatusCode;
|
||||
use poem_openapi::{Object, OpenApi, Tags, payload::Json};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -112,7 +112,7 @@ fn dispatch_sync(
|
||||
format!("{bot_name} {cmd} {args}")
|
||||
};
|
||||
|
||||
match crate::matrix::commands::try_handle_command(&dispatch, &synthetic) {
|
||||
match crate::chat::transport::matrix::commands::try_handle_command(&dispatch, &synthetic) {
|
||||
Some(response) => response,
|
||||
None => {
|
||||
// Command exists in the registry but its fallback handler returns None
|
||||
@@ -138,7 +138,7 @@ async fn dispatch_assign(
|
||||
return "Usage: `/assign <number> <model>` (e.g. `/assign 42 opus`)".to_string();
|
||||
}
|
||||
|
||||
crate::matrix::assign::handle_assign("web-ui", number_str, model_str, project_root, agents)
|
||||
crate::chat::transport::matrix::assign::handle_assign("web-ui", number_str, model_str, project_root, agents)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ async fn dispatch_start(
|
||||
Some(hint_str)
|
||||
};
|
||||
|
||||
crate::matrix::start::handle_start("web-ui", number_str, agent_hint, project_root, agents)
|
||||
crate::chat::transport::matrix::start::handle_start("web-ui", number_str, agent_hint, project_root, agents)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -176,14 +176,14 @@ async fn dispatch_delete(
|
||||
if number_str.is_empty() || !number_str.chars().all(|c| c.is_ascii_digit()) {
|
||||
return "Usage: `/delete <number>` (e.g. `/delete 42`)".to_string();
|
||||
}
|
||||
crate::matrix::delete::handle_delete("web-ui", number_str, project_root, agents).await
|
||||
crate::chat::transport::matrix::delete::handle_delete("web-ui", number_str, project_root, agents).await
|
||||
}
|
||||
|
||||
async fn dispatch_rebuild(
|
||||
project_root: &std::path::Path,
|
||||
agents: &Arc<crate::agents::AgentPool>,
|
||||
) -> String {
|
||||
crate::matrix::rebuild::handle_rebuild("web-ui", project_root, agents).await
|
||||
crate::chat::transport::matrix::rebuild::handle_rebuild("web-ui", project_root, agents).await
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -31,8 +31,8 @@ use settings::SettingsApi;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::slack::SlackWebhookContext;
|
||||
use crate::whatsapp::WhatsAppWebhookContext;
|
||||
use crate::chat::transport::slack::SlackWebhookContext;
|
||||
use crate::chat::transport::whatsapp::WhatsAppWebhookContext;
|
||||
|
||||
const DEFAULT_PORT: u16 = 3001;
|
||||
|
||||
@@ -85,8 +85,8 @@ pub fn build_routes(
|
||||
if let Some(wa_ctx) = whatsapp_ctx {
|
||||
route = route.at(
|
||||
"/webhook/whatsapp",
|
||||
get(crate::whatsapp::webhook_verify)
|
||||
.post(crate::whatsapp::webhook_receive)
|
||||
get(crate::chat::transport::whatsapp::webhook_verify)
|
||||
.post(crate::chat::transport::whatsapp::webhook_receive)
|
||||
.data(wa_ctx),
|
||||
);
|
||||
}
|
||||
@@ -95,11 +95,11 @@ pub fn build_routes(
|
||||
route = route
|
||||
.at(
|
||||
"/webhook/slack",
|
||||
post(crate::slack::webhook_receive).data(sl_ctx.clone()),
|
||||
post(crate::chat::transport::slack::webhook_receive).data(sl_ctx.clone()),
|
||||
)
|
||||
.at(
|
||||
"/webhook/slack/command",
|
||||
post(crate::slack::slash_command_receive).data(sl_ctx),
|
||||
post(crate::chat::transport::slack::slash_command_receive).data(sl_ctx),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,13 +9,10 @@ mod http;
|
||||
mod io;
|
||||
mod llm;
|
||||
pub mod log_buffer;
|
||||
mod matrix;
|
||||
mod chat;
|
||||
pub mod rebuild;
|
||||
pub mod slack;
|
||||
mod state;
|
||||
mod store;
|
||||
pub mod transport;
|
||||
pub mod whatsapp;
|
||||
mod workflow;
|
||||
mod worktree;
|
||||
|
||||
@@ -267,15 +264,15 @@ async fn main() -> Result<(), std::io::Error> {
|
||||
let agents_for_shutdown = Arc::clone(&agents);
|
||||
|
||||
// Build WhatsApp webhook context if bot.toml configures transport = "whatsapp".
|
||||
let whatsapp_ctx: Option<Arc<whatsapp::WhatsAppWebhookContext>> = startup_root
|
||||
let whatsapp_ctx: Option<Arc<chat::transport::whatsapp::WhatsAppWebhookContext>> = startup_root
|
||||
.as_ref()
|
||||
.and_then(|root| matrix::BotConfig::load(root))
|
||||
.and_then(|root| chat::transport::matrix::BotConfig::load(root))
|
||||
.filter(|cfg| cfg.transport == "whatsapp")
|
||||
.map(|cfg| {
|
||||
let provider = cfg.whatsapp_provider.clone();
|
||||
let transport: Arc<dyn crate::transport::ChatTransport> =
|
||||
let transport: Arc<dyn crate::chat::ChatTransport> =
|
||||
if provider == "twilio" {
|
||||
Arc::new(whatsapp::TwilioWhatsAppTransport::new(
|
||||
Arc::new(chat::transport::whatsapp::TwilioWhatsAppTransport::new(
|
||||
cfg.twilio_account_sid.clone().unwrap_or_default(),
|
||||
cfg.twilio_auth_token.clone().unwrap_or_default(),
|
||||
cfg.twilio_whatsapp_number.clone().unwrap_or_default(),
|
||||
@@ -285,7 +282,7 @@ async fn main() -> Result<(), std::io::Error> {
|
||||
.whatsapp_notification_template
|
||||
.clone()
|
||||
.unwrap_or_else(|| "pipeline_notification".to_string());
|
||||
Arc::new(whatsapp::WhatsAppTransport::new(
|
||||
Arc::new(chat::transport::whatsapp::WhatsAppTransport::new(
|
||||
cfg.whatsapp_phone_number_id.clone().unwrap_or_default(),
|
||||
cfg.whatsapp_access_token.clone().unwrap_or_default(),
|
||||
template_name,
|
||||
@@ -296,8 +293,8 @@ async fn main() -> Result<(), std::io::Error> {
|
||||
.clone()
|
||||
.unwrap_or_else(|| "Assistant".to_string());
|
||||
let root = startup_root.clone().unwrap();
|
||||
let history = whatsapp::load_whatsapp_history(&root);
|
||||
Arc::new(whatsapp::WhatsAppWebhookContext {
|
||||
let history = chat::transport::whatsapp::load_whatsapp_history(&root);
|
||||
Arc::new(chat::transport::whatsapp::WhatsAppWebhookContext {
|
||||
verify_token: cfg.whatsapp_verify_token.clone().unwrap_or_default(),
|
||||
provider,
|
||||
transport,
|
||||
@@ -308,17 +305,17 @@ async fn main() -> Result<(), std::io::Error> {
|
||||
ambient_rooms: Arc::new(std::sync::Mutex::new(std::collections::HashSet::new())),
|
||||
history: std::sync::Arc::new(tokio::sync::Mutex::new(history)),
|
||||
history_size: cfg.history_size,
|
||||
window_tracker: Arc::new(whatsapp::MessagingWindowTracker::new()),
|
||||
window_tracker: Arc::new(chat::transport::whatsapp::MessagingWindowTracker::new()),
|
||||
})
|
||||
});
|
||||
|
||||
// Build Slack webhook context if bot.toml configures transport = "slack".
|
||||
let slack_ctx: Option<Arc<slack::SlackWebhookContext>> = startup_root
|
||||
let slack_ctx: Option<Arc<chat::transport::slack::SlackWebhookContext>> = startup_root
|
||||
.as_ref()
|
||||
.and_then(|root| matrix::BotConfig::load(root))
|
||||
.and_then(|root| chat::transport::matrix::BotConfig::load(root))
|
||||
.filter(|cfg| cfg.transport == "slack")
|
||||
.map(|cfg| {
|
||||
let transport = Arc::new(slack::SlackTransport::new(
|
||||
let transport = Arc::new(chat::transport::slack::SlackTransport::new(
|
||||
cfg.slack_bot_token.clone().unwrap_or_default(),
|
||||
));
|
||||
let bot_name = cfg
|
||||
@@ -326,10 +323,10 @@ async fn main() -> Result<(), std::io::Error> {
|
||||
.clone()
|
||||
.unwrap_or_else(|| "Assistant".to_string());
|
||||
let root = startup_root.clone().unwrap();
|
||||
let history = slack::load_slack_history(&root);
|
||||
let history = chat::transport::slack::load_slack_history(&root);
|
||||
let channel_ids: std::collections::HashSet<String> =
|
||||
cfg.slack_channel_ids.iter().cloned().collect();
|
||||
Arc::new(slack::SlackWebhookContext {
|
||||
Arc::new(chat::transport::slack::SlackWebhookContext {
|
||||
signing_secret: cfg.slack_signing_secret.clone().unwrap_or_default(),
|
||||
transport,
|
||||
project_root: root,
|
||||
@@ -353,7 +350,7 @@ async fn main() -> Result<(), std::io::Error> {
|
||||
if let Some(ref ctx) = slack_ctx {
|
||||
let channels: Vec<String> = ctx.channel_ids.iter().cloned().collect();
|
||||
Some(Arc::new(BotShutdownNotifier::new(
|
||||
Arc::clone(&ctx.transport) as Arc<dyn crate::transport::ChatTransport>,
|
||||
Arc::clone(&ctx.transport) as Arc<dyn crate::chat::ChatTransport>,
|
||||
channels,
|
||||
ctx.bot_name.clone(),
|
||||
)))
|
||||
@@ -362,7 +359,7 @@ async fn main() -> Result<(), std::io::Error> {
|
||||
};
|
||||
// Retain a reference to the WhatsApp context for shutdown notifications.
|
||||
// At shutdown time we read ambient_rooms to get the current set of active senders.
|
||||
let whatsapp_ctx_for_shutdown: Option<Arc<whatsapp::WhatsAppWebhookContext>> =
|
||||
let whatsapp_ctx_for_shutdown: Option<Arc<chat::transport::whatsapp::WhatsAppWebhookContext>> =
|
||||
whatsapp_ctx.clone();
|
||||
|
||||
// Watch channel: signals the Matrix bot task to send a shutdown announcement.
|
||||
@@ -391,7 +388,7 @@ async fn main() -> Result<(), std::io::Error> {
|
||||
// Optional Matrix bot: connect to the homeserver and start listening for
|
||||
// messages if `.storkit/bot.toml` is present and enabled.
|
||||
if let Some(ref root) = startup_root {
|
||||
matrix::spawn_bot(
|
||||
chat::transport::matrix::spawn_bot(
|
||||
root,
|
||||
watcher_tx_for_bot,
|
||||
perm_rx_for_bot,
|
||||
@@ -446,7 +443,7 @@ async fn main() -> Result<(), std::io::Error> {
|
||||
let rooms: Vec<String> = ctx.ambient_rooms.lock().unwrap().iter().cloned().collect();
|
||||
if !rooms.is_empty() {
|
||||
let wa_notifier = BotShutdownNotifier::new(
|
||||
Arc::clone(&ctx.transport) as Arc<dyn crate::transport::ChatTransport>,
|
||||
Arc::clone(&ctx.transport) as Arc<dyn crate::chat::ChatTransport>,
|
||||
rooms,
|
||||
ctx.bot_name.clone(),
|
||||
);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::agents::AgentPool;
|
||||
use crate::slog;
|
||||
use crate::transport::ChatTransport;
|
||||
use crate::chat::ChatTransport;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -186,7 +186,7 @@ pub async fn rebuild_and_restart(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use async_trait::async_trait;
|
||||
use crate::transport::MessageId;
|
||||
use crate::chat::MessageId;
|
||||
use std::sync::Mutex;
|
||||
|
||||
/// In-memory transport that records sent messages.
|
||||
|
||||
Reference in New Issue
Block a user