mod send;
use crate::{
agent::session::{self, Conversation, SessionRef, Summary},
store::{self, Producer, RequestView},
};
pub use send::{Sent, send};
use serde_json::{Value, json};
use std::path::{Path, PathBuf};
pub struct Located {
pub request: RequestView,
pub reused_by: Option<crate::types::RequestId>,
pub id: crate::types::SessionId,
pub reference: Option<SessionRef>,
pub path: PathBuf,
}
impl Located {
pub fn load(&self) -> Result<Conversation, String> {
Conversation::load(&self.path)?.ok_or_else(|| {
let request = &self.request.request.id;
if self.reference.is_some() {
format!(
"Session {} of request {request} was removed by the session GC (limits.json agentSessions).",
self.id
)
} else {
format!(
"Session {} of request {request} was not saved: saving was off (limits.json agentSessions.enabled), or the review never started.",
self.id
)
}
})
}
}
fn here(state: &Path, reference: &SessionRef) -> Result<(), String> {
let producer = Producer::current().name;
let state_id = store::read_state_id(state)?;
if reference.producer == producer
&& Some(reference.state.as_str()) == state_id.as_ref().map(crate::types::StateId::as_str)
{
return Ok(());
}
Err(format!(
"Session {} lives in {}'s state {}; this is {producer}'s state {}. Read or continue it there.",
reference.session_id,
reference.producer,
reference.state,
state_id.as_deref().unwrap_or("(not created yet)")
))
}
pub async fn locate(state: &Path, text: &str) -> Result<Located, String> {
let mut view = if let Some(reference) = SessionRef::parse(text) {
here(state, &reference)?;
let view = store::read_request(state, &reference.request_id)
.await
.map_err(|_| format!("Request {} is not in this state.", reference.request_id))?;
if view.request.session_id.as_ref() != Some(&reference.session_id) {
return Err(format!(
"Request {} did not run session {}.",
reference.request_id, reference.session_id
));
}
view
} else {
match store::read_request(state, text).await {
Ok(view) => view,
Err(_) => store::read_session_request(state, text)
.await?
.ok_or_else(|| {
format!(
"No Agent request or session {text} in this state ({}).",
crate::platform::path_text(state)
)
})?,
}
};
let mut reused_by = None;
if view.request.session_id.is_none() {
let Some(reference) = view.request.session.clone() else {
return Err(format!(
"Request {} ran no Agent review and names no saved conversation.",
view.request.id
));
};
here(state, &reference)?;
reused_by = Some(view.request.id.clone());
view = store::read_request(state, &reference.request_id)
.await
.map_err(|_| format!("Request {} is not in this state.", reference.request_id))?;
}
let id = view
.request
.session_id
.clone()
.ok_or_else(|| format!("Request {} ran no Agent review.", view.request.id))?;
Ok(Located {
path: session::path(state, &id)?,
reference: view.request.session.clone(),
reused_by,
id,
request: view,
})
}
pub async fn show(state: &Path, text: &str, summary: bool) -> Result<Value, String> {
let located = locate(state, text).await?;
let conversation = located.load()?;
if summary {
let mut value = json!(Summary::new(&conversation));
value["reference"] = json!(located.reference.as_ref().map(ToString::to_string));
value["sessionId"] = json!(located.id);
value["requestId"] = json!(located.request.request.id);
Ok(value)
} else {
Ok(
json!({"reference": located.reference.as_ref().map(ToString::to_string),
"file": crate::platform::path_text(&conversation.path), "events": conversation.wire_events}),
)
}
}