Skip to main content

magic_coder_types/protocol/
types.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use uuid::Uuid;
4
5#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
6pub enum Role {
7    Assistant,
8    User,
9}
10
11#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
12pub enum RuntimeMode {
13    Agent,
14    Plan,
15}
16
17#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
18pub enum ToolExecutionTarget {
19    Unspecified,
20    /// Tool must be executed by a connected client (TUI/VS Code).
21    ClientLocal,
22    /// Tool is executed server-side (agents/MCP/etc). Clients must not provide outputs.
23    ServerAgents,
24}
25
26#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
27pub enum ToolCallApproval {
28    Unspecified,
29    Pending,
30    Approved,
31    AutoApproved,
32    Rejected,
33}
34
35#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
36pub struct ModelConfig {
37    #[serde(default, skip_serializing_if = "Option::is_none")]
38    pub temperature: Option<f32>,
39    #[serde(default, skip_serializing_if = "Option::is_none")]
40    pub top_p: Option<f32>,
41    #[serde(default, skip_serializing_if = "Option::is_none")]
42    pub presence_penalty: Option<f32>,
43    #[serde(default, skip_serializing_if = "Option::is_none")]
44    pub frequency_penalty: Option<f32>,
45    #[serde(default, skip_serializing_if = "Option::is_none")]
46    pub max_tokens: Option<i32>,
47    #[serde(default, skip_serializing_if = "Option::is_none")]
48    pub reasoning_effort: Option<String>,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
52pub struct ThreadModelOverride {
53    pub model_id: Uuid,
54    pub model_config: ModelConfig,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct ToolCallOutput {
59    /// Tool call id
60    pub id: String,
61
62    pub is_error: bool,
63    pub output: String,
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    pub duration_seconds: Option<i32>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub enum ClientMessage {
70    HelloMath {
71        /// Random per-client-instance id (generated on client startup).
72        ///
73        /// Used for multi-client tool-call claiming/coordination. Not derived from auth token.
74        client_instance_id: String,
75        /// Client version.
76        version: String,
77        /// Minimum supported server version (the client can work with any server >= this).
78        min_supported_version: String,
79    },
80    /// Send a user message.
81    ///
82    /// If `thread_id` is `None`, the server creates a new thread and returns it in `SendMessageAck`.
83    /// `request_id` is a client-generated correlation id for 1:1 request↔response mapping.
84    SendMessage {
85        request_id: Uuid,
86        thread_id: Option<Uuid>,
87        text: String,
88    },
89    /// Update/refresh auth token without reconnecting the WebSocket.
90    UpdateAuthToken {
91        token: String,
92    },
93    /// Update the client's workspace roots / default root (used for prompt context + validations).
94    ///
95    /// Sent separately from `HelloMath` so roots can be updated without reconnecting.
96    UpdateWorkspaceRoots {
97        default_root: String,
98        workspace_roots: Vec<String>,
99    },
100    /// Set runtime mode for a thread.
101    SetRuntimeMode {
102        thread_id: Uuid,
103        mode: RuntimeMode,
104    },
105    /// Set model override for a thread (`None` clears override).
106    SetThreadModel {
107        thread_id: Uuid,
108        #[serde(default, skip_serializing_if = "Option::is_none")]
109        model_override: Option<ThreadModelOverride>,
110    },
111    RejectToolCall {
112        id: String,
113        #[serde(default, skip_serializing_if = "Option::is_none")]
114        reason: Option<String>,
115    },
116    AcceptToolCall {
117        id: String,
118    },
119    ToolCallOutputs {
120        outputs: Vec<ToolCallOutput>,
121    },
122    /// Cancel the current in-progress generation for a specific assistant message.
123    CancelGeneration {
124        message_id: Uuid,
125    },
126}
127
128#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
129pub struct Usage {
130    pub input_tokens: i32,
131    pub output_tokens: i32,
132    #[serde(default)]
133    pub cache_read_input_tokens: i32,
134    #[serde(default)]
135    pub cache_creation_input_tokens: i32,
136    #[serde(default)]
137    pub cache_creation_input_tokens_5m: i32,
138    #[serde(default)]
139    pub cache_creation_input_tokens_1h: i32,
140}
141
142#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
143pub enum MessageStatus {
144    Completed,
145    /// The agent is waiting for the client/user to provide more input (e.g. tool outputs / approvals)
146    /// before it can continue generation.
147    WaitingForUser,
148    Failed,
149    Cancelled,
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize)]
153pub enum ServerMessage {
154    HelloMagic {
155        version: String,
156        min_supported_version: String,
157    },
158    VersionMismatch {
159        server_version: String,
160        server_min_supported_version: String,
161    },
162    Goodbye {
163        reconnect: bool,
164    },
165    SendMessageAck {
166        request_id: Uuid,
167        thread_id: Uuid,
168        user_message_id: Uuid,
169    },
170    AuthUpdated,
171    RuntimeModeUpdated {
172        thread_id: Uuid,
173        mode: RuntimeMode,
174        #[serde(default, skip_serializing_if = "Option::is_none")]
175        changed_by_client_instance_id: Option<String>,
176    },
177    ThreadModelUpdated {
178        thread_id: Uuid,
179        #[serde(default, skip_serializing_if = "Option::is_none")]
180        model_override: Option<ThreadModelOverride>,
181        #[serde(default, skip_serializing_if = "Option::is_none")]
182        changed_by_client_instance_id: Option<String>,
183    },
184    MessageHeader {
185        message_id: Uuid,
186        thread_id: Uuid,
187        role: Role,
188        #[serde(default, skip_serializing_if = "Option::is_none")]
189        request_id: Option<Uuid>,
190    },
191    ReasoningDelta {
192        message_id: Uuid,
193        content: String,
194    },
195    TextDelta {
196        message_id: Uuid,
197        content: String,
198    },
199    ToolCallHeader {
200        message_id: Uuid,
201        tool_call_id: String,
202        name: String,
203        execution_target: ToolExecutionTarget,
204        approval: ToolCallApproval,
205    },
206    ToolCallArgumentsDelta {
207        message_id: Uuid,
208        tool_call_id: String,
209        delta: String,
210    },
211    ToolCall {
212        message_id: Uuid,
213        tool_call_id: String,
214        args: Value,
215    },
216    ToolCallResult {
217        message_id: Uuid,
218        tool_call_id: String,
219        is_error: bool,
220        output: String,
221        #[serde(default, skip_serializing_if = "Option::is_none")]
222        duration_seconds: Option<i32>,
223    },
224    ToolCallClaimed {
225        message_id: Uuid,
226        tool_call_id: String,
227        claimed_by_client_instance_id: String,
228    },
229    ToolCallApprovalUpdated {
230        message_id: Uuid,
231        tool_call_id: String,
232        approval: ToolCallApproval,
233    },
234    MessageDone {
235        message_id: Uuid,
236        #[serde(default, skip_serializing_if = "Option::is_none")]
237        usage: Option<Usage>,
238        status: MessageStatus,
239    },
240    Error {
241        #[serde(default, skip_serializing_if = "Option::is_none")]
242        request_id: Option<Uuid>,
243        #[serde(default, skip_serializing_if = "Option::is_none")]
244        message_id: Option<Uuid>,
245        code: String,
246        message: String,
247    },
248}