artifactize_core/inspect/
session.rs1mod send;
3use crate::{
4 agent::session::{self, Conversation, SessionRef, Summary},
5 store::{self, Producer, RequestView},
6};
7pub use send::{Sent, send};
8use serde_json::{Value, json};
9use std::path::{Path, PathBuf};
10pub struct Located {
12 pub request: RequestView,
14 pub reused_by: Option<crate::types::RequestId>,
16 pub id: crate::types::SessionId,
17 pub reference: Option<SessionRef>,
19 pub path: PathBuf,
20}
21
22impl Located {
23 pub fn load(&self) -> Result<Conversation, String> {
25 Conversation::load(&self.path)?.ok_or_else(|| {
26 let request = &self.request.request.id;
27 if self.reference.is_some() {
28 format!(
29 "Session {} of request {request} was removed by the session GC (limits.json agentSessions).",
30 self.id
31 )
32 } else {
33 format!(
34 "Session {} of request {request} was not saved: saving was off (limits.json agentSessions.enabled), or the review never started.",
35 self.id
36 )
37 }
38 })
39 }
40}
41
42fn here(state: &Path, reference: &SessionRef) -> Result<(), String> {
44 let producer = Producer::current().name;
45 let state_id = store::read_state_id(state)?;
46 if reference.producer == producer
47 && Some(reference.state.as_str()) == state_id.as_ref().map(crate::types::StateId::as_str)
48 {
49 return Ok(());
50 }
51 Err(format!(
52 "Session {} lives in {}'s state {}; this is {producer}'s state {}. Read or continue it there.",
53 reference.session_id,
54 reference.producer,
55 reference.state,
56 state_id.as_deref().unwrap_or("(not created yet)")
57 ))
58}
59
60pub async fn locate(state: &Path, text: &str) -> Result<Located, String> {
62 let mut view = if let Some(reference) = SessionRef::parse(text) {
63 here(state, &reference)?;
64 let view = store::read_request(state, &reference.request_id)
65 .await
66 .map_err(|_| format!("Request {} is not in this state.", reference.request_id))?;
67 if view.request.session_id.as_ref() != Some(&reference.session_id) {
68 return Err(format!(
69 "Request {} did not run session {}.",
70 reference.request_id, reference.session_id
71 ));
72 }
73 view
74 } else {
75 match store::read_request(state, text).await {
76 Ok(view) => view,
77 Err(_) => store::read_session_request(state, text)
78 .await?
79 .ok_or_else(|| {
80 format!(
81 "No Agent request or session {text} in this state ({}).",
82 crate::platform::path_text(state)
83 )
84 })?,
85 }
86 };
87 let mut reused_by = None;
88 if view.request.session_id.is_none() {
89 let Some(reference) = view.request.session.clone() else {
91 return Err(format!(
92 "Request {} ran no Agent review and names no saved conversation.",
93 view.request.id
94 ));
95 };
96 here(state, &reference)?;
97 reused_by = Some(view.request.id.clone());
98 view = store::read_request(state, &reference.request_id)
99 .await
100 .map_err(|_| format!("Request {} is not in this state.", reference.request_id))?;
101 }
102 let id = view
103 .request
104 .session_id
105 .clone()
106 .ok_or_else(|| format!("Request {} ran no Agent review.", view.request.id))?;
107 Ok(Located {
108 path: session::path(state, &id)?,
109 reference: view.request.session.clone(),
110 reused_by,
111 id,
112 request: view,
113 })
114}
115
116pub async fn show(state: &Path, text: &str, summary: bool) -> Result<Value, String> {
118 let located = locate(state, text).await?;
119 let conversation = located.load()?;
120 if summary {
121 let mut value = json!(Summary::new(&conversation));
122 value["reference"] = json!(located.reference.as_ref().map(ToString::to_string));
123 value["sessionId"] = json!(located.id);
124 value["requestId"] = json!(located.request.request.id);
125 Ok(value)
126 } else {
127 Ok(
128 json!({"reference": located.reference.as_ref().map(ToString::to_string),
129 "file": crate::platform::path_text(&conversation.path), "events": conversation.wire_events}),
130 )
131 }
132}