use std::sync::Arc;
use std::sync::atomic::Ordering;
use serde::{Deserialize, Serialize};
use super::ChildAgent;
use crate::events::{ClientEvent, OinoEvent};
use crate::session::{AgentSession, Inner};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AgentRunState {
Running,
Idle,
Hibernated,
Closed,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AgentInfo {
pub id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parent_id: Option<String>,
pub label: String,
pub owner: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub role: Option<String>,
pub state: AgentRunState,
pub depth: u8,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session_file: Option<String>,
}
pub(crate) fn root_of(inner: &Arc<Inner>) -> Arc<Inner> {
let mut current = Arc::clone(inner);
loop {
let parent = current
.agents
.link
.as_ref()
.and_then(|link| link.parent.upgrade());
match parent {
Some(parent) => current = parent,
None => return current,
}
}
}
impl ChildAgent {
#[must_use]
pub fn run_state(&self) -> AgentRunState {
if self.is_closed() {
AgentRunState::Closed
} else if self.is_hibernated() {
AgentRunState::Hibernated
} else if self.session().is_some_and(|session| session.is_idle()) {
AgentRunState::Idle
} else {
AgentRunState::Running
}
}
fn info(&self, parent_id: Option<&str>) -> AgentInfo {
AgentInfo {
id: self.id().to_owned(),
parent_id: parent_id.map(str::to_owned),
label: self.label().to_owned(),
owner: self.owner().to_owned(),
role: self.role().map(str::to_owned),
state: self.run_state(),
depth: self.depth(),
session_file: self.session_file().map(|p| p.display().to_string()),
}
}
}
fn collect(session: &AgentSession, parent_id: Option<&str>, out: &mut Vec<AgentInfo>) {
for child in session.child_agents() {
out.push(child.info(parent_id));
if let Some(live) = child.session() {
collect(&live, Some(child.id()), out);
}
}
}
impl AgentSession {
#[must_use]
pub fn agent_tree(&self) -> Vec<AgentInfo> {
let mut out = Vec::new();
collect(self, None, &mut out);
out
}
#[must_use]
pub fn find_agent(&self, id: &str) -> Option<Arc<ChildAgent>> {
for child in self.child_agents() {
if child.id() == id {
return Some(child);
}
if let Some(live) = child.session()
&& let Some(found) = live.find_agent(id)
{
return Some(found);
}
}
None
}
pub fn follow_agents(&self, on: bool) {
self.inner.agents.tree.follow.store(on, Ordering::SeqCst);
}
#[must_use]
pub fn follows_agents(&self) -> bool {
self.inner.agents.tree.follow.load(Ordering::SeqCst)
}
pub(crate) fn publish_agents_changed(&self) {
let root = AgentSession::from_inner(root_of(&self.inner));
let agents = root.agent_tree();
root.hub().publish(OinoEvent::AgentsChanged { agents });
}
}
pub(crate) fn forward_to_root(parent: &Arc<Inner>, agent_id: &str, event: &ClientEvent) {
if !parent.agents.tree.follow.load(Ordering::SeqCst) {
return;
}
let root = root_of(parent);
root.hub.publish(OinoEvent::AgentEvent {
agent_id: agent_id.to_owned(),
event: Box::new(event.clone()),
});
}
pub(crate) fn changes_state(event: &ClientEvent) -> bool {
use crate::events::AgentSessionEvent;
matches!(
event,
ClientEvent::Session(
AgentSessionEvent::AgentStart | AgentSessionEvent::AgentSettled { .. }
)
)
}