1pub use crate::session::SessionConfigPatch;
2use crate::support::*;
3pub use lash_core::{AcceptedInjectedTurnInput, PluginAction};
4
5#[derive(Clone)]
6pub struct HostEventsControl {
7 pub(crate) core: LashCore,
8}
9
10impl HostEventsControl {
11 pub async fn emit(
12 &self,
13 request: lash_core::HostEventOccurrenceRequest,
14 scoped_effect_controller: ScopedEffectController<'_>,
15 ) -> Result<lash_core::HostEventEmitReport> {
16 let store = self.core.env.host_event_store.as_ref().ok_or_else(|| {
17 EmbedError::Plugin(lash_core::PluginError::Session(
18 "host event store is unavailable in this runtime".to_string(),
19 ))
20 })?;
21 let process_work_poke = self.core.process_work_runner.poke().await;
22 let router = lash_core::HostEventRouter::new(
23 Arc::clone(store),
24 self.core.env.process_registry.clone(),
25 process_work_poke,
26 self.core.env.core.profile.host_profile_id.clone(),
27 );
28 router
29 .emit(request, scoped_effect_controller.controller())
30 .await
31 .map_err(Into::into)
32 }
33
34 pub async fn subscriptions(
35 &self,
36 filter: lash_core::TriggerSubscriptionFilter,
37 ) -> Result<Vec<lash_core::TriggerRegistration>> {
38 let store = self.core.env.host_event_store.as_ref().ok_or_else(|| {
39 EmbedError::Plugin(lash_core::PluginError::Session(
40 "host event store is unavailable in this runtime".to_string(),
41 ))
42 })?;
43 let records = store.list_subscriptions(filter).await?;
44 Ok(records
45 .iter()
46 .map(lash_core::TriggerRegistration::from)
47 .collect())
48 }
49}
50
51#[derive(Clone)]
52pub struct SessionControl {
53 pub(crate) runtime: RuntimeHandle,
54}
55
56impl SessionControl {
57 pub fn config(&self) -> ConfigControl {
58 ConfigControl {
59 control: self.clone(),
60 }
61 }
62
63 pub fn tools(&self) -> ToolsControl {
64 ToolsControl {
65 control: self.clone(),
66 }
67 }
68
69 pub fn commands(&self) -> SessionCommandsControl {
70 SessionCommandsControl {
71 control: self.clone(),
72 }
73 }
74
75 pub fn triggers(&self) -> TriggersControl {
76 TriggersControl {
77 control: self.clone(),
78 }
79 }
80
81 pub fn state(&self) -> StateControl {
82 StateControl {
83 control: self.clone(),
84 }
85 }
86
87 pub fn children(&self) -> ChildrenControl {
88 ChildrenControl {
89 control: self.clone(),
90 }
91 }
92
93 pub fn injection(&self) -> InjectionControl {
94 InjectionControl {
95 control: self.clone(),
96 }
97 }
98
99 pub fn mode(&self) -> ModeControl {
100 ModeControl {
101 control: self.clone(),
102 }
103 }
104
105 pub fn processes(&self) -> ProcessControl {
106 ProcessControl {
107 control: self.clone(),
108 }
109 }
110
111 async fn with_writer<F, T>(&self, f: F) -> T
116 where
117 F: AsyncFnOnce(&mut LashRuntime) -> T,
118 {
119 let writer = self.runtime.writer();
120 let mut runtime = writer.lock().await;
121 let value = f(&mut runtime).await;
122 self.runtime.publish_from(&runtime);
123 value
124 }
125
126 async fn update_config(&self, patch: SessionConfigPatch) -> Result<()> {
127 self.update_session_config(patch.provider, patch.model, patch.prompt)
128 .await?;
129 Ok(())
130 }
131
132 async fn update_session_config(
133 &self,
134 provider: Option<ProviderHandle>,
135 model: Option<lash_core::ModelSpec>,
136 prompt: Option<PromptLayer>,
137 ) -> Result<()> {
138 self.with_writer(async |runtime: &mut LashRuntime| {
139 runtime.update_session_config(provider, model, prompt).await;
140 })
141 .await;
142 Ok(())
143 }
144
145 async fn export_state(&self) -> lash_core::SessionSnapshot {
146 self.runtime.observe().read_view.to_snapshot()
147 }
148
149 async fn append_messages(&self, messages: Vec<PluginMessage>) -> Result<()> {
150 self.with_writer(async |runtime: &mut LashRuntime| {
151 runtime
152 .append_session_nodes(lash_core::AppendSessionNodesRequest {
153 nodes: messages
154 .into_iter()
155 .map(lash_core::SessionAppendNode::message)
156 .collect(),
157 requires_ancestor_node_id: None,
158 })
159 .await
160 .map(|_| ())
161 .map_err(Into::into)
162 })
163 .await
164 }
165
166 async fn append_plugin_body(
167 &self,
168 plugin_type: impl Into<String>,
169 body: serde_json::Value,
170 ) -> Result<()> {
171 self.with_writer(async |runtime: &mut LashRuntime| {
172 runtime
173 .append_session_nodes(lash_core::AppendSessionNodesRequest {
174 nodes: vec![lash_core::SessionAppendNode::plugin(plugin_type, body)],
175 requires_ancestor_node_id: None,
176 })
177 .await
178 .map(|_| ())
179 .map_err(Into::into)
180 })
181 .await
182 }
183
184 async fn set_persisted_state(&self, state: RuntimeSessionState) -> Result<()> {
185 self.with_writer(async |runtime: &mut LashRuntime| {
186 runtime.set_persisted_state(state).map_err(Into::into)
187 })
188 .await
189 }
190
191 async fn set_prompt_template(&self, template: PromptTemplate) -> Result<()> {
192 self.with_writer(async |runtime: &mut LashRuntime| {
193 runtime.set_prompt_template(template).await;
194 })
195 .await;
196 Ok(())
197 }
198
199 async fn clear_prompt_template(&self) -> Result<()> {
200 self.with_writer(async |runtime: &mut LashRuntime| {
201 runtime.clear_prompt_template().await;
202 })
203 .await;
204 Ok(())
205 }
206
207 async fn add_prompt_contribution(&self, contribution: PromptContribution) -> Result<()> {
208 self.with_writer(async |runtime: &mut LashRuntime| {
209 runtime.add_prompt_contribution(contribution).await;
210 })
211 .await;
212 Ok(())
213 }
214
215 async fn replace_prompt_slot(
216 &self,
217 slot: PromptSlot,
218 contributions: impl IntoIterator<Item = PromptContribution>,
219 ) -> Result<()> {
220 self.with_writer(async |runtime: &mut LashRuntime| {
221 runtime.replace_prompt_slot(slot, contributions).await;
222 })
223 .await;
224 Ok(())
225 }
226
227 async fn clear_prompt_slot(&self, slot: PromptSlot) -> Result<()> {
228 self.with_writer(async |runtime: &mut LashRuntime| {
229 runtime.clear_prompt_slot(slot).await;
230 })
231 .await;
232 Ok(())
233 }
234
235 async fn apply_protocol_session_extension(
236 &self,
237 extension: lash_core::ProtocolSessionExtensionHandle,
238 ) -> Result<()> {
239 self.with_writer(async |runtime: &mut LashRuntime| {
240 runtime
241 .apply_protocol_session_extension(extension)
242 .await
243 .map_err(Into::into)
244 })
245 .await
246 }
247
248 async fn branch_to_node(
249 &self,
250 target_leaf: Option<String>,
251 ) -> Result<lash_core::SessionSnapshot> {
252 self.with_writer(async |runtime: &mut LashRuntime| {
253 runtime
254 .branch_to_node(target_leaf)
255 .await
256 .map_err(Into::into)
257 })
258 .await
259 }
260
261 async fn await_background_work(&self) -> Result<()> {
262 self.with_writer(async |runtime: &mut LashRuntime| {
263 runtime.await_background_work().await.map_err(Into::into)
264 })
265 .await
266 }
267
268 async fn refresh_tool_surface(&self) -> Result<()> {
269 self.with_writer(async |runtime: &mut LashRuntime| {
270 runtime
271 .refresh_session_tool_surface()
272 .await
273 .map_err(Into::into)
274 })
275 .await
276 }
277
278 async fn submit_session_command(
279 &self,
280 command: lash_core::SessionCommand,
281 idempotency_key: impl Into<String>,
282 ) -> Result<lash_core::SessionCommandReceipt> {
283 let idempotency_key = idempotency_key.into();
284 self.with_writer(async |runtime: &mut LashRuntime| {
285 runtime
286 .submit_session_command(command, idempotency_key)
287 .await
288 .map_err(Into::into)
289 })
290 .await
291 }
292
293 async fn list_lashlang_trigger_registrations(
294 &self,
295 ) -> Result<Vec<lash_core::TriggerRegistration>> {
296 self.with_writer(async |runtime: &mut LashRuntime| {
297 runtime
298 .list_lashlang_trigger_registrations()
299 .await
300 .map_err(Into::into)
301 })
302 .await
303 }
304
305 async fn lashlang_trigger_registrations_by_source_type(
306 &self,
307 source_type: impl Into<lash_core::TriggerSourceType>,
308 ) -> Result<Vec<lash_core::TriggerRegistration>> {
309 self.with_writer(async |runtime: &mut LashRuntime| {
310 runtime
311 .lashlang_trigger_registrations_by_source_type(source_type)
312 .await
313 .map_err(Into::into)
314 })
315 .await
316 }
317
318 async fn invoke_plugin_action(
319 &self,
320 name: &str,
321 args: serde_json::Value,
322 ) -> Result<ToolResult> {
323 let session_id = self.runtime.observe().session_id().to_string();
324 let writer = self.runtime.writer();
325 writer
326 .lock()
327 .await
328 .invoke_plugin_action(name, args, Some(session_id))
329 .await
330 .map_err(Into::into)
331 }
332
333 async fn call_plugin_action<Op: lash_core::PluginAction>(
334 &self,
335 args: Op::Args,
336 ) -> Result<Op::Output> {
337 let result = self
338 .invoke_plugin_action(
339 Op::NAME,
340 serde_json::to_value(args).map_err(|err| {
341 EmbedError::Plugin(lash_core::PluginError::Invoke(format!(
342 "invalid {} args: {err}",
343 Op::NAME
344 )))
345 })?,
346 )
347 .await?;
348 if !result.is_success() {
349 return Err(EmbedError::Plugin(lash_core::PluginError::Invoke(format!(
350 "{} failed: {}",
351 Op::NAME,
352 result.value_for_projection()
353 ))));
354 }
355 serde_json::from_value(result.into_output().value_for_projection()).map_err(|err| {
356 EmbedError::Plugin(lash_core::PluginError::Invoke(format!(
357 "invalid {} output: {err}",
358 Op::NAME
359 )))
360 })
361 }
362
363 async fn compact_context(
364 &self,
365 instructions: Option<String>,
366 scoped_effect_controller: ScopedEffectController<'_>,
367 ) -> Result<bool> {
368 self.with_writer(async |runtime: &mut LashRuntime| {
369 runtime
370 .compact_context(instructions, scoped_effect_controller)
371 .await
372 .map_err(Into::into)
373 })
374 .await
375 }
376
377 async fn persist_current_state(&self) -> Result<RuntimeSessionState> {
378 self.with_writer(async |runtime: &mut LashRuntime| {
379 runtime.await_background_work().await?;
380 Ok(runtime.export_persisted_state())
381 })
382 .await
383 }
384
385 async fn list_process_handles(&self) -> Result<Vec<lash_core::ProcessHandleSummary>> {
386 Ok(self.runtime.observe().list_process_handles().await)
387 }
388
389 async fn list_all_process_handles(&self) -> Result<Vec<lash_core::ProcessHandleSummary>> {
390 Ok(self.runtime.observe().list_all_process_handles().await)
391 }
392
393 async fn start_process(
394 &self,
395 request: lash_core::ProcessStartRequest,
396 scoped_effect_controller: ScopedEffectController<'_>,
397 ) -> Result<lash_core::ProcessHandleSummary> {
398 let writer = self.runtime.writer();
399 let runtime = writer.lock().await;
400 let session_id = runtime.session_id().to_string();
401 let processes = runtime.process_service()?;
402 let scope = lash_core::ProcessOpScope::new(scoped_effect_controller);
403 processes
404 .start_from_request(&session_id, request, scope)
405 .await
406 .map_err(Into::into)
407 }
408
409 async fn session_state_service(&self) -> Result<Arc<dyn SessionStateService>> {
410 self.runtime
411 .writer()
412 .lock()
413 .await
414 .session_state_service()
415 .map_err(Into::into)
416 }
417
418 async fn cancel_process(
419 &self,
420 process_id: &str,
421 scoped_effect_controller: ScopedEffectController<'_>,
422 ) -> Result<lash_core::ProcessCancelSummary> {
423 let writer = self.runtime.writer();
424 let runtime = writer.lock().await;
425 let session_id = runtime.session_id().to_string();
426 let processes = runtime.process_service()?;
427 let cancel_ability = runtime.process_cancel_ability();
428 let scope = lash_core::ProcessOpScope::new(scoped_effect_controller);
429 cancel_ability
430 .cancel_summary(
431 processes.as_ref(),
432 lash_core::ProcessCancelRequest::new(
433 &session_id,
434 process_id,
435 scope,
436 lash_core::ProcessCancelSource::HostApi,
437 )
438 .with_reason("requested by host API"),
439 )
440 .await
441 .map_err(Into::into)
442 }
443
444 async fn cancel_visible_processes(
445 &self,
446 scoped_effect_controller: ScopedEffectController<'_>,
447 ) -> Result<Vec<lash_core::ProcessCancelSummary>> {
448 let writer = self.runtime.writer();
449 let runtime = writer.lock().await;
450 let session_id = runtime.session_id().to_string();
451 let processes = runtime.process_service()?;
452 let cancel_ability = runtime.process_cancel_ability();
453 let scope = lash_core::ProcessOpScope::new(scoped_effect_controller);
454 cancel_ability
455 .cancel_all_visible(
456 processes.as_ref(),
457 lash_core::ProcessCancelAllRequest::new(
458 &session_id,
459 scope,
460 lash_core::ProcessCancelSource::HostApi,
461 )
462 .with_reason("requested by host API"),
463 )
464 .await
465 .map_err(Into::into)
466 }
467
468 async fn snapshot_execution_state(&self) -> Result<Option<Vec<u8>>> {
469 self.with_writer(async |runtime: &mut LashRuntime| {
470 runtime.snapshot_execution_state().await.map_err(Into::into)
471 })
472 .await
473 }
474
475 async fn restore_execution_state(&self, bytes: &[u8]) -> Result<()> {
476 self.with_writer(async |runtime: &mut LashRuntime| {
477 runtime
478 .restore_execution_state(bytes)
479 .await
480 .map_err(Into::into)
481 })
482 .await
483 }
484
485 async fn tool_state(&self) -> Result<ToolState> {
486 self.runtime.observe().tool_state.clone().ok_or_else(|| {
487 EmbedError::Session(SessionError::Protocol(
488 "runtime session not available".to_string(),
489 ))
490 })
491 }
492
493 async fn apply_tool_state(&self, state: ToolState) -> Result<u64> {
494 self.with_writer(async |runtime: &mut LashRuntime| {
495 runtime
496 .apply_tool_state(state)
497 .await
498 .map_err(EmbedError::from)
499 })
500 .await
501 }
502
503 async fn restore_tool_state(&self, state: ToolState) -> Result<u64> {
504 self.with_writer(async |runtime: &mut LashRuntime| {
505 runtime
506 .restore_tool_state(state)
507 .await
508 .map_err(EmbedError::from)
509 })
510 .await
511 }
512
513 async fn set_tool_availability(
514 &self,
515 name: &str,
516 availability: ToolAvailability,
517 ) -> Result<u64> {
518 self.set_tool_availability_many(&[(name, availability)])
519 .await
520 }
521
522 async fn set_tool_availability_many<N: AsRef<str>>(
523 &self,
524 updates: &[(N, ToolAvailability)],
525 ) -> Result<u64> {
526 let mut state = self.tool_state().await?;
527 for (name, availability) in updates {
528 state
529 .set_availability(name.as_ref(), Some(*availability))
530 .map_err(|err| EmbedError::Session(SessionError::Protocol(err.to_string())))?;
531 }
532 self.apply_tool_state(state).await
533 }
534
535 async fn clear_tool_availability_override(&self, name: &str) -> Result<u64> {
536 let mut state = self.tool_state().await?;
537 state
538 .set_availability(name, None)
539 .map_err(|err| EmbedError::Session(SessionError::Protocol(err.to_string())))?;
540 self.apply_tool_state(state).await
541 }
542
543 async fn active_tool_definitions(&self) -> Result<Vec<ToolManifest>> {
544 Ok(self.tool_state().await?.tool_manifests())
545 }
546
547 async fn add_tool_provider(&self, provider: Arc<dyn ToolProvider>) -> Result<ToolSourceHandle> {
548 let tool_registry = self.tool_registry().await?;
549 let handle = tool_registry
550 .add_tool_provider(provider)
551 .map_err(|err| EmbedError::Session(SessionError::Protocol(err.to_string())))?;
552 self.refresh_tool_surface().await?;
553 Ok(handle)
554 }
555
556 async fn remove_tool_source(&self, handle: &ToolSourceHandle) -> Result<u64> {
557 let tool_registry = self.tool_registry().await?;
558 let generation = tool_registry
559 .remove_source(handle)
560 .map_err(|err| EmbedError::Session(SessionError::Protocol(err.to_string())))?;
561 self.refresh_tool_surface().await?;
562 Ok(generation)
563 }
564
565 async fn create_child_session(&self, request: SessionCreateRequest) -> Result<SessionHandle> {
566 let writer = self.runtime.writer();
567 let runtime = writer.lock().await;
568 let lifecycle = runtime.session_lifecycle_service()?;
569 lifecycle.create_session(request).await.map_err(Into::into)
570 }
571
572 async fn close_child_session(&self, session_id: &str) -> Result<()> {
573 let writer = self.runtime.writer();
574 let runtime = writer.lock().await;
575 let lifecycle = runtime.session_lifecycle_service()?;
576 lifecycle
577 .close_session(session_id)
578 .await
579 .map_err(Into::into)
580 }
581
582 async fn activate_managed_session(&self, session_id: &str) -> Result<()> {
583 self.with_writer(async |runtime: &mut LashRuntime| {
584 runtime
585 .activate_managed_session(session_id)
586 .await
587 .map_err(Into::into)
588 })
589 .await
590 }
591
592 async fn inject_turn_input(&self, id: Option<String>, message: PluginMessage) -> Result<()> {
593 self.inject_turn_inputs(vec![lash_core::InjectedTurnInput { id, message }])
594 .await
595 }
596
597 async fn inject_turn_inputs(&self, messages: Vec<lash_core::InjectedTurnInput>) -> Result<()> {
598 for input in messages {
599 let source_key = input.id.map(|id| format!("injection:{id}"));
600 let turn_input = turn_input_from_plugin_message(input.message);
601 self.runtime
602 .enqueue_turn_input(
603 turn_input,
604 lash_core::DeliveryPolicy::EarliestSafeBoundary,
605 lash_core::SlotPolicy::Join,
606 source_key,
607 )
608 .await
609 .map(|_| ())
610 .map_err(EmbedError::Runtime)?;
611 }
612 Ok(())
613 }
614
615 async fn tool_registry(&self) -> Result<Arc<lash_core::ToolRegistry>> {
616 self.runtime
617 .writer()
618 .lock()
619 .await
620 .plugin_session()
621 .map(|session| session.tool_registry())
622 .ok_or_else(|| {
623 EmbedError::Session(SessionError::Protocol(
624 "tool registry is unavailable in this runtime session".to_string(),
625 ))
626 })
627 }
628}
629
630fn turn_input_from_plugin_message(message: PluginMessage) -> TurnInput {
631 let mut input = TurnInput::empty();
632 if !message.content.is_empty() {
633 input.items.push(InputItem::Text {
634 text: message.content,
635 });
636 }
637 for (index, bytes) in message.images.into_iter().enumerate() {
638 let id = format!("injected-image-{index}");
639 input.items.push(InputItem::ImageRef { id: id.clone() });
640 input.image_blobs.insert(id, bytes);
641 }
642 input
643}
644
645#[derive(Clone)]
646pub struct ConfigControl {
647 control: SessionControl,
648}
649
650impl ConfigControl {
651 pub async fn update(&self, patch: SessionConfigPatch) -> Result<()> {
652 self.control.update_config(patch).await
653 }
654
655 pub async fn update_session_config(
656 &self,
657 provider: Option<ProviderHandle>,
658 model: Option<lash_core::ModelSpec>,
659 prompt: Option<PromptLayer>,
660 ) -> Result<()> {
661 self.control
662 .update_session_config(provider, model, prompt)
663 .await
664 }
665
666 pub async fn set_prompt_template(&self, template: PromptTemplate) -> Result<()> {
667 self.control.set_prompt_template(template).await
668 }
669
670 pub async fn clear_prompt_template(&self) -> Result<()> {
671 self.control.clear_prompt_template().await
672 }
673
674 pub async fn add_prompt_contribution(&self, contribution: PromptContribution) -> Result<()> {
675 self.control.add_prompt_contribution(contribution).await
676 }
677
678 pub async fn replace_prompt_slot(
679 &self,
680 slot: PromptSlot,
681 contributions: impl IntoIterator<Item = PromptContribution>,
682 ) -> Result<()> {
683 self.control.replace_prompt_slot(slot, contributions).await
684 }
685
686 pub async fn clear_prompt_slot(&self, slot: PromptSlot) -> Result<()> {
687 self.control.clear_prompt_slot(slot).await
688 }
689}
690
691#[derive(Clone)]
692pub struct ToolsControl {
693 control: SessionControl,
694}
695
696impl ToolsControl {
697 pub(crate) fn new(control: SessionControl) -> Self {
698 Self { control }
699 }
700}
701
702impl ToolsControl {
703 pub async fn state(&self) -> Result<ToolState> {
704 self.control.tool_state().await
705 }
706
707 pub fn advanced(&self) -> AdvancedToolsControl {
708 AdvancedToolsControl {
709 control: self.control.clone(),
710 }
711 }
712
713 pub async fn set_availability(
714 &self,
715 name: impl AsRef<str>,
716 availability: ToolAvailability,
717 ) -> Result<u64> {
718 self.control
719 .set_tool_availability(name.as_ref(), availability)
720 .await
721 }
722
723 pub async fn set_availability_many<N: AsRef<str>>(
724 &self,
725 updates: &[(N, ToolAvailability)],
726 ) -> Result<u64> {
727 self.control.set_tool_availability_many(updates).await
728 }
729
730 pub async fn clear_availability_override(&self, name: impl AsRef<str>) -> Result<u64> {
731 self.control
732 .clear_tool_availability_override(name.as_ref())
733 .await
734 }
735
736 pub async fn active_definitions(&self) -> Result<Vec<ToolManifest>> {
737 self.control.active_tool_definitions().await
738 }
739
740 pub async fn add_provider(&self, provider: Arc<dyn ToolProvider>) -> Result<ToolSourceHandle> {
741 self.control.add_tool_provider(provider).await
742 }
743
744 pub async fn remove_source(&self, handle: &ToolSourceHandle) -> Result<u64> {
745 self.control.remove_tool_source(handle).await
746 }
747}
748
749#[derive(Clone)]
750pub struct AdvancedToolsControl {
751 control: SessionControl,
752}
753
754impl AdvancedToolsControl {
755 pub async fn apply_state(&self, state: ToolState) -> Result<u64> {
761 self.control.apply_tool_state(state).await
762 }
763
764 pub async fn restore_state(&self, state: ToolState) -> Result<u64> {
772 self.control.restore_tool_state(state).await
773 }
774}
775
776#[derive(Clone)]
777pub struct SessionCommandsControl {
778 control: SessionControl,
779}
780
781impl SessionCommandsControl {
782 pub async fn refresh_tool_surface(
783 &self,
784 reason: impl Into<String>,
785 expected_generation: Option<u64>,
786 idempotency_key: impl Into<String>,
787 ) -> Result<lash_core::SessionCommandReceipt> {
788 self.control
789 .submit_session_command(
790 lash_core::SessionCommand::RefreshToolSurface {
791 reason: reason.into(),
792 expected_generation,
793 },
794 idempotency_key,
795 )
796 .await
797 }
798
799 pub async fn reset(
800 &self,
801 reason: impl Into<String>,
802 idempotency_key: impl Into<String>,
803 ) -> Result<lash_core::SessionCommandReceipt> {
804 self.control
805 .submit_session_command(
806 lash_core::SessionCommand::ResetSession {
807 reason: reason.into(),
808 },
809 idempotency_key,
810 )
811 .await
812 }
813}
814
815#[derive(Clone)]
817pub struct TriggersControl {
818 control: SessionControl,
819}
820
821impl TriggersControl {
822 pub async fn list_all(&self) -> Result<Vec<lash_core::TriggerRegistration>> {
828 self.control.list_lashlang_trigger_registrations().await
829 }
830
831 pub async fn by_source_type(
836 &self,
837 source_type: impl Into<lash_core::TriggerSourceType>,
838 ) -> Result<Vec<lash_core::TriggerRegistration>> {
839 self.control
840 .lashlang_trigger_registrations_by_source_type(source_type)
841 .await
842 }
843}
844
845#[derive(Clone)]
846pub struct ProcessControl {
847 control: SessionControl,
848}
849
850impl ProcessControl {
851 pub(crate) fn new(control: SessionControl) -> Self {
852 Self { control }
853 }
854
855 pub async fn start(
856 &self,
857 request: lash_core::ProcessStartRequest,
858 scoped_effect_controller: ScopedEffectController<'_>,
859 ) -> Result<lash_core::ProcessHandleSummary> {
860 self.control
861 .start_process(request, scoped_effect_controller)
862 .await
863 }
864
865 pub async fn list(&self) -> Result<Vec<lash_core::ProcessHandleSummary>> {
866 self.control.list_process_handles().await
867 }
868
869 pub async fn list_all(&self) -> Result<Vec<lash_core::ProcessHandleSummary>> {
870 self.control.list_all_process_handles().await
871 }
872
873 pub async fn await_all(&self) -> Result<()> {
874 self.control.await_background_work().await
875 }
876
877 pub async fn cancel(
878 &self,
879 process_id: &str,
880 scoped_effect_controller: ScopedEffectController<'_>,
881 ) -> Result<lash_core::ProcessCancelSummary> {
882 self.control
883 .cancel_process(process_id, scoped_effect_controller)
884 .await
885 }
886
887 pub async fn cancel_all(
888 &self,
889 scoped_effect_controller: ScopedEffectController<'_>,
890 ) -> Result<Vec<lash_core::ProcessCancelSummary>> {
891 self.control
892 .cancel_visible_processes(scoped_effect_controller)
893 .await
894 }
895}
896
897#[derive(Clone)]
898pub struct StateControl {
899 control: SessionControl,
900}
901
902impl StateControl {
903 pub async fn export(&self) -> lash_core::SessionSnapshot {
904 self.control.export_state().await
905 }
906
907 pub async fn append_messages(&self, messages: Vec<PluginMessage>) -> Result<()> {
908 self.control.append_messages(messages).await
909 }
910
911 pub async fn append_plugin_body(
912 &self,
913 plugin_type: impl Into<String>,
914 body: serde_json::Value,
915 ) -> Result<()> {
916 self.control.append_plugin_body(plugin_type, body).await
917 }
918
919 pub async fn set_persisted(&self, state: RuntimeSessionState) -> Result<()> {
920 self.control.set_persisted_state(state).await
921 }
922
923 pub async fn branch_to_node(
924 &self,
925 target_leaf: Option<String>,
926 ) -> Result<lash_core::SessionSnapshot> {
927 self.control.branch_to_node(target_leaf).await
928 }
929
930 pub async fn persist_current(&self) -> Result<RuntimeSessionState> {
931 self.control.persist_current_state().await
932 }
933
934 pub async fn session_state_service(&self) -> Result<Arc<dyn SessionStateService>> {
935 self.control.session_state_service().await
936 }
937
938 pub async fn snapshot_execution(&self) -> Result<Option<Vec<u8>>> {
939 self.control.snapshot_execution_state().await
940 }
941
942 pub async fn restore_execution(&self, bytes: &[u8]) -> Result<()> {
943 self.control.restore_execution_state(bytes).await
944 }
945
946 pub async fn compact_context(
947 &self,
948 instructions: Option<String>,
949 scoped_effect_controller: ScopedEffectController<'_>,
950 ) -> Result<bool> {
951 self.control
952 .compact_context(instructions, scoped_effect_controller)
953 .await
954 }
955}
956
957#[derive(Clone)]
958pub struct PluginActions {
959 pub(crate) control: SessionControl,
960}
961
962impl PluginActions {
963 pub async fn call<Op: lash_core::PluginAction>(&self, args: Op::Args) -> Result<Op::Output> {
964 self.control.call_plugin_action::<Op>(args).await
965 }
966}
967
968#[derive(Clone)]
969pub struct ChildrenControl {
970 control: SessionControl,
971}
972
973impl ChildrenControl {
974 pub async fn create_session(&self, request: SessionCreateRequest) -> Result<SessionHandle> {
975 self.control.create_child_session(request).await
976 }
977
978 pub async fn close_session(&self, session_id: &str) -> Result<()> {
979 self.control.close_child_session(session_id).await
980 }
981
982 pub async fn activate_managed_session(&self, session_id: &str) -> Result<()> {
983 self.control.activate_managed_session(session_id).await
984 }
985}
986
987#[derive(Clone)]
988pub struct InjectionControl {
989 control: SessionControl,
990}
991
992impl InjectionControl {
993 pub async fn inject_turn_input(
994 &self,
995 id: Option<String>,
996 message: PluginMessage,
997 ) -> Result<()> {
998 self.control.inject_turn_input(id, message).await
999 }
1000
1001 pub async fn inject_turn_inputs(
1002 &self,
1003 messages: Vec<lash_core::InjectedTurnInput>,
1004 ) -> Result<()> {
1005 self.control.inject_turn_inputs(messages).await
1006 }
1007}
1008
1009#[derive(Clone)]
1010pub struct ModeControl {
1011 control: SessionControl,
1012}
1013
1014impl ModeControl {
1015 pub async fn apply_session_extension(
1016 &self,
1017 extension: lash_core::ProtocolSessionExtensionHandle,
1018 ) -> Result<()> {
1019 self.control
1020 .apply_protocol_session_extension(extension)
1021 .await
1022 }
1023}