Skip to main content

keel_events/
types.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4/// The kind of input flowing through the event queue.
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
6#[serde(rename_all = "snake_case")]
7pub enum InputType {
8    Message,
9    Heartbeat,
10    Cron,
11    Hook,
12    Webhook,
13    AgentMessage,
14}
15
16/// Where an input originated.
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct InputSource {
19    /// Channel the input arrived on: "slack", "telegram", "desktop", "system", "webhook".
20    pub channel: String,
21    /// Identifies the specific source: channel ID, cron job name, hook name, etc.
22    pub identifier: String,
23}
24
25/// Lifecycle events emitted by the system as hooks.
26#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(rename_all = "snake_case")]
28pub enum HookEvent {
29    AppStarted,
30    AppStopping,
31    SessionStarted {
32        session_key: String,
33    },
34    SessionCompleted {
35        session_key: String,
36    },
37    SessionError {
38        session_key: String,
39        error: String,
40    },
41    RoutineTriggered {
42        routine_id: String,
43    },
44    RoutineCompleted {
45        routine_id: String,
46    },
47    Custom {
48        name: String,
49        data: serde_json::Value,
50    },
51}
52
53/// A single input flowing through the event queue.
54///
55/// All inputs — user messages, heartbeats, cron triggers, hooks, webhooks,
56/// and agent-to-agent messages — are represented as `KeelInput`.
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct KeelInput {
59    pub id: String,
60    pub input_type: InputType,
61    pub source: InputSource,
62    pub payload: serde_json::Value,
63    pub session_key: Option<String>,
64    pub project_id: Option<String>,
65    pub created_at: DateTime<Utc>,
66}
67
68impl KeelInput {
69    pub fn new(
70        input_type: InputType,
71        source: InputSource,
72        payload: serde_json::Value,
73    ) -> Self {
74        Self {
75            id: uuid::Uuid::new_v4().to_string(),
76            input_type,
77            source,
78            payload,
79            session_key: None,
80            project_id: None,
81            created_at: Utc::now(),
82        }
83    }
84
85    pub fn with_session(mut self, key: String) -> Self {
86        self.session_key = Some(key);
87        self
88    }
89
90    pub fn with_project(mut self, id: String) -> Self {
91        self.project_id = Some(id);
92        self
93    }
94
95    /// Convenience: create a user message input.
96    pub fn message(channel: &str, identifier: &str, text: &str) -> Self {
97        Self::new(
98            InputType::Message,
99            InputSource {
100                channel: channel.to_string(),
101                identifier: identifier.to_string(),
102            },
103            serde_json::json!({ "text": text }),
104        )
105    }
106
107    /// Convenience: create a heartbeat input.
108    pub fn heartbeat(prompt: &str) -> Self {
109        Self::new(
110            InputType::Heartbeat,
111            InputSource {
112                channel: "system".to_string(),
113                identifier: "heartbeat".to_string(),
114            },
115            serde_json::json!({ "prompt": prompt }),
116        )
117    }
118
119    /// Convenience: create a cron-triggered input.
120    pub fn cron(job_name: &str, prompt: &str) -> Self {
121        Self::new(
122            InputType::Cron,
123            InputSource {
124                channel: "system".to_string(),
125                identifier: job_name.to_string(),
126            },
127            serde_json::json!({ "prompt": prompt }),
128        )
129    }
130
131    /// Convenience: create a lifecycle hook input.
132    pub fn hook(event: HookEvent) -> Self {
133        let payload = serde_json::to_value(&event).unwrap_or_default();
134        Self::new(
135            InputType::Hook,
136            InputSource {
137                channel: "system".to_string(),
138                identifier: "hook".to_string(),
139            },
140            payload,
141        )
142    }
143
144    /// Convenience: create a webhook input.
145    pub fn webhook(endpoint: &str, payload: serde_json::Value) -> Self {
146        Self::new(
147            InputType::Webhook,
148            InputSource {
149                channel: "webhook".to_string(),
150                identifier: endpoint.to_string(),
151            },
152            payload,
153        )
154    }
155
156    /// Convenience: create an agent-to-agent message.
157    pub fn agent_message(from_agent: &str, to_agent: &str, text: &str) -> Self {
158        Self::new(
159            InputType::AgentMessage,
160            InputSource {
161                channel: "agent".to_string(),
162                identifier: from_agent.to_string(),
163            },
164            serde_json::json!({ "to": to_agent, "text": text }),
165        )
166    }
167}