Skip to main content

lash_core/plugin/
surface.rs

1use lash_sansio::ToolCallOutput;
2use serde::{Deserialize, Serialize};
3use tokio::sync::mpsc;
4
5use super::*;
6
7#[derive(Clone)]
8pub struct ToolSurfaceContext {
9    pub session_id: String,
10    pub tools: Vec<ToolManifest>,
11    pub resolve_contract: Option<lash_sansio::ToolContractResolver>,
12    pub tool_access: SessionToolAccess,
13    pub subagent: Option<SubagentSessionContext>,
14    pub lashlang_abilities: lashlang::LashlangAbilities,
15}
16
17#[derive(Clone, Debug)]
18pub struct ToolDiscoveryContext {
19    pub session_id: String,
20    pub catalog: Vec<serde_json::Value>,
21}
22
23#[derive(Clone, Debug, Default, Serialize, Deserialize)]
24pub struct ToolDiscoveryContribution {
25    pub tools: Vec<ToolDiscoveryToolContribution>,
26}
27
28#[derive(Clone, Debug, Default, Serialize, Deserialize)]
29pub struct ToolDiscoveryToolContribution {
30    pub tool_name: String,
31    #[serde(default, skip_serializing_if = "Option::is_none")]
32    pub namespace: Option<String>,
33    #[serde(default, skip_serializing_if = "Vec::is_empty")]
34    pub aliases: Vec<String>,
35}
36
37#[derive(Clone, Debug)]
38pub struct PluginAbort {
39    pub code: String,
40    pub message: String,
41}
42
43#[derive(Clone, Debug, Default)]
44pub struct TurnPreparation {
45    pub messages: crate::MessageSequence,
46    pub events: Vec<crate::SessionEvent>,
47    pub abort: Option<PluginAbort>,
48}
49
50#[derive(Clone)]
51pub struct PrepareTurnRequest {
52    pub session_id: String,
53    pub state: SessionReadView,
54    pub messages: crate::MessageSequence,
55    pub sessions: Arc<dyn SessionStateService>,
56    pub session_lifecycle: Arc<dyn SessionLifecycleService>,
57    pub session_graph: Arc<dyn SessionGraphService>,
58    pub turn_context: crate::TurnContext,
59}
60
61#[derive(Clone, Debug, Default)]
62pub struct CheckpointApplication {
63    pub messages: Vec<PluginMessage>,
64    pub events: Vec<crate::SessionEvent>,
65    pub abort: Option<PluginAbort>,
66}
67
68#[derive(Clone, Debug)]
69pub struct TurnFinalization {
70    pub turn: AssembledTurn,
71    pub events: Vec<crate::SessionEvent>,
72}
73
74pub(crate) async fn emit_plugin_runtime_events(
75    event_tx: &mpsc::Sender<crate::SessionEvent>,
76    plugin_id: &str,
77    events: Vec<PluginRuntimeEvent>,
78) {
79    for event in plugin_runtime_session_events(plugin_id, events) {
80        crate::session_model::send_event(event_tx, event).await;
81    }
82}
83
84pub(crate) fn plugin_runtime_session_events(
85    plugin_id: &str,
86    events: Vec<PluginRuntimeEvent>,
87) -> Vec<crate::SessionEvent> {
88    events
89        .into_iter()
90        .map(|event| crate::SessionEvent::PluginEvent {
91            plugin_id: plugin_id.to_string(),
92            event,
93        })
94        .collect()
95}
96
97#[derive(Clone, Debug, Serialize, Deserialize)]
98#[serde(tag = "kind", rename_all = "snake_case")]
99#[allow(clippy::large_enum_variant)]
100pub enum PluginDirective {
101    AbortTurn {
102        code: String,
103        message: String,
104    },
105    EnqueueMessages {
106        messages: Vec<PluginMessage>,
107    },
108    CreateSession {
109        request: Box<SessionCreateRequest>,
110    },
111    ReplaceToolArgs {
112        args: serde_json::Value,
113    },
114    ShortCircuitTool {
115        output: ToolCallOutput,
116    },
117    EmitRuntimeEvents {
118        events: Vec<PluginRuntimeEvent>,
119    },
120    EmitTrace {
121        name: String,
122        #[serde(default)]
123        payload: serde_json::Value,
124        #[serde(default)]
125        context: Box<lash_trace::TraceContext>,
126    },
127}
128
129impl PluginDirective {
130    pub fn short_circuit(result: ToolResult) -> Self {
131        Self::ShortCircuitTool {
132            output: result.into_output(),
133        }
134    }
135
136    pub fn into_tool_result(self) -> Option<ToolResult> {
137        match self {
138            Self::ShortCircuitTool { output } => Some(ToolResult::from_output(output)),
139            _ => None,
140        }
141    }
142
143    pub fn emit_runtime_events(events: Vec<PluginRuntimeEvent>) -> Self {
144        Self::EmitRuntimeEvents { events }
145    }
146
147    pub fn emit_trace(name: impl Into<String>, payload: serde_json::Value) -> Self {
148        Self::EmitTrace {
149            name: name.into(),
150            payload,
151            context: Box::new(lash_trace::TraceContext::default()),
152        }
153    }
154}