1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct AgentRegister {
6 pub agent_id: String,
7 pub role: AgentRole,
8 pub capabilities: Vec<String>,
9}
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct AgentStatus {
13 pub agent_id: String,
14 pub status: RunnerStatus,
15 pub metrics: HashMap<String, serde_json::Value>,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct AgentSkillReport {
20 pub agent_id: String,
21 pub skill_id: String,
22 pub result: SkillResult,
23 pub score: Option<f64>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct AgentHealth {
28 pub agent_id: String,
29 pub health_checks: Vec<HealthCheck>,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct KingCommand {
34 pub command: String,
35 pub target_agent: String,
36 pub params: HashMap<String, serde_json::Value>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct KingConfigUpdate {
41 pub config_type: String,
42 pub new_config_hash: String,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct PipelineNext {
47 pub stage: PipelineStage,
48 pub artifact_id: String,
49 pub metadata: HashMap<String, serde_json::Value>,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
53#[serde(rename_all = "snake_case")]
54pub enum AgentRole {
55 SkillManage,
56 Learning,
57 PreLoad,
58 Building,
59 Evaluation,
60 User(String),
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
64#[serde(rename_all = "snake_case")]
65pub enum RunnerStatus {
66 Starting,
67 Ready,
68 Busy,
69 Error,
70 Shutting,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74#[serde(rename_all = "snake_case")]
75pub enum SkillResult {
76 Success,
77 Failure(String),
78 Partial(String),
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct HealthCheck {
83 pub name: String,
84 pub endpoint: String,
85 pub healthy: bool,
86 pub latency_ms: Option<u64>,
87 pub error: Option<String>,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
91#[serde(rename_all = "snake_case")]
92pub enum PipelineStage {
93 Learning,
94 Building,
95 PreLoad,
96 Evaluation,
97 SkillManage,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
101#[serde(rename_all = "snake_case")]
102pub enum PipelineRunStatus {
103 Running,
104 Completed,
105 Failed,
106 TimedOut,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct PipelineStageResult {
112 pub run_id: String,
113 pub stage: PipelineStage,
114 pub agent_id: String,
115 pub status: PipelineRunStatus,
116 pub artifact_id: String,
117 pub output: serde_json::Value,
118 pub error: Option<String>,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
122#[serde(rename_all = "snake_case")]
123pub enum TaskStatus {
124 Pending,
125 InProgress,
126 Completed,
127 Failed,
128 Cancelled,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct TaskCreate {
135 pub task_type: String,
136 #[serde(default)]
137 pub agent_id: Option<String>,
138 #[serde(default = "default_empty_object")]
139 pub payload: serde_json::Value,
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct TaskUpdate {
144 pub task_id: String,
145 #[serde(default)]
146 pub status: Option<TaskStatus>,
147 #[serde(default)]
148 pub agent_id: Option<String>,
149 #[serde(default)]
150 pub payload: Option<serde_json::Value>,
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct TaskGet {
155 pub task_id: String,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct TaskList {
160 #[serde(default = "default_task_limit")]
161 pub limit: u32,
162 #[serde(default)]
163 pub status: Option<TaskStatus>,
164 #[serde(default)]
165 pub agent_id: Option<String>,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct TaskDelete {
170 pub task_id: String,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct TaskRecord {
175 pub id: String,
176 pub task_type: String,
177 pub status: String,
178 pub agent_id: String,
179 pub payload: serde_json::Value,
180 pub created_at: String,
181 pub updated_at: String,
182}
183
184fn default_task_limit() -> u32 {
185 50
186}
187
188fn default_empty_object() -> serde_json::Value {
189 serde_json::Value::Object(serde_json::Map::new())
190}
191
192pub mod events {
193 pub const AGENT_REGISTER: &str = "agent:register";
194 pub const AGENT_STATUS: &str = "agent:status";
195 pub const AGENT_SKILL_REPORT: &str = "agent:skill_report";
196 pub const AGENT_HEALTH: &str = "agent:health";
197 pub const KING_COMMAND: &str = "king:command";
198 pub const KING_CONFIG_UPDATE: &str = "king:config_update";
199 pub const PIPELINE_NEXT: &str = "pipeline:next";
200
201 pub const TASK_CREATE: &str = "task:create";
203 pub const TASK_UPDATE: &str = "task:update";
204 pub const TASK_GET: &str = "task:get";
205 pub const TASK_LIST: &str = "task:list";
206 pub const TASK_DELETE: &str = "task:delete";
207 pub const TASK_CHANGED: &str = "task:changed";
208
209 pub const PIPELINE_STAGE_RESULT: &str = "pipeline:stage_result";
211
212 pub const ROOM_KERNEL: &str = "kernel";
214 pub const ROOM_ROLE_PREFIX: &str = "role:";
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn serialize_agent_register() {
223 let msg = AgentRegister {
224 agent_id: "learning-001".into(),
225 role: AgentRole::Learning,
226 capabilities: vec!["discover".into(), "evaluate".into()],
227 };
228 let json = serde_json::to_string(&msg).unwrap();
229 let deserialized: AgentRegister = serde_json::from_str(&json).unwrap();
230 assert_eq!(deserialized.agent_id, "learning-001");
231 assert_eq!(deserialized.role, AgentRole::Learning);
232 }
233
234 #[test]
235 fn serialize_pipeline_next() {
236 let msg = PipelineNext {
237 stage: PipelineStage::Building,
238 artifact_id: "skill-xyz".into(),
239 metadata: HashMap::new(),
240 };
241 let json = serde_json::to_string(&msg).unwrap();
242 let deserialized: PipelineNext = serde_json::from_str(&json).unwrap();
243 assert_eq!(deserialized.stage, PipelineStage::Building);
244 }
245
246 #[test]
247 fn serialize_task_status() {
248 let status = TaskStatus::InProgress;
249 let json = serde_json::to_string(&status).unwrap();
250 assert_eq!(json, r#""in_progress""#);
251 let de: TaskStatus = serde_json::from_str(&json).unwrap();
252 assert_eq!(de, TaskStatus::InProgress);
253 }
254
255 #[test]
256 fn serialize_task_create() {
257 let msg = TaskCreate {
258 task_type: "build".into(),
259 agent_id: Some("building-001".into()),
260 payload: serde_json::json!({"skill_id": "web-search"}),
261 };
262 let json = serde_json::to_string(&msg).unwrap();
263 let de: TaskCreate = serde_json::from_str(&json).unwrap();
264 assert_eq!(de.task_type, "build");
265 assert_eq!(de.agent_id.unwrap(), "building-001");
266 }
267
268 #[test]
269 fn deserialize_task_list_defaults() {
270 let msg: TaskList = serde_json::from_str("{}").unwrap();
271 assert_eq!(msg.limit, 50);
272 assert!(msg.status.is_none());
273 assert!(msg.agent_id.is_none());
274 }
275
276 #[test]
277 fn serialize_pipeline_run_status() {
278 let status = PipelineRunStatus::Running;
279 let json = serde_json::to_string(&status).unwrap();
280 assert_eq!(json, r#""running""#);
281 let de: PipelineRunStatus = serde_json::from_str(&json).unwrap();
282 assert_eq!(de, PipelineRunStatus::Running);
283
284 let timed_out = PipelineRunStatus::TimedOut;
285 let json = serde_json::to_string(&timed_out).unwrap();
286 assert_eq!(json, r#""timed_out""#);
287 }
288
289 #[test]
290 fn serialize_pipeline_stage_result() {
291 let result = PipelineStageResult {
292 run_id: "run-001".into(),
293 stage: PipelineStage::Learning,
294 agent_id: "learning-001".into(),
295 status: PipelineRunStatus::Completed,
296 artifact_id: "artifact-xyz".into(),
297 output: serde_json::json!({"candidates": 3}),
298 error: None,
299 };
300 let json = serde_json::to_string(&result).unwrap();
301 let de: PipelineStageResult = serde_json::from_str(&json).unwrap();
302 assert_eq!(de.run_id, "run-001");
303 assert_eq!(de.stage, PipelineStage::Learning);
304 assert_eq!(de.status, PipelineRunStatus::Completed);
305 assert!(de.error.is_none());
306 }
307
308 #[test]
309 fn serialize_pipeline_stage_result_with_error() {
310 let result = PipelineStageResult {
311 run_id: "run-002".into(),
312 stage: PipelineStage::Building,
313 agent_id: "building-001".into(),
314 status: PipelineRunStatus::Failed,
315 artifact_id: "".into(),
316 output: serde_json::Value::Null,
317 error: Some("build failed: missing dependency".into()),
318 };
319 let json = serde_json::to_string(&result).unwrap();
320 let de: PipelineStageResult = serde_json::from_str(&json).unwrap();
321 assert_eq!(de.status, PipelineRunStatus::Failed);
322 assert_eq!(de.error.unwrap(), "build failed: missing dependency");
323 }
324
325 #[test]
326 fn serialize_task_update_partial() {
327 let msg = TaskUpdate {
328 task_id: "abc-123".into(),
329 status: Some(TaskStatus::Completed),
330 agent_id: None,
331 payload: None,
332 };
333 let json = serde_json::to_string(&msg).unwrap();
334 let de: TaskUpdate = serde_json::from_str(&json).unwrap();
335 assert_eq!(de.task_id, "abc-123");
336 assert_eq!(de.status, Some(TaskStatus::Completed));
337 assert!(de.agent_id.is_none());
338 }
339}