Skip to main content

walrus_core/protocol/message/
client.rs

1//! Messages sent by the client to the gateway.
2
3use compact_str::CompactString;
4use serde::{Deserialize, Serialize};
5
6/// Hub package action.
7#[derive(Debug, Clone, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum HubAction {
10    /// Install a hub package.
11    Install,
12    /// Uninstall a hub package.
13    Uninstall,
14}
15
16/// Send a message to an agent and receive a complete response.
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct SendRequest {
19    /// Target agent identifier.
20    pub agent: CompactString,
21    /// Message content.
22    pub content: String,
23    /// Session to reuse. `None` creates a new session.
24    #[serde(default, skip_serializing_if = "Option::is_none")]
25    pub session: Option<u64>,
26    /// Sender identity (e.g. `"tg:12345"`, `"dc:67890"`). `None` = local.
27    #[serde(default, skip_serializing_if = "Option::is_none")]
28    pub sender: Option<CompactString>,
29}
30
31/// Send a message to an agent and receive a streamed response.
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct StreamRequest {
34    /// Target agent identifier.
35    pub agent: CompactString,
36    /// Message content.
37    pub content: String,
38    /// Session to reuse. `None` creates a new session.
39    #[serde(default, skip_serializing_if = "Option::is_none")]
40    pub session: Option<u64>,
41    /// Sender identity (e.g. `"tg:12345"`, `"dc:67890"`). `None` = local.
42    #[serde(default, skip_serializing_if = "Option::is_none")]
43    pub sender: Option<CompactString>,
44}
45
46/// Request download of a model's files with progress reporting.
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct DownloadRequest {
49    /// HuggingFace model ID.
50    pub model: CompactString,
51}
52
53/// Install or uninstall a hub package.
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct HubRequest {
56    /// Package identifier in `scope/name` format.
57    pub package: CompactString,
58    /// Action to perform.
59    pub action: HubAction,
60}
61
62/// Memory graph query operation.
63#[derive(Debug, Clone, Serialize, Deserialize)]
64#[serde(rename_all = "snake_case")]
65pub enum MemoryOp {
66    /// List entities, optionally filtered by type.
67    Entities {
68        #[serde(default, skip_serializing_if = "Option::is_none")]
69        entity_type: Option<String>,
70        #[serde(default, skip_serializing_if = "Option::is_none")]
71        limit: Option<u32>,
72    },
73    /// List relations, optionally filtered by entity ID.
74    Relations {
75        #[serde(default, skip_serializing_if = "Option::is_none")]
76        entity_id: Option<String>,
77        #[serde(default, skip_serializing_if = "Option::is_none")]
78        limit: Option<u32>,
79    },
80    /// List journal entries, optionally filtered by agent.
81    Journals {
82        #[serde(default, skip_serializing_if = "Option::is_none")]
83        agent: Option<String>,
84        #[serde(default, skip_serializing_if = "Option::is_none")]
85        limit: Option<u32>,
86    },
87    /// Search entities by query text.
88    Search {
89        query: String,
90        #[serde(default, skip_serializing_if = "Option::is_none")]
91        entity_type: Option<String>,
92        #[serde(default, skip_serializing_if = "Option::is_none")]
93        limit: Option<u32>,
94    },
95}
96
97/// Messages sent by the client to the gateway.
98#[derive(Debug, Clone, Serialize, Deserialize)]
99#[serde(tag = "type", rename_all = "snake_case")]
100pub enum ClientMessage {
101    /// Send a message to an agent and receive a complete response.
102    Send {
103        /// Target agent identifier.
104        agent: CompactString,
105        /// Message content.
106        content: String,
107        /// Session to reuse. `None` creates a new session.
108        #[serde(default, skip_serializing_if = "Option::is_none")]
109        session: Option<u64>,
110        /// Sender identity. `None` = local.
111        #[serde(default, skip_serializing_if = "Option::is_none")]
112        sender: Option<CompactString>,
113    },
114    /// Send a message to an agent and receive a streamed response.
115    Stream {
116        /// Target agent identifier.
117        agent: CompactString,
118        /// Message content.
119        content: String,
120        /// Session to reuse. `None` creates a new session.
121        #[serde(default, skip_serializing_if = "Option::is_none")]
122        session: Option<u64>,
123        /// Sender identity. `None` = local.
124        #[serde(default, skip_serializing_if = "Option::is_none")]
125        sender: Option<CompactString>,
126    },
127    /// Request download of a model's files with progress reporting.
128    Download {
129        /// HuggingFace model ID (e.g. "microsoft/Phi-3.5-mini-instruct").
130        model: CompactString,
131    },
132    /// Ping the server (keepalive).
133    Ping,
134    /// Install or uninstall a hub package.
135    Hub {
136        /// Package identifier in `scope/name` format.
137        package: CompactString,
138        /// Action to perform.
139        action: HubAction,
140    },
141    /// List active sessions.
142    Sessions,
143    /// Kill (close) a session.
144    Kill {
145        /// Session ID to close.
146        session: u64,
147    },
148    /// List tasks in the task registry.
149    Tasks,
150    /// Kill (cancel) a task.
151    KillTask {
152        /// Task ID to cancel.
153        task_id: u64,
154    },
155    /// Approve a blocked task's inbox item.
156    Approve {
157        /// Task ID to approve.
158        task_id: u64,
159        /// Response to send to the blocked tool call.
160        response: String,
161    },
162    /// Evaluate whether the agent should respond to a message.
163    ///
164    /// Used by channel loops for group-chat gating (DD#39). Returns
165    /// `ServerMessage::Evaluation` with a boolean decision.
166    Evaluate {
167        /// Target agent identifier.
168        agent: CompactString,
169        /// Message content to evaluate.
170        content: String,
171        /// Session to use for context. `None` creates a temporary session.
172        #[serde(default, skip_serializing_if = "Option::is_none")]
173        session: Option<u64>,
174        /// Sender identity. `None` = local.
175        #[serde(default, skip_serializing_if = "Option::is_none")]
176        sender: Option<CompactString>,
177    },
178    /// Subscribe to task lifecycle events (streaming).
179    SubscribeTasks,
180    /// List downloads in the download registry.
181    Downloads,
182    /// Subscribe to download lifecycle events (streaming).
183    SubscribeDownloads,
184    /// Get the full daemon config as JSON.
185    GetConfig,
186    /// Replace the full daemon config from JSON.
187    SetConfig {
188        /// JSON-serialized `DaemonConfig`.
189        config: String,
190    },
191    /// Query the memory graph.
192    MemoryQuery {
193        /// The query operation to perform.
194        query: MemoryOp,
195    },
196}
197
198impl From<SendRequest> for ClientMessage {
199    fn from(r: SendRequest) -> Self {
200        Self::Send {
201            agent: r.agent,
202            content: r.content,
203            session: r.session,
204            sender: r.sender,
205        }
206    }
207}
208
209impl From<StreamRequest> for ClientMessage {
210    fn from(r: StreamRequest) -> Self {
211        Self::Stream {
212            agent: r.agent,
213            content: r.content,
214            session: r.session,
215            sender: r.sender,
216        }
217    }
218}
219
220impl From<DownloadRequest> for ClientMessage {
221    fn from(r: DownloadRequest) -> Self {
222        Self::Download { model: r.model }
223    }
224}
225
226impl From<HubRequest> for ClientMessage {
227    fn from(r: HubRequest) -> Self {
228        Self::Hub {
229            package: r.package,
230            action: r.action,
231        }
232    }
233}