1use std::sync::Arc;
10use std::sync::atomic::Ordering;
11
12use serde::{Deserialize, Serialize};
13
14use super::ChildAgent;
15use crate::events::{ClientEvent, OinoEvent};
16use crate::session::{AgentSession, Inner};
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(rename_all = "lowercase")]
21pub enum AgentRunState {
22 Running,
24 Idle,
26 Hibernated,
29 Closed,
31}
32
33#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
35#[serde(rename_all = "camelCase")]
36pub struct AgentInfo {
37 pub id: String,
39 #[serde(default, skip_serializing_if = "Option::is_none")]
41 pub parent_id: Option<String>,
42 pub label: String,
44 pub owner: String,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
48 pub role: Option<String>,
49 pub state: AgentRunState,
51 pub depth: u8,
53 #[serde(default, skip_serializing_if = "Option::is_none")]
55 pub session_file: Option<String>,
56}
57
58pub(crate) fn root_of(inner: &Arc<Inner>) -> Arc<Inner> {
60 let mut current = Arc::clone(inner);
61 loop {
62 let parent = current
63 .agents
64 .link
65 .as_ref()
66 .and_then(|link| link.parent.upgrade());
67 match parent {
68 Some(parent) => current = parent,
69 None => return current,
70 }
71 }
72}
73
74impl ChildAgent {
75 #[must_use]
77 pub fn run_state(&self) -> AgentRunState {
78 if self.is_closed() {
79 AgentRunState::Closed
80 } else if self.is_hibernated() {
81 AgentRunState::Hibernated
82 } else if self.session().is_some_and(|session| session.is_idle()) {
83 AgentRunState::Idle
84 } else {
85 AgentRunState::Running
86 }
87 }
88
89 fn info(&self, parent_id: Option<&str>) -> AgentInfo {
90 AgentInfo {
91 id: self.id().to_owned(),
92 parent_id: parent_id.map(str::to_owned),
93 label: self.label().to_owned(),
94 owner: self.owner().to_owned(),
95 role: self.role().map(str::to_owned),
96 state: self.run_state(),
97 depth: self.depth(),
98 session_file: self.session_file().map(|p| p.display().to_string()),
99 }
100 }
101}
102
103fn collect(session: &AgentSession, parent_id: Option<&str>, out: &mut Vec<AgentInfo>) {
104 for child in session.child_agents() {
105 out.push(child.info(parent_id));
106 if let Some(live) = child.session() {
109 collect(&live, Some(child.id()), out);
110 }
111 }
112}
113
114impl AgentSession {
115 #[must_use]
117 pub fn agent_tree(&self) -> Vec<AgentInfo> {
118 let mut out = Vec::new();
119 collect(self, None, &mut out);
120 out
121 }
122
123 #[must_use]
125 pub fn find_agent(&self, id: &str) -> Option<Arc<ChildAgent>> {
126 for child in self.child_agents() {
127 if child.id() == id {
128 return Some(child);
129 }
130 if let Some(live) = child.session()
131 && let Some(found) = live.find_agent(id)
132 {
133 return Some(found);
134 }
135 }
136 None
137 }
138
139 pub fn follow_agents(&self, on: bool) {
142 self.inner.agents.tree.follow.store(on, Ordering::SeqCst);
143 }
144
145 #[must_use]
147 pub fn follows_agents(&self) -> bool {
148 self.inner.agents.tree.follow.load(Ordering::SeqCst)
149 }
150
151 pub(crate) fn publish_agents_changed(&self) {
153 let root = AgentSession::from_inner(root_of(&self.inner));
154 let agents = root.agent_tree();
155 root.hub().publish(OinoEvent::AgentsChanged { agents });
156 }
157}
158
159pub(crate) fn forward_to_root(parent: &Arc<Inner>, agent_id: &str, event: &ClientEvent) {
161 if !parent.agents.tree.follow.load(Ordering::SeqCst) {
162 return;
163 }
164 let root = root_of(parent);
165 root.hub.publish(OinoEvent::AgentEvent {
166 agent_id: agent_id.to_owned(),
167 event: Box::new(event.clone()),
168 });
169}
170
171pub(crate) fn changes_state(event: &ClientEvent) -> bool {
173 use crate::events::AgentSessionEvent;
174 matches!(
175 event,
176 ClientEvent::Session(
177 AgentSessionEvent::AgentStart | AgentSessionEvent::AgentSettled { .. }
178 )
179 )
180}