Skip to main content

evo_common/
messages.rs

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/// Agent reports completion of a pipeline stage back to king.
110#[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// ─── Task management messages ────────────────────────────────────────────────
132
133#[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    #[serde(default)]
141    pub parent_id: Option<String>,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct TaskUpdate {
146    pub task_id: String,
147    #[serde(default)]
148    pub status: Option<TaskStatus>,
149    #[serde(default)]
150    pub agent_id: Option<String>,
151    #[serde(default)]
152    pub payload: Option<serde_json::Value>,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct TaskGet {
157    pub task_id: String,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct TaskList {
162    #[serde(default = "default_task_limit")]
163    pub limit: u32,
164    #[serde(default)]
165    pub status: Option<TaskStatus>,
166    #[serde(default)]
167    pub agent_id: Option<String>,
168    #[serde(default)]
169    pub parent_id: Option<String>,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct TaskDelete {
174    pub task_id: String,
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct TaskRecord {
179    pub id: String,
180    pub task_type: String,
181    pub status: String,
182    pub agent_id: String,
183    pub payload: serde_json::Value,
184    #[serde(default)]
185    pub parent_id: String,
186    pub created_at: String,
187    pub updated_at: String,
188}
189
190fn default_task_limit() -> u32 {
191    50
192}
193
194fn default_memory_limit() -> u32 {
195    20
196}
197
198fn default_empty_object() -> serde_json::Value {
199    serde_json::Value::Object(serde_json::Map::new())
200}
201
202// ─── Memory system types ────────────────────────────────────────────────────
203
204#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
205#[serde(rename_all = "snake_case")]
206pub enum MemoryScope {
207    System,
208    Agent,
209    Pipeline,
210    Skill,
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
214#[serde(rename_all = "snake_case")]
215pub enum MemoryCategory {
216    Case,
217    Pattern,
218    Fact,
219    Preference,
220    Resource,
221    Event,
222}
223
224/// A single tier entry (l0/l1/l2) for memory creation/update.
225#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct MemoryTierEntry {
227    pub tier: String,
228    pub content: String,
229}
230
231/// Agent stores a memory into king.
232#[derive(Debug, Clone, Serialize, Deserialize)]
233pub struct MemoryStore {
234    pub scope: MemoryScope,
235    pub category: MemoryCategory,
236    #[serde(default)]
237    pub key: String,
238    #[serde(default = "default_empty_object")]
239    pub metadata: serde_json::Value,
240    #[serde(default)]
241    pub tags: Vec<String>,
242    #[serde(default)]
243    pub agent_id: String,
244    #[serde(default)]
245    pub run_id: String,
246    #[serde(default)]
247    pub skill_id: String,
248    #[serde(default)]
249    pub relevance_score: f64,
250    #[serde(default)]
251    pub tiers: Vec<MemoryTierEntry>,
252    #[serde(default)]
253    pub task_id: Option<String>,
254}
255
256/// Agent queries memories from king.
257#[derive(Debug, Clone, Serialize, Deserialize)]
258pub struct MemoryQuery {
259    pub query: String,
260    #[serde(default)]
261    pub scope: Option<MemoryScope>,
262    #[serde(default)]
263    pub category: Option<MemoryCategory>,
264    #[serde(default)]
265    pub agent_id: Option<String>,
266    #[serde(default)]
267    pub tier: Option<String>,
268    #[serde(default)]
269    pub task_id: Option<String>,
270    #[serde(default = "default_memory_limit")]
271    pub limit: u32,
272}
273
274/// A single tier in a returned memory record.
275#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct MemoryTierRecord {
277    pub id: String,
278    pub memory_id: String,
279    pub tier: String,
280    pub content: String,
281    pub created_at: String,
282    pub updated_at: String,
283}
284
285/// Serialized memory record returned in results.
286#[derive(Debug, Clone, Serialize, Deserialize)]
287pub struct MemoryRecord {
288    pub id: String,
289    pub scope: String,
290    pub category: String,
291    pub key: String,
292    #[serde(default)]
293    pub tiers: Vec<MemoryTierRecord>,
294    #[serde(default = "default_empty_object")]
295    pub metadata: serde_json::Value,
296    #[serde(default)]
297    pub tags: Vec<String>,
298    #[serde(default)]
299    pub agent_id: String,
300    #[serde(default)]
301    pub run_id: String,
302    #[serde(default)]
303    pub skill_id: String,
304    #[serde(default)]
305    pub relevance_score: f64,
306    #[serde(default)]
307    pub access_count: i64,
308    pub created_at: String,
309    pub updated_at: String,
310}
311
312/// King returns matching memories to an agent.
313#[derive(Debug, Clone, Serialize, Deserialize)]
314pub struct MemoryResult {
315    pub memories: Vec<MemoryRecord>,
316    pub count: u32,
317}
318
319/// Broadcast when a memory is created, updated, or deleted.
320#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct MemoryChanged {
322    pub action: String,
323    #[serde(default)]
324    pub memory: Option<MemoryRecord>,
325    #[serde(default)]
326    pub memory_id: Option<String>,
327}
328
329// ─── Task Room messages ─────────────────────────────────────────────────────
330
331/// King invites agents to join a task room.
332#[derive(Debug, Clone, Serialize, Deserialize)]
333pub struct TaskInvite {
334    pub task_id: String,
335    pub task_type: String,
336    #[serde(default)]
337    pub payload: serde_json::Value,
338}
339
340/// King streams output data into a task room.
341#[derive(Debug, Clone, Serialize, Deserialize)]
342pub struct TaskOutput {
343    pub task_id: String,
344    pub request_id: String,
345    /// Source of output: `"pty"` or `"llm"`.
346    pub source: String,
347    pub delta: String,
348    pub chunk_index: u32,
349    #[serde(default)]
350    pub is_final: bool,
351}
352
353/// King requests evaluation of a completed task.
354#[derive(Debug, Clone, Serialize, Deserialize)]
355pub struct TaskEvaluate {
356    pub task_id: String,
357    pub task_type: String,
358    /// Accumulated output text (truncated if very large).
359    #[serde(default)]
360    pub output_summary: String,
361    #[serde(default)]
362    pub exit_code: Option<i32>,
363    #[serde(default)]
364    pub latency_ms: Option<u64>,
365    #[serde(default)]
366    pub metadata: serde_json::Value,
367}
368
369/// Evaluation agent reports a task summary.
370#[derive(Debug, Clone, Serialize, Deserialize)]
371pub struct TaskSummary {
372    pub task_id: String,
373    pub agent_id: String,
374    pub summary: String,
375    #[serde(default)]
376    pub score: Option<f64>,
377    #[serde(default)]
378    pub tags: Vec<String>,
379    #[serde(default)]
380    pub evaluation: serde_json::Value,
381}
382
383pub mod events {
384    pub const AGENT_REGISTER: &str = "agent:register";
385    pub const AGENT_STATUS: &str = "agent:status";
386    pub const AGENT_SKILL_REPORT: &str = "agent:skill_report";
387    pub const AGENT_HEALTH: &str = "agent:health";
388    pub const KING_COMMAND: &str = "king:command";
389    pub const KING_CONFIG_UPDATE: &str = "king:config_update";
390    pub const PIPELINE_NEXT: &str = "pipeline:next";
391
392    // Task management events
393    pub const TASK_CREATE: &str = "task:create";
394    pub const TASK_UPDATE: &str = "task:update";
395    pub const TASK_GET: &str = "task:get";
396    pub const TASK_LIST: &str = "task:list";
397    pub const TASK_DELETE: &str = "task:delete";
398    pub const TASK_CHANGED: &str = "task:changed";
399
400    // Pipeline coordination events
401    pub const PIPELINE_STAGE_RESULT: &str = "pipeline:stage_result";
402
403    // Debug events
404    pub const DEBUG_PROMPT: &str = "debug:prompt";
405    pub const DEBUG_RESPONSE: &str = "debug:response";
406    pub const DEBUG_STREAM: &str = "debug:stream";
407
408    // Memory events
409    pub const MEMORY_STORE: &str = "memory:store";
410    pub const MEMORY_QUERY: &str = "memory:query";
411    pub const MEMORY_UPDATE: &str = "memory:update";
412    pub const MEMORY_DELETE: &str = "memory:delete";
413    pub const MEMORY_CHANGED: &str = "memory:changed";
414
415    // Task Room events
416    pub const TASK_INVITE: &str = "task:invite";
417    pub const TASK_JOIN: &str = "task:join";
418    pub const TASK_OUTPUT: &str = "task:output";
419    pub const TASK_EVALUATE: &str = "task:evaluate";
420    pub const TASK_SUMMARY: &str = "task:summary";
421    pub const TASK_LOG: &str = "task:log";
422
423    // System info events
424    pub const KING_SYSTEM_INFO: &str = "king:system_info";
425
426    // Rooms
427    pub const ROOM_KERNEL: &str = "kernel";
428    pub const ROOM_ROLE_PREFIX: &str = "role:";
429    pub const ROOM_TASK_PREFIX: &str = "task:";
430}
431
432#[cfg(test)]
433mod tests {
434    use super::*;
435
436    #[test]
437    fn serialize_agent_register() {
438        let msg = AgentRegister {
439            agent_id: "learning-001".into(),
440            role: AgentRole::Learning,
441            capabilities: vec!["discover".into(), "evaluate".into()],
442        };
443        let json = serde_json::to_string(&msg).unwrap();
444        let deserialized: AgentRegister = serde_json::from_str(&json).unwrap();
445        assert_eq!(deserialized.agent_id, "learning-001");
446        assert_eq!(deserialized.role, AgentRole::Learning);
447    }
448
449    #[test]
450    fn serialize_pipeline_next() {
451        let msg = PipelineNext {
452            stage: PipelineStage::Building,
453            artifact_id: "skill-xyz".into(),
454            metadata: HashMap::new(),
455        };
456        let json = serde_json::to_string(&msg).unwrap();
457        let deserialized: PipelineNext = serde_json::from_str(&json).unwrap();
458        assert_eq!(deserialized.stage, PipelineStage::Building);
459    }
460
461    #[test]
462    fn serialize_task_status() {
463        let status = TaskStatus::InProgress;
464        let json = serde_json::to_string(&status).unwrap();
465        assert_eq!(json, r#""in_progress""#);
466        let de: TaskStatus = serde_json::from_str(&json).unwrap();
467        assert_eq!(de, TaskStatus::InProgress);
468    }
469
470    #[test]
471    fn serialize_task_create() {
472        let msg = TaskCreate {
473            task_type: "build".into(),
474            agent_id: Some("building-001".into()),
475            payload: serde_json::json!({"skill_id": "web-search"}),
476            parent_id: None,
477        };
478        let json = serde_json::to_string(&msg).unwrap();
479        let de: TaskCreate = serde_json::from_str(&json).unwrap();
480        assert_eq!(de.task_type, "build");
481        assert_eq!(de.agent_id.unwrap(), "building-001");
482    }
483
484    #[test]
485    fn deserialize_task_list_defaults() {
486        let msg: TaskList = serde_json::from_str("{}").unwrap();
487        assert_eq!(msg.limit, 50);
488        assert!(msg.status.is_none());
489        assert!(msg.agent_id.is_none());
490    }
491
492    #[test]
493    fn serialize_pipeline_run_status() {
494        let status = PipelineRunStatus::Running;
495        let json = serde_json::to_string(&status).unwrap();
496        assert_eq!(json, r#""running""#);
497        let de: PipelineRunStatus = serde_json::from_str(&json).unwrap();
498        assert_eq!(de, PipelineRunStatus::Running);
499
500        let timed_out = PipelineRunStatus::TimedOut;
501        let json = serde_json::to_string(&timed_out).unwrap();
502        assert_eq!(json, r#""timed_out""#);
503    }
504
505    #[test]
506    fn serialize_pipeline_stage_result() {
507        let result = PipelineStageResult {
508            run_id: "run-001".into(),
509            stage: PipelineStage::Learning,
510            agent_id: "learning-001".into(),
511            status: PipelineRunStatus::Completed,
512            artifact_id: "artifact-xyz".into(),
513            output: serde_json::json!({"candidates": 3}),
514            error: None,
515        };
516        let json = serde_json::to_string(&result).unwrap();
517        let de: PipelineStageResult = serde_json::from_str(&json).unwrap();
518        assert_eq!(de.run_id, "run-001");
519        assert_eq!(de.stage, PipelineStage::Learning);
520        assert_eq!(de.status, PipelineRunStatus::Completed);
521        assert!(de.error.is_none());
522    }
523
524    #[test]
525    fn serialize_pipeline_stage_result_with_error() {
526        let result = PipelineStageResult {
527            run_id: "run-002".into(),
528            stage: PipelineStage::Building,
529            agent_id: "building-001".into(),
530            status: PipelineRunStatus::Failed,
531            artifact_id: "".into(),
532            output: serde_json::Value::Null,
533            error: Some("build failed: missing dependency".into()),
534        };
535        let json = serde_json::to_string(&result).unwrap();
536        let de: PipelineStageResult = serde_json::from_str(&json).unwrap();
537        assert_eq!(de.status, PipelineRunStatus::Failed);
538        assert_eq!(de.error.unwrap(), "build failed: missing dependency");
539    }
540
541    #[test]
542    fn serialize_task_update_partial() {
543        let msg = TaskUpdate {
544            task_id: "abc-123".into(),
545            status: Some(TaskStatus::Completed),
546            agent_id: None,
547            payload: None,
548        };
549        let json = serde_json::to_string(&msg).unwrap();
550        let de: TaskUpdate = serde_json::from_str(&json).unwrap();
551        assert_eq!(de.task_id, "abc-123");
552        assert_eq!(de.status, Some(TaskStatus::Completed));
553        assert!(de.agent_id.is_none());
554    }
555
556    #[test]
557    fn deserialize_task_create_with_parent_id() {
558        let msg: TaskCreate =
559            serde_json::from_str(r#"{"task_type": "subtask", "parent_id": "abc-123"}"#).unwrap();
560        assert_eq!(msg.parent_id, Some("abc-123".to_string()));
561    }
562
563    #[test]
564    fn deserialize_task_create_without_parent_id() {
565        let msg: TaskCreate = serde_json::from_str(r#"{"task_type": "test"}"#).unwrap();
566        assert!(msg.parent_id.is_none());
567    }
568
569    #[test]
570    fn deserialize_task_list_with_parent_id() {
571        let msg: TaskList = serde_json::from_str(r#"{"parent_id": "parent-001"}"#).unwrap();
572        assert_eq!(msg.parent_id, Some("parent-001".to_string()));
573        assert_eq!(msg.limit, 50);
574    }
575
576    #[test]
577    fn serialize_memory_scope() {
578        let scope = MemoryScope::Agent;
579        let json = serde_json::to_string(&scope).unwrap();
580        assert_eq!(json, r#""agent""#);
581        let de: MemoryScope = serde_json::from_str(&json).unwrap();
582        assert_eq!(de, MemoryScope::Agent);
583    }
584
585    #[test]
586    fn serialize_memory_category() {
587        let cat = MemoryCategory::Pattern;
588        let json = serde_json::to_string(&cat).unwrap();
589        assert_eq!(json, r#""pattern""#);
590        let de: MemoryCategory = serde_json::from_str(&json).unwrap();
591        assert_eq!(de, MemoryCategory::Pattern);
592    }
593
594    #[test]
595    fn serialize_memory_store() {
596        let msg = MemoryStore {
597            scope: MemoryScope::Agent,
598            category: MemoryCategory::Pattern,
599            key: "memory://agent/learning/api_pattern".into(),
600            metadata: serde_json::json!({"source": "pipeline"}),
601            tags: vec!["discovery".into(), "api".into()],
602            agent_id: "learning-001".into(),
603            run_id: "".into(),
604            skill_id: "".into(),
605            relevance_score: 0.85,
606            tiers: vec![
607                MemoryTierEntry {
608                    tier: "l0".into(),
609                    content: "API discovery pattern".into(),
610                },
611                MemoryTierEntry {
612                    tier: "l2".into(),
613                    content: "Full detailed content...".into(),
614                },
615            ],
616            task_id: None,
617        };
618        let json = serde_json::to_string(&msg).unwrap();
619        let de: MemoryStore = serde_json::from_str(&json).unwrap();
620        assert_eq!(de.scope, MemoryScope::Agent);
621        assert_eq!(de.category, MemoryCategory::Pattern);
622        assert_eq!(de.tiers.len(), 2);
623    }
624
625    #[test]
626    fn deserialize_memory_query_defaults() {
627        let msg: MemoryQuery = serde_json::from_str(r#"{"query": "api discovery"}"#).unwrap();
628        assert_eq!(msg.limit, 20);
629        assert!(msg.scope.is_none());
630        assert!(msg.task_id.is_none());
631    }
632
633    #[test]
634    fn serialize_memory_changed() {
635        let msg = MemoryChanged {
636            action: "created".into(),
637            memory: None,
638            memory_id: Some("mem-001".into()),
639        };
640        let json = serde_json::to_string(&msg).unwrap();
641        let de: MemoryChanged = serde_json::from_str(&json).unwrap();
642        assert_eq!(de.action, "created");
643        assert_eq!(de.memory_id.unwrap(), "mem-001");
644    }
645}