Skip to main content

walrus_core/protocol/message/
server.rs

1//! Messages sent by the gateway to the client.
2
3use compact_str::CompactString;
4use serde::{Deserialize, Serialize};
5
6/// Complete response from an agent.
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct SendResponse {
9    /// Source agent identifier.
10    pub agent: CompactString,
11    /// Response content.
12    pub content: String,
13    /// Session ID used for this request.
14    pub session: u64,
15}
16
17/// Lightweight tool call info for streaming events.
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct ToolCallInfo {
20    /// Tool name.
21    pub name: CompactString,
22    /// Tool arguments (JSON string).
23    pub arguments: String,
24}
25
26/// Events emitted during a streamed agent response.
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub enum StreamEvent {
29    /// Stream has started.
30    Start {
31        /// Source agent identifier.
32        agent: CompactString,
33        /// Session ID used for this stream.
34        session: u64,
35    },
36    /// A chunk of streamed content.
37    Chunk {
38        /// Chunk content.
39        content: String,
40    },
41    /// A chunk of thinking/reasoning content.
42    Thinking {
43        /// Thinking content.
44        content: String,
45    },
46    /// Agent started executing tool calls.
47    ToolStart {
48        /// Tool calls being executed.
49        calls: Vec<ToolCallInfo>,
50    },
51    /// A single tool call completed.
52    ToolResult {
53        /// The tool call ID.
54        call_id: CompactString,
55        /// Tool output.
56        output: String,
57    },
58    /// All tool calls completed.
59    ToolsComplete,
60    /// Stream has ended.
61    End {
62        /// Source agent identifier.
63        agent: CompactString,
64    },
65}
66
67/// Events emitted during a model download.
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub enum DownloadEvent {
70    /// Download has started.
71    Start {
72        /// Model being downloaded.
73        model: CompactString,
74    },
75    /// A file download has started.
76    FileStart {
77        /// Model being downloaded.
78        model: CompactString,
79        /// Filename within the repo.
80        filename: String,
81        /// Total size in bytes.
82        size: u64,
83    },
84    /// Download progress for current file (delta, not cumulative).
85    Progress {
86        /// Model being downloaded.
87        model: CompactString,
88        /// Bytes downloaded in this chunk.
89        bytes: u64,
90    },
91    /// A file download has completed.
92    FileEnd {
93        /// Model being downloaded.
94        model: CompactString,
95        /// Filename within the repo.
96        filename: String,
97    },
98    /// All downloads complete.
99    End {
100        /// Model that was downloaded.
101        model: CompactString,
102    },
103}
104
105/// Events emitted during a hub install or uninstall operation.
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub enum HubEvent {
108    /// Operation has started.
109    Start {
110        /// Package being operated on.
111        package: CompactString,
112    },
113    /// A progress step message.
114    Step {
115        /// Human-readable step description.
116        message: String,
117    },
118    /// Operation has completed.
119    End {
120        /// Package that was operated on.
121        package: CompactString,
122    },
123}
124
125/// Summary of an active session.
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct SessionInfo {
128    /// Session identifier.
129    pub id: u64,
130    /// Agent this session is bound to.
131    pub agent: CompactString,
132    /// Origin of this session.
133    pub created_by: CompactString,
134    /// Number of messages in history.
135    pub message_count: usize,
136    /// Seconds since session was created.
137    pub alive_secs: u64,
138}
139
140/// Summary of a task in the task registry.
141#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct TaskInfo {
143    /// Task identifier.
144    pub id: u64,
145    /// Parent task ID (if sub-task).
146    #[serde(default, skip_serializing_if = "Option::is_none")]
147    pub parent_id: Option<u64>,
148    /// Agent assigned to this task.
149    pub agent: CompactString,
150    /// Current status.
151    pub status: String,
152    /// Task description / message.
153    pub description: String,
154    /// Result content (if finished).
155    #[serde(default, skip_serializing_if = "Option::is_none")]
156    pub result: Option<String>,
157    /// Error message (if failed).
158    #[serde(default, skip_serializing_if = "Option::is_none")]
159    pub error: Option<String>,
160    /// Origin of this task.
161    pub created_by: CompactString,
162    /// Cumulative prompt tokens.
163    pub prompt_tokens: u64,
164    /// Cumulative completion tokens.
165    pub completion_tokens: u64,
166    /// Seconds since task was created.
167    pub alive_secs: u64,
168    /// Pending inbox question (if blocked).
169    #[serde(default, skip_serializing_if = "Option::is_none")]
170    pub blocked_on: Option<String>,
171}
172
173/// Messages sent by the gateway to the client.
174#[derive(Debug, Clone, Serialize, Deserialize)]
175#[serde(tag = "type", rename_all = "snake_case")]
176pub enum ServerMessage {
177    /// Complete response from an agent.
178    Response(SendResponse),
179    /// A streamed response event.
180    Stream(StreamEvent),
181    /// A model download event.
182    Download(DownloadEvent),
183    /// Error response.
184    Error {
185        /// Error code.
186        code: u16,
187        /// Error message.
188        message: String,
189    },
190    /// Pong response to client ping.
191    Pong,
192    /// A hub install/uninstall event.
193    Hub(HubEvent),
194    /// Active session list.
195    Sessions(Vec<SessionInfo>),
196    /// Task registry list.
197    Tasks(Vec<TaskInfo>),
198}
199
200impl From<SendResponse> for ServerMessage {
201    fn from(r: SendResponse) -> Self {
202        Self::Response(r)
203    }
204}
205
206impl From<StreamEvent> for ServerMessage {
207    fn from(e: StreamEvent) -> Self {
208        Self::Stream(e)
209    }
210}
211
212impl From<DownloadEvent> for ServerMessage {
213    fn from(e: DownloadEvent) -> Self {
214        Self::Download(e)
215    }
216}
217
218impl From<HubEvent> for ServerMessage {
219    fn from(e: HubEvent) -> Self {
220        Self::Hub(e)
221    }
222}
223
224fn error_or_unexpected(msg: ServerMessage) -> anyhow::Error {
225    match msg {
226        ServerMessage::Error { code, message } => {
227            anyhow::anyhow!("server error ({code}): {message}")
228        }
229        other => anyhow::anyhow!("unexpected response: {other:?}"),
230    }
231}
232
233impl TryFrom<ServerMessage> for SendResponse {
234    type Error = anyhow::Error;
235    fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
236        match msg {
237            ServerMessage::Response(r) => Ok(r),
238            other => Err(error_or_unexpected(other)),
239        }
240    }
241}
242
243impl TryFrom<ServerMessage> for StreamEvent {
244    type Error = anyhow::Error;
245    fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
246        match msg {
247            ServerMessage::Stream(e) => Ok(e),
248            other => Err(error_or_unexpected(other)),
249        }
250    }
251}
252
253impl TryFrom<ServerMessage> for DownloadEvent {
254    type Error = anyhow::Error;
255    fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
256        match msg {
257            ServerMessage::Download(e) => Ok(e),
258            other => Err(error_or_unexpected(other)),
259        }
260    }
261}
262
263impl TryFrom<ServerMessage> for HubEvent {
264    type Error = anyhow::Error;
265    fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
266        match msg {
267            ServerMessage::Hub(e) => Ok(e),
268            other => Err(error_or_unexpected(other)),
269        }
270    }
271}