Skip to main content

walrus_protocol/message/
mod.rs

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