Skip to main content

lash_core/runtime/
observation.rs

1use std::sync::Arc;
2
3use arc_swap::ArcSwap;
4use tokio::sync::Mutex;
5
6use super::{BackgroundTaskHost, BackgroundTaskRecord, LashRuntime};
7
8#[derive(Clone)]
9pub struct RuntimeObservation {
10    pub session_id: Arc<str>,
11    pub policy: crate::SessionPolicy,
12    pub read_view: crate::SessionReadView,
13    pub persisted_state: super::PersistedSessionState,
14    pub usage_report: super::SessionUsageReport,
15    pub tool_state: Option<crate::ToolState>,
16    pub tool_catalog: Arc<Vec<serde_json::Value>>,
17    pub runtime_scope_id: Arc<str>,
18    pub background_task_host: Option<Arc<dyn BackgroundTaskHost>>,
19}
20
21impl RuntimeObservation {
22    fn from_runtime(runtime: &LashRuntime) -> Self {
23        Self {
24            session_id: Arc::from(runtime.session_id()),
25            policy: runtime.read_view().policy().clone(),
26            read_view: runtime.read_view(),
27            persisted_state: runtime.export_persisted_state(),
28            usage_report: runtime.usage_report(),
29            tool_state: runtime.tool_state().ok(),
30            tool_catalog: runtime.active_tool_catalog_shared(),
31            runtime_scope_id: Arc::clone(&runtime.runtime_scope_id),
32            background_task_host: runtime.host.background_task_host.clone(),
33        }
34    }
35
36    pub fn session_id(&self) -> &str {
37        &self.session_id
38    }
39
40    pub fn background_scope_key(&self) -> String {
41        format!("{}:{}", self.runtime_scope_id, self.session_id)
42    }
43
44    pub async fn list_background_tasks(&self) -> Vec<BackgroundTaskRecord> {
45        let Some(executor) = self.background_task_host.as_ref() else {
46            return Vec::new();
47        };
48        executor.list_managed(&self.background_scope_key()).await
49    }
50}
51
52#[derive(Clone)]
53pub struct RuntimeHandle {
54    pub(in crate::runtime) runtime: Arc<Mutex<LashRuntime>>,
55    observation: Arc<ArcSwap<RuntimeObservation>>,
56}
57
58impl RuntimeHandle {
59    pub fn new(runtime: LashRuntime) -> Self {
60        let observation = RuntimeObservation::from_runtime(&runtime);
61        Self {
62            runtime: Arc::new(Mutex::new(runtime)),
63            observation: Arc::new(ArcSwap::from_pointee(observation)),
64        }
65    }
66
67    pub fn writer(&self) -> Arc<Mutex<LashRuntime>> {
68        Arc::clone(&self.runtime)
69    }
70
71    pub fn observe(&self) -> Arc<RuntimeObservation> {
72        self.observation.load_full()
73    }
74
75    pub fn publish_from(&self, runtime: &LashRuntime) {
76        self.observation
77            .store(Arc::new(RuntimeObservation::from_runtime(runtime)));
78    }
79
80    pub fn try_into_runtime(self) -> Result<LashRuntime, Self> {
81        match Arc::try_unwrap(self.runtime) {
82            Ok(mutex) => Ok(mutex.into_inner()),
83            Err(runtime) => Err(Self {
84                runtime,
85                observation: self.observation,
86            }),
87        }
88    }
89}