1use serde::{Deserialize, Serialize};
2
3use super::*;
4
5#[async_trait::async_trait]
6pub trait SessionSnapshotHost: Send + Sync {
7 async fn snapshot_current(&self) -> Result<SessionSnapshot, PluginError>;
8 async fn snapshot_session(&self, session_id: &str) -> Result<SessionSnapshot, PluginError>;
9}
10
11#[async_trait::async_trait]
12pub trait ToolCatalogHost: Send + Sync {
13 async fn tool_catalog(&self, session_id: &str) -> Result<Vec<serde_json::Value>, PluginError>;
14}
15
16#[async_trait::async_trait]
17pub trait ToolStateHost: Send + Sync {
18 async fn tool_state(&self, _session_id: &str) -> Result<crate::ToolState, PluginError> {
19 Err(PluginError::Session(
20 "tool state is unavailable in this session".to_string(),
21 ))
22 }
23 async fn apply_tool_state(
24 &self,
25 _session_id: &str,
26 _snapshot: crate::ToolState,
27 ) -> Result<u64, PluginError> {
28 Err(PluginError::Session(
29 "tool state mutation is unavailable in this session".to_string(),
30 ))
31 }
32 async fn set_tools_availability(
33 &self,
34 session_id: &str,
35 tool_names: &[String],
36 availability: Option<crate::ToolAvailability>,
37 ) -> Result<u64, PluginError> {
38 let mut snapshot = self.tool_state(session_id).await?;
39 for name in tool_names {
40 snapshot
41 .set_availability(name, availability)
42 .map_err(|err| PluginError::Session(err.to_string()))?;
43 }
44 self.apply_tool_state(session_id, snapshot).await
45 }
46 async fn set_tool_availability(
47 &self,
48 session_id: &str,
49 tool_name: &str,
50 availability: Option<ToolAvailability>,
51 ) -> Result<u64, PluginError> {
52 let mut snapshot = self.tool_state(session_id).await?;
53 snapshot
54 .set_availability(tool_name, availability)
55 .map_err(|err| PluginError::Session(err.to_string()))?;
56 self.apply_tool_state(session_id, snapshot).await
57 }
58}
59
60#[async_trait::async_trait]
61pub trait SessionLifecycleHost: Send + Sync {
62 async fn create_session(
63 &self,
64 request: SessionCreateRequest,
65 ) -> Result<SessionHandle, PluginError>;
66 async fn take_first_turn_input(
72 &self,
73 _session_id: &str,
74 ) -> Result<Option<PluginMessage>, PluginError> {
75 Ok(None)
76 }
77 async fn close_session(&self, session_id: &str) -> Result<(), PluginError>;
78}
79
80#[async_trait::async_trait]
81pub trait TurnHost: Send + Sync {
82 async fn start_turn_stream(
83 &self,
84 session_id: &str,
85 input: TurnInput,
86 ) -> Result<SessionTurnHandle, PluginError>;
87 async fn await_turn(&self, turn_id: &str) -> Result<AssembledTurn, PluginError>;
88 async fn cancel_turn(&self, turn_id: &str) -> Result<(), PluginError>;
89 async fn start_turn(
90 &self,
91 session_id: &str,
92 input: TurnInput,
93 ) -> Result<AssembledTurn, PluginError> {
94 let handle = self.start_turn_stream(session_id, input).await?;
95 drop(handle.events);
96 self.await_turn(&handle.turn_id).await
97 }
98}
99
100#[async_trait::async_trait]
101pub trait TaskHost: Send + Sync {
102 async fn inject_turn_input(
109 &self,
110 _session_id: &str,
111 _input: crate::InjectedTurnInput,
112 ) -> Result<(), PluginError> {
113 Err(PluginError::Session(
114 "turn input injection is unavailable in this session".to_string(),
115 ))
116 }
117 async fn spawn_hidden_task(
118 &self,
119 _session_id: &str,
120 _label: &str,
121 _task: PluginSessionTask,
122 ) -> Result<(), PluginError> {
123 Err(PluginError::Session(
124 "background tasks are unavailable in this session".to_string(),
125 ))
126 }
127 async fn await_hidden_tasks(&self, _session_id: &str) -> Result<(), PluginError> {
128 Ok(())
129 }
130 async fn spawn_managed_task(
131 &self,
132 _session_id: &str,
133 _spec: crate::BackgroundTaskRegistration,
134 _task: PluginSessionTask,
135 ) -> Result<(), PluginError> {
136 Err(PluginError::Session(
137 "managed background tasks are unavailable in this session".to_string(),
138 ))
139 }
140 async fn cancel_managed_task(
141 &self,
142 _session_id: &str,
143 _task_id: &str,
144 ) -> Result<(), PluginError> {
145 Err(PluginError::Session(
146 "managed background tasks are unavailable in this session".to_string(),
147 ))
148 }
149 async fn register_background_task(
150 &self,
151 _session_id: &str,
152 _spec: crate::BackgroundTaskRegistration,
153 _cancel: Option<crate::LocalBackgroundTaskCancel>,
154 ) -> Result<(), PluginError> {
155 Err(PluginError::Session(
156 "background task registry is unavailable in this session".to_string(),
157 ))
158 }
159 async fn unregister_background_task(&self, _session_id: &str, _task_id: &str) {}
160 async fn complete_background_task(
161 &self,
162 _session_id: &str,
163 _task_id: &str,
164 _state: crate::BackgroundTaskState,
165 ) {
166 }
167 async fn transition_background_task_live_state(
172 &self,
173 _session_id: &str,
174 _task_id: &str,
175 _state: crate::BackgroundTaskState,
176 ) {
177 }
178 async fn list_background_tasks(
179 &self,
180 _session_id: &str,
181 ) -> Result<Vec<crate::BackgroundTaskRecord>, PluginError> {
182 Err(PluginError::Session(
183 "background task registry is unavailable in this session".to_string(),
184 ))
185 }
186 async fn cancel_background_task(
190 &self,
191 _session_id: &str,
192 _task_id: &str,
193 ) -> Result<crate::BackgroundTaskRecord, PluginError> {
194 Err(PluginError::Session(
195 "background task registry is unavailable in this session".to_string(),
196 ))
197 }
198 async fn cancel_all_background_tasks(
199 &self,
200 session_id: &str,
201 ) -> Result<Vec<crate::BackgroundTaskRecord>, PluginError> {
202 let tasks = self.list_background_tasks(session_id).await?;
203 let mut cancelled = Vec::new();
204 for task in tasks {
205 if task.state.is_terminal() {
206 continue;
207 }
208 cancelled.push(self.cancel_background_task(session_id, &task.id).await?);
209 }
210 Ok(cancelled)
211 }
212 async fn validate_async_handles_visible(
213 &self,
214 _session_id: &str,
215 _handle_ids: &[String],
216 ) -> Result<(), PluginError> {
217 Ok(())
218 }
219 async fn transfer_async_handles(
220 &self,
221 _from_session_id: &str,
222 _to_session_id: &str,
223 _handle_ids: &[String],
224 ) -> Result<(), PluginError> {
225 Ok(())
226 }
227 async fn cancel_unreferenced_async_handles(
228 &self,
229 _session_id: &str,
230 _keep_handle_ids: &[String],
231 ) -> Result<Vec<crate::BackgroundTaskRecord>, PluginError> {
232 Ok(Vec::new())
233 }
234}
235
236#[async_trait::async_trait]
237pub trait MonitorHost: Send + Sync {
238 async fn monitor_snapshot(&self, _session_id: &str) -> Result<MonitorSnapshot, PluginError> {
239 Err(PluginError::Session(
240 "monitors are unavailable in this session".to_string(),
241 ))
242 }
243 async fn take_monitor_updates(
244 &self,
245 _session_id: &str,
246 ) -> Result<MonitorUpdateBatch, PluginError> {
247 Err(PluginError::Session(
248 "monitors are unavailable in this session".to_string(),
249 ))
250 }
251 async fn start_monitor(
252 &self,
253 _session_id: &str,
254 _spec: MonitorSpec,
255 ) -> Result<MonitorSnapshot, PluginError> {
256 Err(PluginError::Session(
257 "monitors are unavailable in this session".to_string(),
258 ))
259 }
260 async fn stop_monitor(
261 &self,
262 _session_id: &str,
263 _monitor_id: &str,
264 ) -> Result<MonitorSnapshot, PluginError> {
265 Err(PluginError::Session(
266 "monitors are unavailable in this session".to_string(),
267 ))
268 }
269}
270
271#[async_trait::async_trait]
272pub trait SessionGraphHost: Send + Sync {
273 async fn append_session_nodes(
274 &self,
275 _session_id: &str,
276 _request: AppendSessionNodesRequest,
277 ) -> Result<AppendSessionNodesResult, PluginError> {
278 Err(PluginError::Session(
279 "session graph mutation is unavailable in this session".to_string(),
280 ))
281 }
282}
283
284#[async_trait::async_trait]
285pub trait DirectCompletionHost: Send + Sync {
286 async fn direct_completion(
292 &self,
293 _request: crate::DirectRequest,
294 _usage_source: &str,
295 ) -> Result<DirectCompletion, PluginError> {
296 Err(PluginError::Session(
297 "direct completions are unavailable in this session".to_string(),
298 ))
299 }
300
301 async fn direct_llm_completion(
302 &self,
303 _request: crate::LlmRequest,
304 _usage_source: &str,
305 ) -> Result<DirectLlmCompletion, PluginError> {
306 Err(PluginError::Session(
307 "direct LLM completions are unavailable in this session".to_string(),
308 ))
309 }
310}
311
312#[async_trait::async_trait]
313pub trait TraceHost: Send + Sync {
314 async fn emit_trace_event(
315 &self,
316 _context: lash_trace::TraceContext,
317 _event: lash_trace::TraceEvent,
318 ) -> Result<(), PluginError> {
319 Ok(())
320 }
321}
322
323pub trait PromptHookHost:
324 SessionSnapshotHost + ToolCatalogHost + TaskHost + DirectCompletionHost
325{
326}
327impl<T> PromptHookHost for T where
328 T: SessionSnapshotHost + ToolCatalogHost + TaskHost + DirectCompletionHost + ?Sized
329{
330}
331
332pub trait TurnHookHost:
333 SessionSnapshotHost + ToolStateHost + SessionLifecycleHost + TraceHost
334{
335}
336impl<T> TurnHookHost for T where
337 T: SessionSnapshotHost + ToolStateHost + SessionLifecycleHost + TraceHost + ?Sized
338{
339}
340
341pub trait ToolHookHost:
342 SessionSnapshotHost
343 + ToolCatalogHost
344 + ToolStateHost
345 + SessionLifecycleHost
346 + TurnHost
347 + TaskHost
348 + MonitorHost
349 + SessionGraphHost
350 + DirectCompletionHost
351 + TraceHost
352 + TurnResultHookHost
353 + CheckpointHookHost
354{
355}
356impl<T> ToolHookHost for T where
357 T: SessionSnapshotHost
358 + ToolCatalogHost
359 + ToolStateHost
360 + SessionLifecycleHost
361 + TurnHost
362 + TaskHost
363 + MonitorHost
364 + SessionGraphHost
365 + DirectCompletionHost
366 + TraceHost
367 + ?Sized
368{
369}
370
371pub trait TurnResultHookHost: SessionLifecycleHost + TraceHost {}
372impl<T> TurnResultHookHost for T where T: SessionLifecycleHost + TraceHost + ?Sized {}
373
374pub trait CheckpointHookHost: SessionLifecycleHost + TraceHost {}
375impl<T> CheckpointHookHost for T where T: SessionLifecycleHost + TraceHost + ?Sized {}
376
377pub trait HistoryHost:
378 SessionSnapshotHost
379 + SessionLifecycleHost
380 + TurnHost
381 + TaskHost
382 + SessionGraphHost
383 + DirectCompletionHost
384{
385}
386impl<T> HistoryHost for T where
387 T: SessionSnapshotHost
388 + SessionLifecycleHost
389 + TurnHost
390 + TaskHost
391 + SessionGraphHost
392 + DirectCompletionHost
393 + ?Sized
394{
395}
396
397pub trait PluginActionHost:
398 SessionSnapshotHost
399 + ToolCatalogHost
400 + ToolStateHost
401 + SessionLifecycleHost
402 + TurnHost
403 + TaskHost
404 + MonitorHost
405 + SessionGraphHost
406 + DirectCompletionHost
407 + TraceHost
408{
409}
410impl<T> PluginActionHost for T where
411 T: SessionSnapshotHost
412 + ToolCatalogHost
413 + ToolStateHost
414 + SessionLifecycleHost
415 + TurnHost
416 + TaskHost
417 + MonitorHost
418 + SessionGraphHost
419 + DirectCompletionHost
420 + TraceHost
421 + ?Sized
422{
423}
424
425pub trait RuntimeSessionHost:
426 PluginActionHost + ToolHookHost + HistoryHost + TurnHookHost + PromptHookHost
427{
428}
429impl<T> RuntimeSessionHost for T where
430 T: PluginActionHost + ToolHookHost + HistoryHost + TurnHookHost + PromptHookHost + ?Sized
431{
432}
433
434#[derive(Clone, Debug)]
437pub struct DirectCompletion {
438 pub text: String,
439 pub usage: crate::TokenUsage,
440}
441
442#[derive(Clone, Debug)]
443pub struct DirectLlmCompletion {
444 pub response: crate::LlmResponse,
445 pub usage: crate::TokenUsage,
446}
447
448#[derive(Clone, Debug, Serialize, Deserialize)]
449pub struct AppendSessionNodesRequest {
450 pub nodes: Vec<SessionAppendNode>,
451 #[serde(default)]
452 pub requires_ancestor_node_id: Option<String>,
453}
454
455#[derive(Clone, Debug, Serialize, Deserialize)]
456#[serde(tag = "status", rename_all = "snake_case")]
457pub enum AppendSessionNodesResult {
458 Appended {
459 node_ids: Vec<String>,
460 leaf_node_id: String,
461 },
462 StaleBranch {
463 current_leaf_node_id: Option<String>,
464 },
465}