wactorz_core/
message.rs

1//! Inter-actor message types.
2//!
3//! All communication between actors flows through typed [`Message`] values
4//! delivered via async [`tokio::sync::mpsc`] channels (the mailbox).
5//!
6//! This mirrors the Python `dict`-based message protocol but adds compile-time
7//! type safety via [`MessageType`].
8
9use serde::{Deserialize, Serialize};
10
11/// Discriminated union of all message payloads that actors exchange.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13#[serde(tag = "type", rename_all = "snake_case")]
14pub enum MessageType {
15    /// Plain text request/response.
16    Text { content: String },
17
18    /// Request the recipient to perform a task and reply.
19    Task {
20        task_id: String,
21        description: String,
22        /// Arbitrary JSON payload for the task.
23        payload: serde_json::Value,
24    },
25
26    /// Reply to a previously received `Task`.
27    TaskResult {
28        task_id: String,
29        success: bool,
30        result: serde_json::Value,
31    },
32
33    /// Heartbeat / keep-alive signal.
34    Heartbeat { sequence: u64 },
35
36    /// Actor lifecycle command.
37    Command { command: ActorCommand },
38
39    /// Alert/error broadcast.
40    Alert {
41        severity: AlertSeverity,
42        message: String,
43        context: serde_json::Value,
44    },
45
46    /// Spawn request: ask an orchestrator to create a new agent.
47    SpawnRequest {
48        agent_type: String,
49        agent_name: String,
50        config: serde_json::Value,
51    },
52
53    /// Confirmation that a spawn completed.
54    SpawnResult {
55        agent_name: String,
56        agent_id: String,
57        success: bool,
58        error: Option<String>,
59    },
60}
61
62/// Commands that can be sent to any actor.
63#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
64#[serde(rename_all = "snake_case")]
65pub enum ActorCommand {
66    /// Gracefully stop the actor.
67    Stop,
68    /// Temporarily pause message processing.
69    Pause,
70    /// Resume after a pause.
71    Resume,
72    /// Request an immediate status report.
73    Status,
74}
75
76/// Severity levels for alert messages.
77#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
78#[serde(rename_all = "lowercase")]
79pub enum AlertSeverity {
80    Info,
81    Warning,
82    Error,
83    Critical,
84}
85
86/// A message envelope routed between actors.
87#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct Message {
89    /// Unique WID for this message.
90    pub id: String,
91    /// Sender's actor WID (`None` for system-generated messages).
92    pub from: Option<String>,
93    /// Recipient actor WID (`None` means broadcast).
94    pub to: Option<String>,
95    /// Unix timestamp (milliseconds) when the message was created.
96    pub timestamp_ms: u64,
97    /// The actual payload.
98    pub payload: MessageType,
99}
100
101impl Message {
102    /// Construct a new message with a fresh WID and current timestamp.
103    pub fn new(from: Option<String>, to: Option<String>, payload: MessageType) -> Self {
104        let id = wid::HLCWidGen::new("msg".to_string(), 4, 0)
105            .expect("HLCWidGen init failed")
106            .next_hlc_wid();
107        let timestamp_ms = std::time::SystemTime::now()
108            .duration_since(std::time::UNIX_EPOCH)
109            .unwrap_or_default()
110            .as_millis() as u64;
111        Self {
112            id,
113            from,
114            to,
115            timestamp_ms,
116            payload,
117        }
118    }
119
120    /// Shorthand: send a plain text message.
121    pub fn text(from: Option<String>, to: Option<String>, content: impl Into<String>) -> Self {
122        Self::new(
123            from,
124            to,
125            MessageType::Text {
126                content: content.into(),
127            },
128        )
129    }
130
131    /// Shorthand: send a command to a specific actor.
132    pub fn command(to: String, cmd: ActorCommand) -> Self {
133        Self::new(None, Some(to), MessageType::Command { command: cmd })
134    }
135}