use async_trait::async_trait;
use crate::agent::AgentRunError;
use crate::agent::assembly::AgentHarness;
pub use crate::agent::session::session::{
SESSION_GRAPH_STATE_CUSTOM_TYPE, SessionGraphState, SubagentJobSnapshot,
};
use crate::agent::session::session::{Session, SessionTreeEntry};
use super::graph::engine::DagEngine;
use super::graph::persist::to_persisted;
use super::jobs::SubagentJobRegistry;
pub trait IntoSessionId {
fn into_session_id(self) -> Option<String>;
}
impl IntoSessionId for Option<&str> {
fn into_session_id(self) -> Option<String> {
self.map(str::to_string)
}
}
impl IntoSessionId for &str {
fn into_session_id(self) -> Option<String> {
Some(self.to_string())
}
}
impl IntoSessionId for Option<String> {
fn into_session_id(self) -> Option<String> {
self
}
}
impl IntoSessionId for String {
fn into_session_id(self) -> Option<String> {
Some(self)
}
}
impl IntoSessionId for &String {
fn into_session_id(self) -> Option<String> {
Some(self.clone())
}
}
pub fn snapshot_for_session<S: IntoSessionId>(
engine: &DagEngine,
jobs: &SubagentJobRegistry,
session_id: S,
) -> SessionGraphState {
let session_id = session_id.into_session_id();
let session_id = session_id.as_deref();
let dags = engine
.list_runs()
.into_iter()
.filter(|run| run.session_id.as_deref() == session_id)
.map(|run| to_persisted(&run))
.collect();
let subagents = jobs.snapshot_for_session(session_id);
SessionGraphState { dags, subagents }
}
#[async_trait]
pub trait CollapseMaterialSource {
async fn compact_text(&self) -> String;
async fn raw_text_ref(&self) -> String;
async fn subagent_graph(&self) -> SessionGraphState;
}
#[async_trait]
impl CollapseMaterialSource for Session {
async fn compact_text(&self) -> String {
self.latest_collapse_summary()
.await
.ok()
.flatten()
.unwrap_or_default()
}
async fn raw_text_ref(&self) -> String {
self.session_id().await.ok().flatten().unwrap_or_default()
}
async fn subagent_graph(&self) -> SessionGraphState {
self.session_graph_state()
.await
.ok()
.flatten()
.unwrap_or_default()
}
}
#[async_trait]
impl CollapseMaterialSource for SessionGraphState {
async fn compact_text(&self) -> String {
String::new()
}
async fn raw_text_ref(&self) -> String {
String::new()
}
async fn subagent_graph(&self) -> SessionGraphState {
self.clone()
}
}
#[async_trait]
impl CollapseMaterialSource for &Session {
async fn compact_text(&self) -> String {
(**self)
.latest_collapse_summary()
.await
.ok()
.flatten()
.unwrap_or_default()
}
async fn raw_text_ref(&self) -> String {
(**self)
.session_id()
.await
.ok()
.flatten()
.unwrap_or_default()
}
async fn subagent_graph(&self) -> SessionGraphState {
(**self)
.session_graph_state()
.await
.ok()
.flatten()
.unwrap_or_default()
}
}
#[async_trait]
impl CollapseMaterialSource for &SessionGraphState {
async fn compact_text(&self) -> String {
String::new()
}
async fn raw_text_ref(&self) -> String {
String::new()
}
async fn subagent_graph(&self) -> SessionGraphState {
(*self).clone()
}
}
pub async fn collapse_material<S: CollapseMaterialSource>(
source: S,
) -> (String, String, SessionGraphState) {
(
source.compact_text().await,
source.raw_text_ref().await,
source.subagent_graph().await,
)
}
pub async fn collapse_material_with_compaction(
harness: &AgentHarness,
custom_instructions: Option<String>,
) -> Result<(String, String, SessionGraphState), AgentRunError> {
harness.force_compact(custom_instructions).await?;
Ok(collapse_material(harness.session()).await)
}
pub fn attach_runs<S: IntoSessionId, T: IntoSessionId>(
engine: &DagEngine,
jobs: &SubagentJobRegistry,
from_session: S,
to_session: T,
) -> usize {
let from = from_session.into_session_id();
let to = to_session.into_session_id();
let run_count = match (&from, &to) {
(Some(from), Some(to)) => engine.rehome_runs(from, to),
_ => 0,
};
let job_ids: Vec<String> = jobs
.list()
.into_iter()
.filter(|job| job.session_id == from)
.map(|job| job.id)
.collect();
for job_id in &job_ids {
jobs.update(job_id, |job| {
if job.session_id == from {
job.session_id = to.clone();
}
});
}
run_count + job_ids.len()
}
pub fn graph_state_from_entries(entries: &[SessionTreeEntry]) -> Option<SessionGraphState> {
crate::agent::session::session::latest_session_graph_state(entries)
}
#[cfg(test)]
tests_bridge_macro::tests_bridge!("multiagent/session_graph");