1use crate::event::EventEnvelope;
4use crate::id::SessionId;
5use aios_protocol::HiveTaskId;
6
7#[derive(Debug, Clone)]
9pub struct HiveTask {
10 pub hive_task_id: HiveTaskId,
11 pub objective: String,
12 pub agent_sessions: Vec<SessionId>,
13 pub current_generation: u32,
14 pub best_score: Option<f32>,
15 pub best_session_id: Option<SessionId>,
16 pub completed: bool,
17}
18
19impl HiveTask {
20 pub fn from_events(events: &[EventEnvelope]) -> Option<Self> {
25 let mut task: Option<Self> = None;
26
27 for envelope in events {
28 match &envelope.payload {
29 aios_protocol::EventKind::HiveTaskCreated {
30 hive_task_id,
31 objective,
32 ..
33 } => {
34 task = Some(Self {
35 hive_task_id: hive_task_id.clone(),
36 objective: objective.clone(),
37 agent_sessions: Vec::new(),
38 current_generation: 0,
39 best_score: None,
40 best_session_id: None,
41 completed: false,
42 });
43 }
44 aios_protocol::EventKind::HiveArtifactShared {
45 source_session_id,
46 score,
47 ..
48 } => {
49 if let Some(ref mut t) = task {
50 let sid = SessionId::from_string(source_session_id.as_str());
51 if !t.agent_sessions.iter().any(|s| s.as_str() == sid.as_str()) {
52 t.agent_sessions.push(sid);
53 }
54 if t.best_score.is_none_or(|best| *score > best) {
55 t.best_score = Some(*score);
56 t.best_session_id =
57 Some(SessionId::from_string(source_session_id.as_str()));
58 }
59 }
60 }
61 aios_protocol::EventKind::HiveSelectionMade {
62 winning_session_id,
63 winning_score,
64 generation,
65 ..
66 } => {
67 if let Some(ref mut t) = task {
68 t.current_generation = *generation;
69 t.best_score = Some(*winning_score);
70 t.best_session_id =
71 Some(SessionId::from_string(winning_session_id.as_str()));
72 }
73 }
74 aios_protocol::EventKind::HiveGenerationCompleted { generation, .. } => {
75 if let Some(ref mut t) = task {
76 t.current_generation = *generation;
77 }
78 }
79 aios_protocol::EventKind::HiveTaskCompleted { .. } => {
80 if let Some(ref mut t) = task {
81 t.completed = true;
82 }
83 }
84 _ => {}
85 }
86 }
87
88 task
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use crate::event::{EventEnvelope, EventPayload};
96 use crate::id::{BranchId, EventId};
97 use std::collections::HashMap;
98
99 fn make_envelope(payload: EventPayload) -> EventEnvelope {
100 EventEnvelope {
101 event_id: EventId::from_string("E1"),
102 session_id: SessionId::from_string("S1"),
103 branch_id: BranchId::from_string("main"),
104 run_id: None,
105 seq: 1,
106 timestamp: 100,
107 parent_id: None,
108 payload,
109 metadata: HashMap::new(),
110 schema_version: 1,
111 }
112 }
113
114 #[test]
115 fn from_events_empty() {
116 assert!(HiveTask::from_events(&[]).is_none());
117 }
118
119 #[test]
120 fn from_events_full_lifecycle() {
121 let events = vec![
122 make_envelope(EventPayload::HiveTaskCreated {
123 hive_task_id: HiveTaskId::from_string("H1"),
124 objective: "optimize".into(),
125 agent_count: 3,
126 }),
127 make_envelope(EventPayload::HiveArtifactShared {
128 hive_task_id: HiveTaskId::from_string("H1"),
129 source_session_id: aios_protocol::SessionId::from_string("SA"),
130 score: 0.8,
131 mutation_summary: "first try".into(),
132 }),
133 make_envelope(EventPayload::HiveArtifactShared {
134 hive_task_id: HiveTaskId::from_string("H1"),
135 source_session_id: aios_protocol::SessionId::from_string("SB"),
136 score: 0.9,
137 mutation_summary: "better".into(),
138 }),
139 make_envelope(EventPayload::HiveSelectionMade {
140 hive_task_id: HiveTaskId::from_string("H1"),
141 winning_session_id: aios_protocol::SessionId::from_string("SB"),
142 winning_score: 0.9,
143 generation: 1,
144 }),
145 make_envelope(EventPayload::HiveTaskCompleted {
146 hive_task_id: HiveTaskId::from_string("H1"),
147 total_generations: 1,
148 total_trials: 6,
149 final_score: 0.95,
150 }),
151 ];
152
153 let task = HiveTask::from_events(&events).unwrap();
154 assert_eq!(task.hive_task_id.as_str(), "H1");
155 assert_eq!(task.objective, "optimize");
156 assert_eq!(task.agent_sessions.len(), 2);
157 assert_eq!(task.current_generation, 1);
158 assert_eq!(task.best_score, Some(0.9));
159 assert_eq!(task.best_session_id.as_ref().unwrap().as_str(), "SB");
160 assert!(task.completed);
161 }
162
163 #[test]
164 fn from_events_no_task_created() {
165 let events = vec![make_envelope(EventPayload::HiveArtifactShared {
166 hive_task_id: HiveTaskId::from_string("H1"),
167 source_session_id: aios_protocol::SessionId::from_string("SA"),
168 score: 0.8,
169 mutation_summary: "orphan".into(),
170 })];
171 assert!(HiveTask::from_events(&events).is_none());
172 }
173}