Skip to main content

walrus_core/protocol/message/
mod.rs

1//! Wire protocol message types — enums, payload structs, and conversions.
2
3use crate::protocol::message::{client::ClientMessage, server::ServerMessage};
4use compact_str::CompactString;
5use serde::{Deserialize, Serialize};
6use std::collections::BTreeMap;
7
8pub mod client;
9pub mod server;
10
11// ---------------------------------------------------------------------------
12// Shared summary types
13// ---------------------------------------------------------------------------
14
15/// Summary of a registered agent.
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct AgentSummary {
18    /// Agent name.
19    pub name: CompactString,
20    /// Agent description.
21    pub description: CompactString,
22}
23
24/// Summary of a connected MCP server.
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct McpServerSummary {
27    /// Server name.
28    pub name: CompactString,
29    /// Tool names provided by this server.
30    pub tools: Vec<CompactString>,
31}
32
33// ---------------------------------------------------------------------------
34// Request structs (from ClientMessage variants with fields)
35// ---------------------------------------------------------------------------
36
37/// Send a message to an agent and receive a complete response.
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct SendRequest {
40    /// Target agent identifier.
41    pub agent: CompactString,
42    /// Message content.
43    pub content: String,
44}
45
46/// Send a message to an agent and receive a streamed response.
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct StreamRequest {
49    /// Target agent identifier.
50    pub agent: CompactString,
51    /// Message content.
52    pub content: String,
53}
54
55/// Clear the session history for an agent.
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct ClearSessionRequest {
58    /// Target agent identifier.
59    pub agent: CompactString,
60}
61
62/// Get detailed info for a specific agent.
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct AgentInfoRequest {
65    /// Agent name.
66    pub agent: CompactString,
67}
68
69/// Get a specific memory entry by key.
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct GetMemoryRequest {
72    /// Memory key.
73    pub key: String,
74}
75
76/// Request download of a model's files with progress reporting.
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct DownloadRequest {
79    /// HuggingFace model ID.
80    pub model: CompactString,
81}
82
83/// Add an MCP server to config and reload.
84#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct McpAddRequest {
86    /// Server name.
87    pub name: CompactString,
88    /// Command to spawn.
89    pub command: String,
90    /// Command arguments.
91    #[serde(default)]
92    pub args: Vec<String>,
93    /// Environment variables.
94    #[serde(default)]
95    pub env: BTreeMap<String, String>,
96}
97
98/// Remove an MCP server from config and reload.
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct McpRemoveRequest {
101    /// Server name to remove.
102    pub name: CompactString,
103}
104
105// ---------------------------------------------------------------------------
106// Response structs (from ServerMessage variants)
107// ---------------------------------------------------------------------------
108
109/// Complete response from an agent.
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct SendResponse {
112    /// Source agent identifier.
113    pub agent: CompactString,
114    /// Response content.
115    pub content: String,
116}
117
118/// Session cleared confirmation.
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct SessionCleared {
121    /// Agent whose session was cleared.
122    pub agent: CompactString,
123}
124
125/// List of registered agents.
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct AgentList {
128    /// Agent summaries.
129    pub agents: Vec<AgentSummary>,
130}
131
132/// Detailed agent information.
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct AgentDetail {
135    /// Agent name.
136    pub name: CompactString,
137    /// Agent description.
138    pub description: CompactString,
139    /// Registered tool names.
140    pub tools: Vec<CompactString>,
141    /// Skill tags.
142    pub skill_tags: Vec<CompactString>,
143    /// System prompt.
144    pub system_prompt: String,
145}
146
147/// List of memory entries.
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct MemoryList {
150    /// Key-value pairs.
151    pub entries: Vec<(String, String)>,
152}
153
154/// A single memory entry.
155#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct MemoryEntry {
157    /// Memory key.
158    pub key: String,
159    /// Memory value (None if not found).
160    pub value: Option<String>,
161}
162
163/// Skills reloaded confirmation.
164#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct SkillsReloaded {
166    /// Number of skills loaded.
167    pub count: usize,
168}
169
170/// MCP server added confirmation.
171#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct McpAdded {
173    /// Server name.
174    pub name: CompactString,
175    /// Tools provided by this server.
176    pub tools: Vec<CompactString>,
177}
178
179/// MCP server removed confirmation.
180#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct McpRemoved {
182    /// Server name.
183    pub name: CompactString,
184    /// Tools that were removed.
185    pub tools: Vec<CompactString>,
186}
187
188/// MCP servers reloaded confirmation.
189#[derive(Debug, Clone, Serialize, Deserialize)]
190pub struct McpReloaded {
191    /// Connected servers after reload.
192    pub servers: Vec<McpServerSummary>,
193}
194
195/// List of connected MCP servers.
196#[derive(Debug, Clone, Serialize, Deserialize)]
197pub struct McpServerList {
198    /// Connected servers.
199    pub servers: Vec<McpServerSummary>,
200}
201
202// ---------------------------------------------------------------------------
203// Streaming event enums
204// ---------------------------------------------------------------------------
205
206/// Events emitted during a streamed agent response.
207#[derive(Debug, Clone, Serialize, Deserialize)]
208pub enum StreamEvent {
209    /// Stream has started.
210    Start {
211        /// Source agent identifier.
212        agent: CompactString,
213    },
214    /// A chunk of streamed content.
215    Chunk {
216        /// Chunk content.
217        content: String,
218    },
219    /// Stream has ended.
220    End {
221        /// Source agent identifier.
222        agent: CompactString,
223    },
224}
225
226/// Events emitted during a model download.
227#[derive(Debug, Clone, Serialize, Deserialize)]
228pub enum DownloadEvent {
229    /// Download has started.
230    Start {
231        /// Model being downloaded.
232        model: CompactString,
233    },
234    /// A file download has started.
235    FileStart {
236        /// Filename within the repo.
237        filename: String,
238        /// Total size in bytes.
239        size: u64,
240    },
241    /// Download progress for current file (delta, not cumulative).
242    Progress {
243        /// Bytes downloaded in this chunk.
244        bytes: u64,
245    },
246    /// A file download has completed.
247    FileEnd {
248        /// Filename within the repo.
249        filename: String,
250    },
251    /// All downloads complete.
252    End {
253        /// Model that was downloaded.
254        model: CompactString,
255    },
256}
257
258// ---------------------------------------------------------------------------
259// From<Request> for ClientMessage
260// ---------------------------------------------------------------------------
261
262impl From<SendRequest> for ClientMessage {
263    fn from(r: SendRequest) -> Self {
264        Self::Send {
265            agent: r.agent,
266            content: r.content,
267        }
268    }
269}
270
271impl From<StreamRequest> for ClientMessage {
272    fn from(r: StreamRequest) -> Self {
273        Self::Stream {
274            agent: r.agent,
275            content: r.content,
276        }
277    }
278}
279
280impl From<ClearSessionRequest> for ClientMessage {
281    fn from(r: ClearSessionRequest) -> Self {
282        Self::ClearSession { agent: r.agent }
283    }
284}
285
286impl From<AgentInfoRequest> for ClientMessage {
287    fn from(r: AgentInfoRequest) -> Self {
288        Self::AgentInfo { agent: r.agent }
289    }
290}
291
292impl From<GetMemoryRequest> for ClientMessage {
293    fn from(r: GetMemoryRequest) -> Self {
294        Self::GetMemory { key: r.key }
295    }
296}
297
298impl From<DownloadRequest> for ClientMessage {
299    fn from(r: DownloadRequest) -> Self {
300        Self::Download { model: r.model }
301    }
302}
303
304impl From<McpAddRequest> for ClientMessage {
305    fn from(r: McpAddRequest) -> Self {
306        Self::McpAdd {
307            name: r.name,
308            command: r.command,
309            args: r.args,
310            env: r.env,
311        }
312    }
313}
314
315impl From<McpRemoveRequest> for ClientMessage {
316    fn from(r: McpRemoveRequest) -> Self {
317        Self::McpRemove { name: r.name }
318    }
319}
320
321// ---------------------------------------------------------------------------
322// From<Response> for ServerMessage
323// ---------------------------------------------------------------------------
324
325impl From<SendResponse> for ServerMessage {
326    fn from(r: SendResponse) -> Self {
327        Self::Response {
328            agent: r.agent,
329            content: r.content,
330        }
331    }
332}
333
334impl From<SessionCleared> for ServerMessage {
335    fn from(r: SessionCleared) -> Self {
336        Self::SessionCleared { agent: r.agent }
337    }
338}
339
340impl From<AgentList> for ServerMessage {
341    fn from(r: AgentList) -> Self {
342        Self::AgentList { agents: r.agents }
343    }
344}
345
346impl From<AgentDetail> for ServerMessage {
347    fn from(r: AgentDetail) -> Self {
348        Self::AgentDetail {
349            name: r.name,
350            description: r.description,
351            tools: r.tools,
352            skill_tags: r.skill_tags,
353            system_prompt: r.system_prompt,
354        }
355    }
356}
357
358impl From<MemoryList> for ServerMessage {
359    fn from(r: MemoryList) -> Self {
360        Self::MemoryList { entries: r.entries }
361    }
362}
363
364impl From<MemoryEntry> for ServerMessage {
365    fn from(r: MemoryEntry) -> Self {
366        Self::MemoryEntry {
367            key: r.key,
368            value: r.value,
369        }
370    }
371}
372
373impl From<SkillsReloaded> for ServerMessage {
374    fn from(r: SkillsReloaded) -> Self {
375        Self::SkillsReloaded { count: r.count }
376    }
377}
378
379impl From<McpAdded> for ServerMessage {
380    fn from(r: McpAdded) -> Self {
381        Self::McpAdded {
382            name: r.name,
383            tools: r.tools,
384        }
385    }
386}
387
388impl From<McpRemoved> for ServerMessage {
389    fn from(r: McpRemoved) -> Self {
390        Self::McpRemoved {
391            name: r.name,
392            tools: r.tools,
393        }
394    }
395}
396
397impl From<McpReloaded> for ServerMessage {
398    fn from(r: McpReloaded) -> Self {
399        Self::McpReloaded { servers: r.servers }
400    }
401}
402
403impl From<McpServerList> for ServerMessage {
404    fn from(r: McpServerList) -> Self {
405        Self::McpServerList { servers: r.servers }
406    }
407}
408
409// ---------------------------------------------------------------------------
410// From<StreamEvent> for ServerMessage
411// ---------------------------------------------------------------------------
412
413impl From<StreamEvent> for ServerMessage {
414    fn from(e: StreamEvent) -> Self {
415        match e {
416            StreamEvent::Start { agent } => Self::StreamStart { agent },
417            StreamEvent::Chunk { content } => Self::StreamChunk { content },
418            StreamEvent::End { agent } => Self::StreamEnd { agent },
419        }
420    }
421}
422
423// ---------------------------------------------------------------------------
424// From<DownloadEvent> for ServerMessage
425// ---------------------------------------------------------------------------
426
427impl From<DownloadEvent> for ServerMessage {
428    fn from(e: DownloadEvent) -> Self {
429        match e {
430            DownloadEvent::Start { model } => Self::DownloadStart { model },
431            DownloadEvent::FileStart { filename, size } => {
432                Self::DownloadFileStart { filename, size }
433            }
434            DownloadEvent::Progress { bytes } => Self::DownloadProgress { bytes },
435            DownloadEvent::FileEnd { filename } => Self::DownloadFileEnd { filename },
436            DownloadEvent::End { model } => Self::DownloadEnd { model },
437        }
438    }
439}
440
441// ---------------------------------------------------------------------------
442// TryFrom<ServerMessage> for response structs
443// ---------------------------------------------------------------------------
444
445fn unexpected(msg: &str) -> anyhow::Error {
446    anyhow::anyhow!("unexpected response: {msg}")
447}
448
449fn error_or_unexpected(msg: ServerMessage) -> anyhow::Error {
450    match msg {
451        ServerMessage::Error { code, message } => {
452            anyhow::anyhow!("server error ({code}): {message}")
453        }
454        other => unexpected(&format!("{other:?}")),
455    }
456}
457
458impl TryFrom<ServerMessage> for SendResponse {
459    type Error = anyhow::Error;
460    fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
461        match msg {
462            ServerMessage::Response { agent, content } => Ok(Self { agent, content }),
463            other => Err(error_or_unexpected(other)),
464        }
465    }
466}
467
468impl TryFrom<ServerMessage> for SessionCleared {
469    type Error = anyhow::Error;
470    fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
471        match msg {
472            ServerMessage::SessionCleared { agent } => Ok(Self { agent }),
473            other => Err(error_or_unexpected(other)),
474        }
475    }
476}
477
478impl TryFrom<ServerMessage> for AgentList {
479    type Error = anyhow::Error;
480    fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
481        match msg {
482            ServerMessage::AgentList { agents } => Ok(Self { agents }),
483            other => Err(error_or_unexpected(other)),
484        }
485    }
486}
487
488impl TryFrom<ServerMessage> for AgentDetail {
489    type Error = anyhow::Error;
490    fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
491        match msg {
492            ServerMessage::AgentDetail {
493                name,
494                description,
495                tools,
496                skill_tags,
497                system_prompt,
498            } => Ok(Self {
499                name,
500                description,
501                tools,
502                skill_tags,
503                system_prompt,
504            }),
505            other => Err(error_or_unexpected(other)),
506        }
507    }
508}
509
510impl TryFrom<ServerMessage> for MemoryList {
511    type Error = anyhow::Error;
512    fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
513        match msg {
514            ServerMessage::MemoryList { entries } => Ok(Self { entries }),
515            other => Err(error_or_unexpected(other)),
516        }
517    }
518}
519
520impl TryFrom<ServerMessage> for MemoryEntry {
521    type Error = anyhow::Error;
522    fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
523        match msg {
524            ServerMessage::MemoryEntry { key, value } => Ok(Self { key, value }),
525            other => Err(error_or_unexpected(other)),
526        }
527    }
528}
529
530impl TryFrom<ServerMessage> for SkillsReloaded {
531    type Error = anyhow::Error;
532    fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
533        match msg {
534            ServerMessage::SkillsReloaded { count } => Ok(Self { count }),
535            other => Err(error_or_unexpected(other)),
536        }
537    }
538}
539
540impl TryFrom<ServerMessage> for McpAdded {
541    type Error = anyhow::Error;
542    fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
543        match msg {
544            ServerMessage::McpAdded { name, tools } => Ok(Self { name, tools }),
545            other => Err(error_or_unexpected(other)),
546        }
547    }
548}
549
550impl TryFrom<ServerMessage> for McpRemoved {
551    type Error = anyhow::Error;
552    fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
553        match msg {
554            ServerMessage::McpRemoved { name, tools } => Ok(Self { name, tools }),
555            other => Err(error_or_unexpected(other)),
556        }
557    }
558}
559
560impl TryFrom<ServerMessage> for McpReloaded {
561    type Error = anyhow::Error;
562    fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
563        match msg {
564            ServerMessage::McpReloaded { servers } => Ok(Self { servers }),
565            other => Err(error_or_unexpected(other)),
566        }
567    }
568}
569
570impl TryFrom<ServerMessage> for McpServerList {
571    type Error = anyhow::Error;
572    fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
573        match msg {
574            ServerMessage::McpServerList { servers } => Ok(Self { servers }),
575            other => Err(error_or_unexpected(other)),
576        }
577    }
578}
579
580// ---------------------------------------------------------------------------
581// TryFrom<ServerMessage> for streaming events
582// ---------------------------------------------------------------------------
583
584impl TryFrom<ServerMessage> for StreamEvent {
585    type Error = anyhow::Error;
586    fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
587        match msg {
588            ServerMessage::StreamStart { agent } => Ok(Self::Start { agent }),
589            ServerMessage::StreamChunk { content } => Ok(Self::Chunk { content }),
590            ServerMessage::StreamEnd { agent } => Ok(Self::End { agent }),
591            other => Err(error_or_unexpected(other)),
592        }
593    }
594}
595
596impl TryFrom<ServerMessage> for DownloadEvent {
597    type Error = anyhow::Error;
598    fn try_from(msg: ServerMessage) -> anyhow::Result<Self> {
599        match msg {
600            ServerMessage::DownloadStart { model } => Ok(Self::Start { model }),
601            ServerMessage::DownloadFileStart { filename, size } => {
602                Ok(Self::FileStart { filename, size })
603            }
604            ServerMessage::DownloadProgress { bytes } => Ok(Self::Progress { bytes }),
605            ServerMessage::DownloadFileEnd { filename } => Ok(Self::FileEnd { filename }),
606            ServerMessage::DownloadEnd { model } => Ok(Self::End { model }),
607            other => Err(error_or_unexpected(other)),
608        }
609    }
610}