diff --git a/src/agent/channel.rs b/src/agent/channel.rs index ad9932f81..f606c3b52 100644 --- a/src/agent/channel.rs +++ b/src/agent/channel.rs @@ -1822,8 +1822,11 @@ impl Channel { // Response mode guardrail: // In Quiet/MentionOnly modes, ingest messages but only reply when explicitly invoked. + // Cron-originated messages carry a platform source (e.g., "slack") for adapter + // routing but use sender_id="system" — exempt them from suppression. if !matches!(self.resolved_settings.response_mode, ResponseMode::Active) && message.source != "system" + && message.sender_id != "system" && !self.is_dm() { (invoked_by_command, invoked_by_mention, invoked_by_reply) = @@ -3809,6 +3812,35 @@ mod tests { assert!(!invoked_by_reply); } + #[test] + fn response_mode_guard_allows_cron_messages_through() { + // Cron messages have source="slack" (for adapter routing) but sender_id="system". + // The response-mode guard must not suppress them. + let mut message = inbound_message("slack", &[], "Check the weather"); + message.sender_id = "system".into(); + message.conversation_id = "cron:daily-weather".into(); + + // compute_listen_mode_invocation returns all false — no command/mention/reply + let (cmd, mention, reply) = compute_listen_mode_invocation(&message, "Check the weather"); + assert!(!cmd); + assert!(!mention); + assert!(!reply); + + // The guard condition should NOT enter suppression because sender_id == "system" + let response_mode_not_active = true; // Quiet or MentionOnly + let source_is_system = message.source == "system"; // false + let sender_is_system = message.sender_id == "system"; // true + let is_dm = is_dm_conversation_id(&message.conversation_id); // false + + // Full guard: all four conditions must be true to suppress + let would_suppress = + response_mode_not_active && !source_is_system && !sender_is_system && !is_dm; + assert!( + !would_suppress, + "cron messages (sender_id=system) must bypass response-mode suppression" + ); + } + #[test] fn discord_quiet_mode_ping_ack_requires_directed_ping() { let directed_message = inbound_message(