storkit: merge 421_story_timer_command_for_deferred_agent_start

This commit is contained in:
dave
2026-03-28 08:59:36 +00:00
parent 1ec9aaab8a
commit cf5424f9a6
7 changed files with 836 additions and 0 deletions
@@ -1,4 +1,5 @@
use crate::agents::AgentPool;
use crate::chat::timer::TimerStore;
use crate::chat::ChatTransport;
use crate::http::context::{PermissionDecision, PermissionForward};
use matrix_sdk::ruma::{OwnedEventId, OwnedRoomId, OwnedUserId};
@@ -55,6 +56,8 @@ pub struct BotContext {
/// All message I/O goes through this abstraction so the bot logic works
/// with any platform, not just Matrix.
pub transport: Arc<dyn ChatTransport>,
/// Persistent store for pending deferred-start timers.
pub timer_store: Arc<TimerStore>,
}
// ---------------------------------------------------------------------------
@@ -103,6 +106,9 @@ mod tests {
"test-token".to_string(),
"pipeline_notification".to_string(),
)),
timer_store: Arc::new(crate::chat::timer::TimerStore::load(
std::path::PathBuf::from("/tmp/timers.json"),
)),
};
// Clone must work (required by Matrix SDK event handler injection).
let _cloned = ctx.clone();
@@ -433,6 +433,29 @@ pub(super) async fn on_room_message(
return;
}
// Check for the timer command, which requires async file I/O and cannot
// be handled by the sync command registry.
if let Some(timer_cmd) = crate::chat::timer::extract_timer_command(
&user_message,
&ctx.bot_name,
ctx.bot_user_id.as_str(),
) {
slog!("[matrix-bot] Handling timer command from {sender}: {timer_cmd:?}");
let response = crate::chat::timer::handle_timer_command(
timer_cmd,
&ctx.timer_store,
&ctx.project_root,
)
.await;
let html = markdown_to_html(&response);
if let Ok(msg_id) = ctx.transport.send_message(&room_id_str, &response, &html).await
&& let Ok(event_id) = msg_id.parse()
{
ctx.bot_sent_event_ids.lock().await.insert(event_id);
}
return;
}
// Spawn a separate task so the Matrix sync loop is not blocked while we
// wait for the LLM response (which can take several seconds).
tokio::spawn(async move {
@@ -215,6 +215,15 @@ pub async fn run_bot(
.unwrap_or_else(|| "Assistant".to_string());
let announce_bot_name = bot_name.clone();
let timer_store = Arc::new(crate::chat::timer::TimerStore::load(
project_root.join(".storkit").join("timers.json"),
));
crate::chat::timer::spawn_timer_tick_loop(
Arc::clone(&timer_store),
Arc::clone(&agents),
project_root.clone(),
);
let ctx = BotContext {
bot_user_id,
target_room_ids,
@@ -231,6 +240,7 @@ pub async fn run_bot(
agents,
htop_sessions: Arc::new(TokioMutex::new(HashMap::new())),
transport: Arc::clone(&transport),
timer_store,
};
slog!("[matrix-bot] Cryptographic identity verification is always ON — commands from unencrypted rooms or unverified devices are rejected");