Skip to main content

systemprompt_models/notifications/
event.rs

1//! The event a producer appends to the outbox for the notifications consumer.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use chrono::{DateTime, Utc};
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9use systemprompt_identifiers::{AgentName, NotificationStreamId, UserId};
10
11use super::attention::Attention;
12use super::kind::NotificationKind;
13use super::subject::SubjectRef;
14
15pub const NOTIFICATIONS_CONSUMER: &str = "notifications";
16pub const NOTIFICATION_EVENT_VERSION: u32 = 1;
17
18/// A user the producer addresses directly, optionally at a chosen attention.
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
20pub struct DirectRecipient {
21    pub user: UserId,
22    pub attention: Option<Attention>,
23}
24
25/// One thing that happened, before routing decides who hears about it.
26#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
27pub struct NotificationEvent {
28    pub kind: NotificationKind,
29    pub actor: UserId,
30    pub actor_is_agent: bool,
31    pub via_agent: Option<AgentName>,
32    pub subjects: Vec<SubjectRef>,
33    pub stream: Option<NotificationStreamId>,
34    pub direct_recipients: Vec<DirectRecipient>,
35    pub sender_attention: Option<Attention>,
36    pub title: Option<String>,
37    pub body: Option<String>,
38    // JSON: the producer's payload is the event's free-form contract field.
39    pub payload: serde_json::Value,
40    pub occurred_at: DateTime<Utc>,
41}
42
43impl NotificationEvent {
44    #[must_use]
45    pub const fn new(kind: NotificationKind, actor: UserId, occurred_at: DateTime<Utc>) -> Self {
46        Self {
47            kind,
48            actor,
49            actor_is_agent: false,
50            via_agent: None,
51            subjects: Vec::new(),
52            stream: None,
53            direct_recipients: Vec::new(),
54            sender_attention: None,
55            title: None,
56            body: None,
57            payload: serde_json::Value::Null,
58            occurred_at,
59        }
60    }
61
62    #[must_use]
63    pub const fn with_actor_is_agent(mut self, actor_is_agent: bool) -> Self {
64        self.actor_is_agent = actor_is_agent;
65        self
66    }
67
68    #[must_use]
69    pub fn with_via_agent(mut self, via_agent: AgentName) -> Self {
70        self.via_agent = Some(via_agent);
71        self
72    }
73
74    #[must_use]
75    pub fn with_subject(mut self, subject: SubjectRef) -> Self {
76        self.subjects.push(subject);
77        self
78    }
79
80    #[must_use]
81    pub fn with_subjects(mut self, subjects: impl IntoIterator<Item = SubjectRef>) -> Self {
82        self.subjects.extend(subjects);
83        self
84    }
85
86    #[must_use]
87    pub fn with_stream(mut self, stream: NotificationStreamId) -> Self {
88        self.stream = Some(stream);
89        self
90    }
91
92    #[must_use]
93    pub fn with_direct_recipient(mut self, user: UserId, attention: Option<Attention>) -> Self {
94        self.direct_recipients
95            .push(DirectRecipient { user, attention });
96        self
97    }
98
99    #[must_use]
100    pub fn with_direct_recipients(
101        mut self,
102        recipients: impl IntoIterator<Item = DirectRecipient>,
103    ) -> Self {
104        self.direct_recipients.extend(recipients);
105        self
106    }
107
108    #[must_use]
109    pub const fn with_sender_attention(mut self, attention: Attention) -> Self {
110        self.sender_attention = Some(attention);
111        self
112    }
113
114    #[must_use]
115    pub fn with_title(mut self, title: impl Into<String>) -> Self {
116        self.title = Some(title.into());
117        self
118    }
119
120    #[must_use]
121    pub fn with_body(mut self, body: impl Into<String>) -> Self {
122        self.body = Some(body.into());
123        self
124    }
125
126    #[must_use]
127    // JSON: the producer's payload is the event's free-form contract field.
128    pub fn with_payload(mut self, payload: serde_json::Value) -> Self {
129        self.payload = payload;
130        self
131    }
132}