//! Reminder Hand - Internal hand for scheduled reminders //! //! This is a system hand (id `_reminder`) used by the schedule interception //! layer in `agent_chat_stream`. When the NlScheduleParser detects a schedule //! intent in chat, it creates a trigger targeting this hand. The SchedulerService //! fires the trigger at the scheduled time. use async_trait::async_trait; use serde_json::Value; use zclaw_types::Result; use crate::{Hand, HandConfig, HandContext, HandResult, HandStatus}; /// Internal reminder hand for scheduled tasks pub struct ReminderHand { config: HandConfig, } impl ReminderHand { /// Create a new reminder hand pub fn new() -> Self { Self { config: HandConfig { id: "_reminder".to_string(), name: "定时提醒".to_string(), description: "Internal hand for scheduled reminders".to_string(), needs_approval: false, dependencies: vec![], input_schema: None, tags: vec!["system".to_string()], enabled: true, max_concurrent: 0, timeout_secs: 0, }, } } } #[async_trait] impl Hand for ReminderHand { fn config(&self) -> &HandConfig { &self.config } async fn execute(&self, _context: &HandContext, input: Value) -> Result { let task_desc = input .get("task_description") .and_then(|v| v.as_str()) .unwrap_or("定时提醒"); let cron = input .get("cron") .and_then(|v| v.as_str()) .unwrap_or(""); let fired_at = input .get("fired_at") .and_then(|v| v.as_str()) .unwrap_or("unknown time"); tracing::info!( "[ReminderHand] Fired at {} — task: {}, cron: {}", fired_at, task_desc, cron ); Ok(HandResult::success(serde_json::json!({ "task": task_desc, "cron": cron, "fired_at": fired_at, "status": "reminded", }))) } fn status(&self) -> HandStatus { HandStatus::Idle } }