1pub mod attachments;
2pub mod chronological;
3pub mod direct;
4pub mod host_events;
5pub mod lashlang_bridge;
6pub mod llm;
7mod model;
8pub mod plugin;
9mod plugin_stack;
10mod protocol_build;
11pub mod provider;
12pub mod runtime;
13pub mod search;
14pub mod session;
15pub mod session_graph;
16pub mod session_model;
17mod stable_hash;
18pub mod store;
19#[cfg(any(test, feature = "testing"))]
20pub mod testing;
21pub mod tool_dispatch;
22mod tool_provider;
23pub mod tool_registry;
24mod tool_result;
25mod trace;
26
27pub use lash_sansio::sansio;
28
29pub const VERSION: &str = env!("CARGO_PKG_VERSION");
30pub const SANSIO_VERSION: &str = lash_sansio::VERSION;
31
32pub use attachments::{
34 AttachmentStore, AttachmentStoreError, AttachmentStorePersistence, InMemoryAttachmentStore,
35 SessionScopedAttachmentStore, StoredAttachment,
36};
37pub use chronological::{
41 BorrowedChronologicalEntry, BorrowedChronologicalMessage, BorrowedChronologicalPayload,
42 ChronologicalEntry, ChronologicalPayload, ChronologicalProjection, visit_turn_view,
43};
44pub use direct::{
45 DirectJsonSchema, DirectLlmClient, DirectLlmError, DirectMessage, DirectOutputSpec, DirectPart,
46 DirectRequest, DirectRole,
47};
48pub use host_events::{
49 HostEvent, HostEventCatalog, HostEventEmitReport, HostEventKey, host_event_source_type,
50};
51pub use lash_sansio::llm::types::{
52 GenerationOptions, LlmOutputPart, LlmRequest, LlmResponse, LlmTerminalReason,
53};
54pub use lash_sansio::{
55 AcceptedInjectedTurnInput, AttachmentCreateMeta, AttachmentId, AttachmentMeta, AttachmentRef,
56 BaseRenderCache, CheckpointDelivery, CheckpointKind, CompactToolContract, EffectId,
57 ErrorEnvelope, ExecImage, ExecResponse, ImageMediaType, LashSchema, LlmCallError, MediaType,
58 Message, MessageOrigin, MessageRole, MessageSequence, ModelToolReturn, ModelToolReturnPart,
59 Part, PartKind, PluginMessage, PluginRuntimeEvent, PreparedPrompt, PromptBuildInput,
60 PromptBuiltin, PromptContext, PromptContribution, PromptContributionGate,
61 PromptContributionSet, PromptFingerprint, PromptLayer, PromptSlot, PromptSlotLayer,
62 PromptTemplate, PromptTemplateEntry, PromptTemplateSection, PruneState, RenderedPrompt,
63 ResolvedPromptLayer, Response, SchemaProjectionOverride, SessionEvent, TextProjectionMetadata,
64 TokenUsage, ToolActivation, ToolAgentExecutableSurface, ToolAgentSurface,
65 ToolArgumentProjectionPolicy, ToolAvailability, ToolAvailabilityConfig, ToolCallOutcome,
66 ToolCallOutput, ToolCallRecord, ToolCallStatus, ToolCancellation, ToolContract, ToolControl,
67 ToolDefinition, ToolFailure, ToolFailureClass, ToolFailureSource, ToolId, ToolManifest,
68 ToolOutputContract, ToolRetryDisposition, ToolRetryPolicy, ToolScheduling, ToolSurface,
69 ToolSurfaceBuildInput, ToolSurfaceEntry, ToolSurfaceOverride, ToolValue, TurnCause, TurnFinish,
70 TurnLimitFinalMessage, TurnOutcome, TurnStop, append_assistant_text_part, build_prompt,
71 build_tool_surface, build_turn, default_prompt_template, head_tail_truncate,
72 messages_are_prompt_resume_safe, normalized_response_parts, prompt_template_fingerprint,
73 prompt_text_fingerprint, prompt_tool_names_fingerprint, reasoning_part,
74 render_turn_causes_prompt, resolve_prompt_layers, shared_parts, validate_tool_input,
75};
76pub use lashlang::{DurabilityTier, InMemoryLashlangArtifactStore, LashlangArtifactStore};
77pub use protocol_build::ProtocolBuildInput;
78pub use session::triggers::TriggerActivationService;
79pub use tool_registry::{
80 ReconfigureError, ToolRegistry, ToolSourceHandle, ToolState, ToolStateEntry,
81};
82pub use tool_result::ToolResult;
83#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
84pub struct ProtocolTurnOptions {
85 #[serde(default = "empty_protocol_turn_payload")]
86 pub payload: serde_json::Value,
87}
88
89fn empty_protocol_turn_payload() -> serde_json::Value {
90 serde_json::Value::Object(serde_json::Map::new())
91}
92
93impl Default for ProtocolTurnOptions {
94 fn default() -> Self {
95 Self::empty()
96 }
97}
98
99impl ProtocolTurnOptions {
100 pub fn empty() -> Self {
101 Self {
102 payload: serde_json::Value::Object(serde_json::Map::new()),
103 }
104 }
105
106 pub fn is_empty(&self) -> bool {
107 match &self.payload {
108 serde_json::Value::Object(map) => map.is_empty(),
109 _ => false,
110 }
111 }
112
113 pub fn merged_with_override(&self, override_options: &Self) -> Self {
114 match (&self.payload, &override_options.payload) {
115 (serde_json::Value::Object(base), serde_json::Value::Object(overrides)) => {
116 let mut payload = base.clone();
117 payload.extend(overrides.clone());
118 Self {
119 payload: serde_json::Value::Object(payload),
120 }
121 }
122 _ => override_options.clone(),
123 }
124 }
125
126 pub fn typed<T>(value: T) -> Result<Self, serde_json::Error>
127 where
128 T: serde::Serialize,
129 {
130 Ok(Self {
131 payload: serde_json::to_value(value)?,
132 })
133 }
134
135 pub fn decode<T>(&self) -> Result<T, serde_json::Error>
136 where
137 T: serde::de::DeserializeOwned,
138 {
139 serde_json::from_value(self.payload.clone())
140 }
141}
142
143#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
144pub struct ProtocolDriverState {
145 pub plugin_id: String,
146 pub payload: serde_json::Value,
147}
148
149impl ProtocolDriverState {
150 pub fn new(plugin_id: impl Into<String>, payload: serde_json::Value) -> Self {
151 Self {
152 plugin_id: plugin_id.into(),
153 payload,
154 }
155 }
156}
157
158#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
159pub struct HostTurnProtocol;
160
161impl lash_sansio::TurnProtocol for HostTurnProtocol {
162 type Event = crate::session_model::ProtocolEvent;
163 type Termination = ProtocolTurnOptions;
164 type DriverState = ProtocolDriverState;
165}
166
167pub type Effect = lash_sansio::Effect<HostTurnProtocol>;
168pub type DriverAction = lash_sansio::DriverAction<HostTurnProtocol>;
169pub type DriverContextView<'a> = lash_sansio::DriverContextView<'a, HostTurnProtocol>;
170pub type TurnDriverConfig = lash_sansio::TurnDriverConfig<HostTurnProtocol>;
171pub type TurnDriverPreamble = lash_sansio::TurnDriverPreamble<HostTurnProtocol>;
172pub type ProjectorContext<'a> = lash_sansio::ProjectorContext<'a, HostTurnProtocol>;
173pub type PreparedTurnMachine = lash_sansio::PreparedTurnMachine<HostTurnProtocol>;
174pub type SansIoTurnInput = lash_sansio::SansIoTurnInput<HostTurnProtocol>;
175pub type TurnMachine = lash_sansio::TurnMachine<HostTurnProtocol>;
176pub type TurnMachineConfig = lash_sansio::TurnMachineConfig<HostTurnProtocol>;
177#[cfg(feature = "otel-trace")]
178pub use lash_trace::otel::{OtelTraceOptions, OtelTraceSink};
179pub use lash_trace::{
180 JsonlTraceSink, TraceAttachment, TraceBranchSelection, TraceContentBlock, TraceContext,
181 TraceError, TraceEvent, TraceLabelMetadata, TraceLashlangChildExecution,
182 TraceLashlangEdgeSelection, TraceLashlangExecutionEvent, TraceLashlangExecutionIdentity,
183 TraceLashlangGraph, TraceLashlangGraphChildLink, TraceLashlangGraphEdge,
184 TraceLashlangGraphNode, TraceLashlangGraphStore, TraceLashlangMap, TraceLashlangMapEdge,
185 TraceLashlangMapNode, TraceLashlangNodeStatus, TraceLashlangStatus, TraceLevel,
186 TraceLlmMessage, TraceLlmRequest, TraceLlmResponse, TracePromptComponent,
187 TraceProviderStreamEvent, TraceRecord, TraceRuntimeScope, TraceRuntimeStreamEvent,
188 TraceRuntimeSubject, TraceSink, TraceSinkError, TraceTokenUsage, TraceToolSpec,
189};
190pub use llm::transport::{LlmTransportError, ProviderFailure, ProviderFailureKind};
191pub use model::{ModelLimits, ModelSpec};
192pub use plugin::{
193 AgentFrameAssignment, AgentFrameId, AgentFrameReason, AgentFrameRecord, AgentFrameStatus,
194 AppendSessionNodesRequest, AppendSessionNodesResult, AssistantResponseHookContext,
195 AssistantResponseTransform, AssistantStreamHookContext, AssistantStreamTransform,
196 CheckpointHookContext, DirectCompletion, DirectLlmCompletion, HistoryError,
197 HistoryRegistrations, HistoryRewriteMetadata, HistoryRewriter, HistoryState,
198 HostEventRegistrations, PersistentRuntimeServices, PluginAction, PluginActionContext,
199 PluginActionDef, PluginActionFailure, PluginActionInvokeError, PluginActionKind,
200 PluginDirective, PluginError, PluginFactory, PluginHost, PluginLifecycleEvent,
201 PluginLifecycleEventHook, PluginOptions, PluginOwned, PluginRegistrar, PluginSession,
202 PluginSessionContext, PluginSessionSnapshot, PluginSnapshotArtifact, PluginSnapshotEntry,
203 PluginSnapshotMeta, PluginSpec, PluginSpecFactory, PromptHookContext,
204 ProtocolBeforeLlmCallContext, ProtocolLlmCallAction, RewriteContext, RewriteTrigger,
205 RuntimeServices, SessionAppendNode, SessionConfigChangedContext, SessionContextSurface,
206 SessionCreateRequest, SessionGraphService, SessionHandle, SessionLifecycleService,
207 SessionParam, SessionPlugin, SessionPluginSource, SessionReadView, SessionRelation,
208 SessionSnapshot, SessionStartPoint, SessionStateChangedContext, SessionStateService,
209 SessionToolAccess, SessionTurnInput, SessionTurnRequest, SnapshotReader, SnapshotWriter,
210 SubagentSessionContext, ToolDiscoveryContext, ToolDiscoveryContribution,
211 ToolDiscoveryContributor, ToolDiscoveryToolContribution, ToolResultProjectionContext,
212 ToolResultProjector, ToolSurfaceContribution, TriggerRegistration, TriggerSourceType,
213 TriggerTargetSummary, TurnContextTransform, TurnHookContext, TurnResultHookContext,
214 TurnResultSummary, TurnTransformContext, plugin_action_def,
215};
216pub use plugin_stack::PluginStack;
217pub use provider::{
218 CacheRetention, EmptyProviderResolver, LlmTimeouts, MapProviderResolver, Provider,
219 ProviderBinding, ProviderComponents, ProviderFactory, ProviderHandle, ProviderModelPolicy,
220 ProviderOptions, ProviderResolutionError, ProviderSpec, ProviderThinkingPolicy, RequestTimeout,
221 RuntimeProviderResolver, SingleProviderResolver, StaticModelPolicy,
222};
223#[cfg(any(test, feature = "testing"))]
224pub use runtime::TestLocalProcessRegistry;
225pub use runtime::{
226 AgentFrameRun, AssembledTurn, AssistantOutput, CausalRef, CodeOutputRecord,
227 DefaultProcessCancelAbility, DeliveryPolicy, DirectCompletionClient, DurableProcessWorker,
228 DurableProcessWorkerConfig, DurableStoreFacet, EffectHost, EffectScope, EmbeddedRuntimeBuilder,
229 EmbeddedRuntimeHost, EventSink, ExecutionSummary, InMemorySessionStore,
230 InMemorySessionStoreFactory, InlineEffectHost, InlineProcessRunHandle,
231 InlineRuntimeEffectController, InputItem, LashRuntime, MergeKey, NoopEventSink,
232 NoopTurnActivitySink, ObservedProcess, ObservedProcessEvent, ObservedWorkItem, OutputState,
233 PROCESS_LEASE_SCHEMA_VERSION, ParkedSession, ProcessAwaitOutput, ProcessCancelAbility,
234 ProcessCancelAllRequest, ProcessCancelRequest, ProcessCancelSource, ProcessCancelSummary,
235 ProcessDefinitionSelector, ProcessDefinitionSummary, ProcessEvent, ProcessEventAppendRequest,
236 ProcessEventAppendResult, ProcessEventType, ProcessExecutionContext, ProcessExternalRef,
237 ProcessHandleDescriptor, ProcessHandleGrant, ProcessHandleSummary, ProcessId, ProcessInput,
238 ProcessLease, ProcessLeaseCompletion, ProcessLifecycleStatus, ProcessListFilter,
239 ProcessListMode, ProcessOpScope, ProcessProvenance, ProcessRecord, ProcessRegistration,
240 ProcessRegistry, ProcessRunHandle, ProcessRuntimeHost, ProcessScope, ProcessScopeId,
241 ProcessService, ProcessSessionDeleteReport, ProcessStartGrant, ProcessStartOptions,
242 ProcessStartRequest, ProcessStatus, ProcessStatusFilter, ProcessTerminalSemantics,
243 ProcessTerminalSpec, ProcessTerminalState, ProcessValueSelector, ProcessWake,
244 ProcessWakeDedupeKey, ProcessWakeDelivery, ProcessWakeSpec, ProcessWorkDriver,
245 ProcessWorkObserver, ProcessWorkPoke, ProcessWorkRunner, ProcessWorkSnapshot, PromptUsage,
246 ProtocolSessionExtension, ProtocolSessionExtensionHandle, ProtocolTurnExtension,
247 ProtocolTurnExtensionHandle, QueuedWorkPoke, QueuedWorkRunHandle, QueuedWorkRunOutcome,
248 QueuedWorkRunRequest, QueuedWorkRunner, Residency, RuntimeEnvironment,
249 RuntimeEnvironmentBuilder, RuntimeError, RuntimeErrorCode, RuntimeHandle, RuntimeHostConfig,
250 RuntimeObservation, ScopedEffectController, SessionCommand, SessionCommandReceipt,
251 SessionStoreCreateRequest, SessionStoreFactory, SessionUsageReport, SlotPolicy,
252 TerminationPolicy, TokenLedgerEntry, TurnActivity, TurnActivityId, TurnActivitySink,
253 TurnContext, TurnEvent, TurnInput, TurnIssue, TurnOptions, UnavailableProcessService,
254 UsageReportRow, UsageTotals, current_epoch_ms, diff_token_ledger, diff_usage_reports,
255 ensure_durable_effect_input, epoch_ms_from_system_time, lashlang_process_event_types,
256 system_time_from_epoch_ms,
257};
258#[allow(unused_imports)]
259pub(crate) use runtime::{
260 LlmAttachmentSpec, PreparedProcessEventAppend, ProcessEventSemantics, QUEUED_WORK_CLAIM_TTL_MS,
261 QueuedCheckpointWork, QueuedTurnWork, QueuedWorkBatch, QueuedWorkBatchDraft, QueuedWorkClaim,
262 QueuedWorkClaimBoundary, QueuedWorkCompletion, QueuedWorkItem, QueuedWorkPayload,
263 RuntimeReplay, RuntimeScope, RuntimeSubject, materialize_process_event_semantics,
264 prepare_process_event_append, prepare_process_registration, process_event_invocation,
265 process_event_payload_hash, process_wake_batch_draft, process_wake_delivery,
266 process_wake_input_from_event_payload, process_wake_turn_cause, process_wake_turn_text,
267 require_event_replay,
268};
269pub use runtime::{
273 LlmRequestSpec, ProcessCommand, ProcessEffectOutcome, ProcessEventSemanticsSpec,
274 RuntimeEffectCommand, RuntimeEffectController, RuntimeEffectControllerError,
275 RuntimeEffectEnvelope, RuntimeEffectKind, RuntimeEffectLocalExecutor, RuntimeEffectOutcome,
276 RuntimeInvocation, RuntimeSessionState,
277};
278pub use schemars::JsonSchema;
279pub use session::{
280 ExecRequest, InjectedTurnInput, RuntimeExecutionContext, Session, SessionError, ToolInvocation,
281 ToolInvocationReply,
282};
283pub use session_graph::{
284 PersistedSessionConfig, PersistedTurnState, SessionGraph, SessionMessageTreeNode,
285 SessionNodePayload, SessionNodeRecord,
286};
287pub use session_model::context::PreparedContext;
288pub use session_model::{ConversationRecord, ProtocolEvent, SessionEventRecord, ToolEvent};
289pub use session_model::{RuntimeSessionPolicy, SessionPolicy, SessionSpec};
290pub use store::{
291 AttachmentIntent, AttachmentManifest, AttachmentManifestEntry, BlobRef, GcReport,
292 RuntimePersistence, SessionMeta, SessionPickerInfo, SessionReadScope, StoreError, VacuumReport,
293};
294#[allow(unused_imports)]
295pub(crate) use store::{
296 GraphCommitDelta, PersistedSessionRead, RuntimeCommitResult, SessionCheckpoint,
297 SessionHeadMeta, ensure_supported_schema_version, load_persisted_session_state,
298 load_persisted_session_state_active_path,
299};
300pub use store::{
301 HydratedSessionCheckpoint, RuntimeCommit, RuntimeTurnCommitStamp, SessionHead,
302 refresh_persisted_session_state,
303};
304pub use tool_provider::{
305 PreparedToolCall, ProgressSender, SandboxMessage, ToolCall, ToolContext, ToolHostEventControl,
306 ToolLashlangExecutionCallSite, ToolPrepareCall, ToolPrepareContext, ToolProvider,
307 ToolSessionControl, ToolSessionModel,
308};
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313
314 #[test]
315 fn protocol_turn_options_missing_payload_deserializes_to_empty_object() {
316 let options: ProtocolTurnOptions =
317 serde_json::from_value(serde_json::json!({})).expect("deserialize options");
318
319 assert!(options.is_empty());
320 assert_eq!(options.payload, serde_json::json!({}));
321 }
322
323 #[test]
324 fn protocol_turn_options_explicit_null_is_not_empty() {
325 let options: ProtocolTurnOptions =
326 serde_json::from_value(serde_json::json!({ "payload": null }))
327 .expect("deserialize options");
328
329 assert!(!options.is_empty());
330 assert_eq!(options.payload, serde_json::Value::Null);
331 }
332
333 #[test]
334 fn root_exports_do_not_reintroduce_removed_session_state_shapes() {
335 let source = include_str!("lib.rs");
336 let removed_envelope = ["SessionState", "Envelope"].concat();
337 let removed_persisted = ["PersistedSession", "Snapshot"].concat();
338
339 assert!(!source.contains(&removed_envelope));
340 assert!(!source.contains(&removed_persisted));
341 }
342
343 fn public_reexport_block(source: &str, module: &str) -> String {
344 let start = format!("pub use {module}::{{");
345 let mut block = String::new();
346 let mut collecting = false;
347 for line in source.lines() {
348 if line.trim_start().starts_with(&start) {
349 collecting = true;
350 }
351 if collecting {
352 block.push_str(line);
353 block.push('\n');
354 if line.trim_end() == "};" {
355 break;
356 }
357 }
358 }
359 assert!(!block.is_empty(), "missing public {module} re-export block");
360 block
361 }
362
363 #[test]
364 fn root_runtime_exports_exclude_internal_runtime_records() {
365 let runtime_exports = public_reexport_block(include_str!("lib.rs"), "runtime");
366 for removed in [
367 "RuntimeEffectCommand",
368 "RuntimeEffectEnvelope",
369 "RuntimeEffectKind",
370 "RuntimeEffectOutcome",
371 "RuntimeInvocation",
372 "RuntimeScope",
373 "RuntimeSessionState",
374 "QueuedWorkBatch",
375 "QueuedWorkBatchDraft",
376 "QueuedWorkPayload",
377 "prepare_process_registration",
378 "process_wake_batch_draft",
379 "require_event_replay",
380 ] {
381 assert!(
382 !runtime_exports.contains(removed),
383 "runtime root export leaked {removed}"
384 );
385 }
386 }
387
388 #[test]
389 fn root_store_exports_exclude_wire_records() {
390 let store_exports = public_reexport_block(include_str!("lib.rs"), "store");
391 for removed in [
392 "SessionHead",
393 "SessionCheckpoint",
394 "RuntimeCommit",
395 "HydratedSessionCheckpoint",
396 "PersistedSessionRead",
397 "GraphCommitDelta",
398 ] {
399 assert!(
400 !store_exports.contains(removed),
401 "store root export leaked {removed}"
402 );
403 }
404 }
405
406 #[test]
407 fn removed_manager_and_host_trait_names_stay_removed() {
408 let removed_manager = ["Runtime", "Session", "Manager"].concat();
409 let removed_host = ["Runtime", "Session", "Host"].concat();
410 let sources = [
411 include_str!("runtime/session_manager/mod.rs"),
412 include_str!("plugin/runtime_host.rs"),
413 include_str!("tool_dispatch/context.rs"),
414 include_str!("tool_provider.rs"),
415 ];
416
417 for source in sources {
418 assert!(!source.contains(&removed_manager));
419 assert!(!source.contains(&removed_host));
420 }
421 }
422}