1pub mod attachments;
14pub mod chronological;
15pub mod direct;
16pub mod llm;
17mod model;
18pub mod plugin;
19mod plugin_stack;
20mod protocol_build;
21pub mod provider;
22pub mod runtime;
23pub mod search;
24pub mod session;
25pub mod session_graph;
26pub mod session_model;
27mod stable_hash;
28pub mod store;
29#[cfg(any(test, feature = "testing"))]
30pub mod testing;
31pub mod tool_dispatch;
32mod tool_provider;
33pub mod tool_registry;
34mod tool_result;
35mod trace;
36pub mod triggers;
37
38pub use lash_sansio::sansio;
39
40pub const VERSION: &str = env!("CARGO_PKG_VERSION");
41pub const SANSIO_VERSION: &str = lash_sansio::VERSION;
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub enum DurabilityTier {
45 Inline,
46 Durable,
47}
48
49pub use attachments::{
51 AttachmentStore, AttachmentStoreError, AttachmentStorePersistence, FileAttachmentStore,
52 InMemoryAttachmentStore, SessionScopedAttachmentStore, StoredAttachment,
53};
54pub use chronological::{
55 BorrowedChronologicalEntry, BorrowedChronologicalMessage, BorrowedChronologicalPayload,
56 ChronologicalEntry, ChronologicalPayload, ChronologicalProjection, visit_turn_view,
57};
58pub use direct::{
59 DirectJsonSchema, DirectLlmClient, DirectLlmError, DirectMessage, DirectOutputSpec, DirectPart,
60 DirectRequest, DirectRole,
61};
62pub use lash_sansio::llm::types::{
63 GenerationOptions, LlmOutputPart, LlmRequest, LlmResponse, LlmTerminalReason,
64};
65pub use lash_sansio::{
66 AcceptedInjectedTurnInput, AttachmentCreateMeta, AttachmentId, AttachmentMeta, AttachmentRef,
67 BaseRenderCache, CheckpointDelivery, CheckpointKind, CompactToolContract, EffectId,
68 ErrorEnvelope, ExecImage, ExecResponse, ImageMediaType, LashSchema, LlmCallError, MediaType,
69 Message, MessageOrigin, MessageRole, MessageSequence, ModelToolReturn, ModelToolReturnPart,
70 Part, PartKind, PluginMessage, PluginRuntimeEvent, PreparedPrompt, PromptBuildInput,
71 PromptBuiltin, PromptContext, PromptContribution, PromptContributionGate,
72 PromptContributionSet, PromptFingerprint, PromptLayer, PromptSlot, PromptSlotLayer,
73 PromptTemplate, PromptTemplateEntry, PromptTemplateSection, PruneState, RenderedPrompt,
74 ResolvedPromptLayer, Response, SchemaProjectionOverride, SessionEvent, TextProjectionMetadata,
75 TokenUsage, ToolActivation, ToolArgumentProjectionPolicy, ToolAvailability,
76 ToolAvailabilityConfig, ToolCallOutcome, ToolCallOutput, ToolCallRecord, ToolCallStatus,
77 ToolCancellation, ToolCatalog, ToolCatalogBuildInput, ToolCatalogEntry, ToolCatalogOverride,
78 ToolContract, ToolControl, ToolDefinition, ToolFailure, ToolFailureClass, ToolFailureSource,
79 ToolId, ToolManifest, ToolOutputContract, ToolRetryDisposition, ToolRetryPolicy,
80 ToolScheduling, ToolValue, TurnCause, TurnFinish, TurnLimitFinalMessage, TurnOutcome, TurnStop,
81 append_assistant_text_part, build_prompt, build_tool_catalog, build_turn,
82 default_prompt_template, head_tail_truncate, messages_are_prompt_resume_safe,
83 normalized_response_parts, prompt_template_fingerprint, prompt_text_fingerprint,
84 prompt_tool_names_fingerprint, reasoning_part, render_turn_causes_prompt,
85 resolve_prompt_layers, shared_parts, validate_tool_input,
86};
87pub use protocol_build::ProtocolBuildInput;
88pub use tool_registry::{
89 ReconfigureError, ToolRegistry, ToolRestoreReport, ToolSourceHandle, ToolState, ToolStateEntry,
90};
91pub use tool_result::{CancelHint, PendingCompletion, TimeoutBehavior, ToolResult};
92pub use triggers::{
93 InMemoryTriggerStore, TriggerDeliveryReservation, TriggerEmitReport, TriggerEvent,
94 TriggerEventCatalog, TriggerEventKey, TriggerEventType, TriggerInputBinding,
95 TriggerOccurrenceRecord, TriggerOccurrenceRequest, TriggerRegistration, TriggerRouter,
96 TriggerStore, TriggerSubscriptionDraft, TriggerSubscriptionFilter, TriggerSubscriptionRecord,
97 TriggerTargetSummary, default_trigger_source_key, deterministic_delivery_process_id,
98 deterministic_occurrence_id, empty_trigger_source_key, trigger_event_type,
99 trigger_occurrence_request_hash, validate_trigger_occurrence_request,
100};
101#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
102pub struct ProtocolTurnOptions {
103 #[serde(default = "empty_protocol_turn_payload")]
104 pub payload: serde_json::Value,
105}
106
107fn empty_protocol_turn_payload() -> serde_json::Value {
108 serde_json::Value::Object(serde_json::Map::new())
109}
110
111impl Default for ProtocolTurnOptions {
112 fn default() -> Self {
113 Self::empty()
114 }
115}
116
117impl ProtocolTurnOptions {
118 pub fn empty() -> Self {
119 Self {
120 payload: serde_json::Value::Object(serde_json::Map::new()),
121 }
122 }
123
124 pub fn is_empty(&self) -> bool {
125 match &self.payload {
126 serde_json::Value::Object(map) => map.is_empty(),
127 _ => false,
128 }
129 }
130
131 pub fn merged_with_override(&self, override_options: &Self) -> Self {
132 match (&self.payload, &override_options.payload) {
133 (serde_json::Value::Object(base), serde_json::Value::Object(overrides)) => {
134 let mut payload = base.clone();
135 payload.extend(overrides.clone());
136 Self {
137 payload: serde_json::Value::Object(payload),
138 }
139 }
140 _ => override_options.clone(),
141 }
142 }
143
144 pub fn typed<T>(value: T) -> Result<Self, serde_json::Error>
145 where
146 T: serde::Serialize,
147 {
148 Ok(Self {
149 payload: serde_json::to_value(value)?,
150 })
151 }
152
153 pub fn decode<T>(&self) -> Result<T, serde_json::Error>
154 where
155 T: serde::de::DeserializeOwned,
156 {
157 serde_json::from_value(self.payload.clone())
158 }
159}
160
161#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
162pub struct ProtocolDriverState {
163 pub plugin_id: String,
164 pub payload: serde_json::Value,
165}
166
167impl ProtocolDriverState {
168 pub fn new(plugin_id: impl Into<String>, payload: serde_json::Value) -> Self {
169 Self {
170 plugin_id: plugin_id.into(),
171 payload,
172 }
173 }
174}
175
176#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
177pub struct HostTurnProtocol;
178
179impl lash_sansio::TurnProtocol for HostTurnProtocol {
180 type Event = crate::session_model::ProtocolEvent;
181 type Termination = ProtocolTurnOptions;
182 type DriverState = ProtocolDriverState;
183}
184
185pub type Effect = lash_sansio::Effect<HostTurnProtocol>;
186pub type DriverAction = lash_sansio::DriverAction<HostTurnProtocol>;
187pub type DriverContextView<'a> = lash_sansio::DriverContextView<'a, HostTurnProtocol>;
188pub type TurnDriverConfig = lash_sansio::TurnDriverConfig<HostTurnProtocol>;
189pub type TurnDriverPreamble = lash_sansio::TurnDriverPreamble<HostTurnProtocol>;
190pub type ProjectorContext<'a> = lash_sansio::ProjectorContext<'a, HostTurnProtocol>;
191pub type PreparedTurnMachine = lash_sansio::PreparedTurnMachine<HostTurnProtocol>;
192pub type SansIoTurnInput = lash_sansio::SansIoTurnInput<HostTurnProtocol>;
193pub type TurnMachine = lash_sansio::TurnMachine<HostTurnProtocol>;
194pub type TurnMachineConfig = lash_sansio::TurnMachineConfig<HostTurnProtocol>;
195#[cfg(feature = "otel-trace")]
196pub use lash_trace::otel::{OtelTraceOptions, OtelTraceSink};
197pub use lash_trace::{
198 JsonlTraceSink, TraceAttachment, TraceBranchSelection, TraceContentBlock, TraceContext,
199 TraceError, TraceEvent, TraceLabelMetadata, TraceLevel, TraceLlmMessage, TraceLlmRequest,
200 TraceLlmResponse, TracePromptComponent, TraceProviderStreamEvent, TraceRecord,
201 TraceRuntimeScope, TraceRuntimeStreamEvent, TraceRuntimeSubject, TraceSink, TraceSinkError,
202 TraceTokenUsage, TraceToolSpec,
203};
204pub use llm::transport::{LlmTransportError, ProviderFailure, ProviderFailureKind};
205pub use model::{ModelLimits, ModelSpec};
206pub use plugin::{
207 AgentFrameAssignment, AgentFrameId, AgentFrameReason, AgentFrameRecord, AgentFrameStatus,
208 AppendSessionNodesRequest, AppendSessionNodesResult, AssistantResponseHookContext,
209 AssistantResponseTransform, AssistantStreamHookContext, AssistantStreamTransform,
210 CheckpointHookContext, CompactionContext, ContextCompaction, ContextCompactor, ContextError,
211 ContextRegistrations, DirectCompletion, DirectLlmCompletion, OpenAgentFrameRequest,
212 OpenAgentFrameResult, PersistentRuntimeServices, PluginCommand, PluginCommandContext,
213 PluginCommandOutcome, PluginCommandReceipt, PluginDirective, PluginError,
214 PluginExtensionContribution, PluginExtensions, PluginFactory, PluginHost, PluginLifecycleEvent,
215 PluginLifecycleEventHook, PluginOperation, PluginOperationDef, PluginOperationFailure,
216 PluginOperationInvokeError, PluginOperationKind, PluginOptions, PluginOwned, PluginQuery,
217 PluginQueryContext, PluginRegistrar, PluginRuntimeDirective, PluginSession,
218 PluginSessionContext, PluginSessionSnapshot, PluginSnapshotArtifact, PluginSnapshotEntry,
219 PluginSnapshotMeta, PluginSpec, PluginSpecFactory, PluginTask, PluginTaskContext,
220 PluginTaskOutcome, PluginTaskReceipt, PromptHookContext, ProtocolBeforeLlmCallContext,
221 ProtocolLlmCallAction, RuntimeServices, SessionAppendNode, SessionConfigChangedContext,
222 SessionContextOverlay, SessionCreateRequest, SessionGraphService, SessionHandle,
223 SessionLifecycleService, SessionParam, SessionPlugin, SessionPluginSource, SessionReadView,
224 SessionRelation, SessionSnapshot, SessionStartPoint, SessionStateChangedContext,
225 SessionStateService, SessionToolAccess, SessionTurnInput, SessionTurnRequest, SnapshotReader,
226 SnapshotWriter, SubagentSessionContext, ToolCatalogContribution, ToolDiscoveryContext,
227 ToolDiscoveryContribution, ToolDiscoveryContributor, ToolDiscoveryToolContribution,
228 ToolResultProjectionContext, ToolResultProjector, TriggerEventRegistrations,
229 TurnContextTransform, TurnHookContext, TurnResultHookContext, TurnResultSummary,
230 TurnTransformContext, plugin_operation_def,
231};
232pub use plugin_stack::PluginStack;
233pub use provider::{
234 CacheRetention, EmptyProviderResolver, LlmTimeouts, MapProviderResolver, Provider,
235 ProviderBinding, ProviderComponents, ProviderFactory, ProviderHandle, ProviderModelPolicy,
236 ProviderOptions, ProviderResolutionError, ProviderSpec, RequestTimeout,
237 RuntimeProviderResolver, SingleProviderResolver, StaticModelPolicy,
238};
239#[cfg(any(test, feature = "testing"))]
240pub use runtime::TestLocalProcessRegistry;
241pub use runtime::{
242 AgentFrameRun, AssembledTurn, AssistantOutput, AwaitEventKey, AwaitEventWaitIdentity,
243 CausalRef, Clock, CodeOutputRecord, DefaultProcessCancelAbility, DeliveryPolicy,
244 DirectCompletionClient, DurableProcessWorker, DurableProcessWorkerConfig, DurableStoreFacet,
245 EffectHost, EmbeddedRuntimeBuilder, EmbeddedRuntimeHost, EventSink, ExecutionScope,
246 ExecutionSummary, ExternalCompletionError, InMemoryLiveReplayStore,
247 InMemoryLiveReplayStoreConfig, InMemoryProcessExecutionEnvStore, InMemorySessionStore,
248 InMemorySessionStoreFactory, InlineEffectHost, InlineProcessRunHandle,
249 InlineRuntimeEffectController, InputItem, LashRuntime, LiveReplayGap, LiveReplayGapReason,
250 LiveReplayResult, LiveReplayStore, LiveReplayStoreError, LiveReplaySubscribeResult,
251 LiveReplaySubscription, MergeKey, NoopEventSink, NoopTurnActivitySink, ObservedProcess,
252 ObservedProcessEvent, ObservedWorkItem, OutputState, PROCESS_LEASE_SCHEMA_VERSION,
253 ParkedSession, ProcessAwaitOutput, ProcessCancelAbility, ProcessCancelAllRequest,
254 ProcessCancelRequest, ProcessCancelSource, ProcessCancelSummary, ProcessEngine,
255 ProcessEngineRegistry, ProcessEngineRunContext, ProcessEngineRunGuard,
256 ProcessEngineRuntimeContext, ProcessEngineValidationContext, ProcessEvent,
257 ProcessEventAppendPlan, ProcessEventAppendRequest, ProcessEventAppendResult, ProcessEventType,
258 ProcessExecutionContext, ProcessExecutionEnvRef, ProcessExecutionEnvSpec,
259 ProcessExecutionEnvStore, ProcessExternalRef, ProcessHandleDescriptor, ProcessHandleGrant,
260 ProcessHandleSummary, ProcessId, ProcessIdentity, ProcessInput, ProcessLease,
261 ProcessLeaseCompletion, ProcessLifecycleStatus, ProcessListFilter, ProcessListMode,
262 ProcessOpScope, ProcessOriginator, ProcessProvenance, ProcessRecord, ProcessRegistration,
263 ProcessRegistry, ProcessRunHandle, ProcessRuntimeHost, ProcessService,
264 ProcessSessionDeleteReport, ProcessSpawnProvenance, ProcessStartGrant, ProcessStartOptions,
265 ProcessStartRequest, ProcessStatus, ProcessStatusFilter, ProcessTerminalSemantics,
266 ProcessTerminalSpec, ProcessTerminalState, ProcessValueSelector, ProcessWake,
267 ProcessWakeDedupeKey, ProcessWakeDelivery, ProcessWakeSpec, ProcessWorkDriver,
268 ProcessWorkObserver, ProcessWorkSnapshot, PromptUsage, ProtocolSessionExtension,
269 ProtocolSessionExtensionHandle, ProtocolTurnExtension, ProtocolTurnExtensionHandle,
270 QueuedWorkDriver, QueuedWorkRunHandle, QueuedWorkRunRequest, Residency, Resolution,
271 ResolveOutcome, RuntimeEnvironment, RuntimeEnvironmentBuilder, RuntimeError, RuntimeErrorCode,
272 RuntimeHandle, RuntimeHostConfig, RuntimeObservation, ScopedEffectController, SessionCommand,
273 SessionCommandReceipt, SessionCursor, SessionCursorError, SessionObservation,
274 SessionObservationEvent, SessionObservationEventPayload, SessionObservationSubscription,
275 SessionProcessEventKind, SessionQueueEventKind, SessionResume, SessionRevision, SessionScope,
276 SessionScopeId, SessionStoreCreateRequest, SessionStoreFactory, SessionUsageReport, SlotPolicy,
277 SystemClock, TerminationPolicy, TokenLedgerEntry, ToolCallLaunch, TurnActivity, TurnActivityId,
278 TurnActivitySink, TurnContext, TurnEvent, TurnInput, TurnIssue, TurnOptions,
279 UnavailableProcessService, UsageReportRow, UsageTotals, WaitKind, WaitState,
280 apply_process_status_projection, current_epoch_ms, diff_token_ledger, diff_usage_reports,
281 ensure_durable_effect_input, epoch_ms_from_system_time, process_signal_event_type,
282 process_signal_name_from_event_type, process_signal_wait_key, system_time_from_epoch_ms,
283 validate_process_signal_name,
284};
285#[allow(unused_imports)]
286pub(crate) use runtime::{
287 LlmAttachmentSpec, ProcessEventSemantics, QUEUED_WORK_CLAIM_TTL_MS, QueuedCheckpointWork,
288 QueuedTurnWork, QueuedWorkBatch, QueuedWorkBatchDraft, QueuedWorkClaim,
289 QueuedWorkClaimBoundary, QueuedWorkCompletion, QueuedWorkItem, QueuedWorkPayload,
290 RuntimeReplay, RuntimeScope, RuntimeSubject, load_process_execution_env,
291 materialize_process_event_semantics, persist_process_execution_env,
292 prepare_process_event_append, prepare_process_registration, process_event_invocation,
293 process_event_payload_hash, process_wake_batch_draft, process_wake_delivery,
294 process_wake_input_from_event_payload, process_wake_turn_cause, process_wake_turn_text,
295 require_event_replay,
296};
297pub use session_model::{
298 PLUGIN_RUNTIME_PROTOCOL_PLUGIN_ID, PersistedPluginRuntimeEvent,
299 plugin_runtime_event_from_protocol, plugin_runtime_protocol_event,
300};
301pub use runtime::{
305 LlmRequestSpec, ProcessCommand, ProcessEffectOutcome, ProcessEventSemanticsSpec,
306 RuntimeEffectCommand, RuntimeEffectController, RuntimeEffectControllerError,
307 RuntimeEffectEnvelope, RuntimeEffectKind, RuntimeEffectLocalExecutor, RuntimeEffectOutcome,
308 RuntimeInvocation, RuntimeSessionState, ToolBatchEffectOutcome,
309};
310pub use schemars::JsonSchema;
311pub use session::{
312 ExecRequest, InjectedTurnInput, RuntimeExecutionContext, Session, SessionError, ToolInvocation,
313 ToolInvocationReply,
314};
315pub use session_graph::{
316 PersistedSessionConfig, PersistedTurnState, SessionGraph, SessionMessageTreeNode,
317 SessionNodePayload, SessionNodeRecord,
318};
319pub use session_model::context::PreparedContext;
320pub use session_model::{ConversationRecord, ProtocolEvent, SessionEventRecord};
321pub use session_model::{RuntimeSessionPolicy, SessionPolicy, SessionSpec};
322pub use store::{
323 AttachmentIntent, AttachmentManifest, AttachmentManifestEntry, BlobRef, GcReport,
324 RuntimePersistence, SessionMeta, SessionPickerInfo, SessionReadScope, StoreError, VacuumReport,
325};
326#[allow(unused_imports)]
327pub(crate) use store::{
328 GraphCommitDelta, PersistedSessionRead, RuntimeCommitResult, SessionCheckpoint,
329 SessionHeadMeta, ensure_supported_schema_version, load_persisted_session_state,
330 load_persisted_session_state_active_path,
331};
332pub use store::{
333 HydratedSessionCheckpoint, RuntimeCommit, RuntimeTurnCommitStamp, SessionHead,
334 refresh_persisted_session_state,
335};
336pub use tool_provider::{
337 PreparedToolBatch, PreparedToolBatchCall, PreparedToolCall, ProgressSender, SandboxMessage,
338 ToolCall, ToolChildExecutionTraceHook, ToolChildProcessStarted, ToolContext,
339 ToolDurableEffects, ToolPrepareCall, ToolPrepareContext, ToolProvider, ToolSessionAdmin,
340 ToolSessionModel, ToolTriggerClient,
341};
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346
347 #[test]
348 fn protocol_turn_options_missing_payload_deserializes_to_empty_object() {
349 let options: ProtocolTurnOptions =
350 serde_json::from_value(serde_json::json!({})).expect("deserialize options");
351
352 assert!(options.is_empty());
353 assert_eq!(options.payload, serde_json::json!({}));
354 }
355
356 #[test]
357 fn protocol_turn_options_explicit_null_is_not_empty() {
358 let options: ProtocolTurnOptions =
359 serde_json::from_value(serde_json::json!({ "payload": null }))
360 .expect("deserialize options");
361
362 assert!(!options.is_empty());
363 assert_eq!(options.payload, serde_json::Value::Null);
364 }
365
366 #[test]
367 fn root_exports_do_not_reintroduce_removed_session_state_shapes() {
368 let source = include_str!("lib.rs");
369 let removed_envelope = ["SessionState", "Envelope"].concat();
370 let removed_persisted = ["PersistedSession", "Snapshot"].concat();
371 let removed_history_rewriter = ["History", "Rewriter"].concat();
372 let removed_rewrite_trigger = ["Rewrite", "Trigger"].concat();
373 let removed_rewrite_context = ["Rewrite", "Context"].concat();
374 let removed_history_state = ["History", "State"].concat();
375 let removed_history_metadata = ["History", "Rewrite", "Metadata"].concat();
376
377 assert!(!source.contains(&removed_envelope));
378 assert!(!source.contains(&removed_persisted));
379 assert!(!source.contains(&removed_history_rewriter));
380 assert!(!source.contains(&removed_rewrite_trigger));
381 assert!(!source.contains(&removed_rewrite_context));
382 assert!(!source.contains(&removed_history_state));
383 assert!(!source.contains(&removed_history_metadata));
384 }
385
386 fn public_reexport_block(source: &str, module: &str) -> String {
387 let start = format!("pub use {module}::{{");
388 let mut block = String::new();
389 let mut collecting = false;
390 for line in source.lines() {
391 if line.trim_start().starts_with(&start) {
392 collecting = true;
393 }
394 if collecting {
395 block.push_str(line);
396 block.push('\n');
397 if line.trim_end() == "};" {
398 break;
399 }
400 }
401 }
402 assert!(!block.is_empty(), "missing public {module} re-export block");
403 block
404 }
405
406 #[test]
407 fn root_runtime_exports_exclude_internal_runtime_records() {
408 let runtime_exports = public_reexport_block(include_str!("lib.rs"), "runtime");
409 for removed in [
410 "RuntimeEffectCommand",
411 "RuntimeEffectEnvelope",
412 "RuntimeEffectKind",
413 "RuntimeEffectOutcome",
414 "RuntimeInvocation",
415 "RuntimeScope",
416 "RuntimeSessionState",
417 "QueuedWorkBatch",
418 "QueuedWorkBatchDraft",
419 "QueuedWorkPayload",
420 "prepare_process_registration",
421 "process_wake_batch_draft",
422 "require_event_replay",
423 ] {
424 assert!(
425 !runtime_exports.contains(removed),
426 "runtime root export leaked {removed}"
427 );
428 }
429 }
430
431 #[test]
432 fn root_store_exports_exclude_wire_records() {
433 let store_exports = public_reexport_block(include_str!("lib.rs"), "store");
434 for removed in [
435 "SessionHead",
436 "SessionCheckpoint",
437 "RuntimeCommit",
438 "HydratedSessionCheckpoint",
439 "PersistedSessionRead",
440 "GraphCommitDelta",
441 ] {
442 assert!(
443 !store_exports.contains(removed),
444 "store root export leaked {removed}"
445 );
446 }
447 }
448
449 #[test]
450 fn removed_manager_and_host_trait_names_stay_removed() {
451 let removed_manager = ["Runtime", "Session", "Manager"].concat();
452 let removed_host = ["Runtime", "Session", "Host"].concat();
453 let sources = [
454 include_str!("runtime/session_manager/mod.rs"),
455 include_str!("plugin/runtime_host.rs"),
456 include_str!("tool_dispatch/context.rs"),
457 include_str!("tool_provider.rs"),
458 ];
459
460 for source in sources {
461 assert!(!source.contains(&removed_manager));
462 assert!(!source.contains(&removed_host));
463 }
464 }
465}