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, ToolAttemptEffectOutcome, ToolAttemptLaunch,
309 ToolBatchEffectOutcome,
310};
311pub use schemars::JsonSchema;
312pub use session::{
313 ExecRequest, InjectedTurnInput, RuntimeExecutionContext, Session, SessionError, ToolInvocation,
314 ToolInvocationReply,
315};
316pub use session_graph::{
317 PersistedSessionConfig, PersistedTurnState, SessionGraph, SessionMessageTreeNode,
318 SessionNodePayload, SessionNodeRecord,
319};
320pub use session_model::context::PreparedContext;
321pub use session_model::{ConversationRecord, ProtocolEvent, SessionEventRecord};
322pub use session_model::{RuntimeSessionPolicy, SessionPolicy, SessionSpec};
323pub use store::{
324 AttachmentIntent, AttachmentManifest, AttachmentManifestEntry, BlobRef, GcReport,
325 RuntimePersistence, SessionExecutionLease, SessionExecutionLeaseCompletion,
326 SessionExecutionLeaseFence, SessionMeta, SessionPickerInfo, SessionReadScope, StoreError,
327 VacuumReport,
328};
329#[allow(unused_imports)]
330pub(crate) use store::{
331 GraphCommitDelta, PersistedSessionRead, RuntimeCommitResult, SessionCheckpoint,
332 SessionHeadMeta, ensure_supported_schema_version, load_persisted_session_state,
333 load_persisted_session_state_active_path,
334};
335pub use store::{
336 HydratedSessionCheckpoint, RuntimeCommit, RuntimeTurnCommitStamp, SessionHead,
337 refresh_persisted_session_state,
338};
339pub use tool_provider::{
340 PreparedToolBatch, PreparedToolBatchCall, PreparedToolCall, ProgressSender, SandboxMessage,
341 ToolCall, ToolChildExecutionTraceHook, ToolChildProcessStarted, ToolContext,
342 ToolDurableEffects, ToolPrepareCall, ToolPrepareContext, ToolProvider, ToolSessionAdmin,
343 ToolSessionModel, ToolTriggerClient,
344};
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[test]
351 fn protocol_turn_options_missing_payload_deserializes_to_empty_object() {
352 let options: ProtocolTurnOptions =
353 serde_json::from_value(serde_json::json!({})).expect("deserialize options");
354
355 assert!(options.is_empty());
356 assert_eq!(options.payload, serde_json::json!({}));
357 }
358
359 #[test]
360 fn protocol_turn_options_explicit_null_is_not_empty() {
361 let options: ProtocolTurnOptions =
362 serde_json::from_value(serde_json::json!({ "payload": null }))
363 .expect("deserialize options");
364
365 assert!(!options.is_empty());
366 assert_eq!(options.payload, serde_json::Value::Null);
367 }
368
369 #[test]
370 fn root_exports_do_not_reintroduce_removed_session_state_shapes() {
371 let source = include_str!("lib.rs");
372 let removed_envelope = ["SessionState", "Envelope"].concat();
373 let removed_persisted = ["PersistedSession", "Snapshot"].concat();
374 let removed_history_rewriter = ["History", "Rewriter"].concat();
375 let removed_rewrite_trigger = ["Rewrite", "Trigger"].concat();
376 let removed_rewrite_context = ["Rewrite", "Context"].concat();
377 let removed_history_state = ["History", "State"].concat();
378 let removed_history_metadata = ["History", "Rewrite", "Metadata"].concat();
379
380 assert!(!source.contains(&removed_envelope));
381 assert!(!source.contains(&removed_persisted));
382 assert!(!source.contains(&removed_history_rewriter));
383 assert!(!source.contains(&removed_rewrite_trigger));
384 assert!(!source.contains(&removed_rewrite_context));
385 assert!(!source.contains(&removed_history_state));
386 assert!(!source.contains(&removed_history_metadata));
387 }
388
389 fn public_reexport_block(source: &str, module: &str) -> String {
390 let start = format!("pub use {module}::{{");
391 let mut block = String::new();
392 let mut collecting = false;
393 for line in source.lines() {
394 if line.trim_start().starts_with(&start) {
395 collecting = true;
396 }
397 if collecting {
398 block.push_str(line);
399 block.push('\n');
400 if line.trim_end() == "};" {
401 break;
402 }
403 }
404 }
405 assert!(!block.is_empty(), "missing public {module} re-export block");
406 block
407 }
408
409 #[test]
410 fn root_runtime_exports_exclude_internal_runtime_records() {
411 let runtime_exports = public_reexport_block(include_str!("lib.rs"), "runtime");
412 for removed in [
413 "RuntimeEffectCommand",
414 "RuntimeEffectEnvelope",
415 "RuntimeEffectKind",
416 "RuntimeEffectOutcome",
417 "RuntimeInvocation",
418 "RuntimeScope",
419 "RuntimeSessionState",
420 "QueuedWorkBatch",
421 "QueuedWorkBatchDraft",
422 "QueuedWorkPayload",
423 "prepare_process_registration",
424 "process_wake_batch_draft",
425 "require_event_replay",
426 ] {
427 assert!(
428 !runtime_exports.contains(removed),
429 "runtime root export leaked {removed}"
430 );
431 }
432 }
433
434 #[test]
435 fn root_store_exports_exclude_wire_records() {
436 let store_exports = public_reexport_block(include_str!("lib.rs"), "store");
437 for removed in [
438 "SessionHead",
439 "SessionCheckpoint",
440 "RuntimeCommit",
441 "HydratedSessionCheckpoint",
442 "PersistedSessionRead",
443 "GraphCommitDelta",
444 ] {
445 assert!(
446 !store_exports.contains(removed),
447 "store root export leaked {removed}"
448 );
449 }
450 }
451
452 #[test]
453 fn removed_manager_and_host_trait_names_stay_removed() {
454 let removed_manager = ["Runtime", "Session", "Manager"].concat();
455 let removed_host = ["Runtime", "Session", "Host"].concat();
456 let sources = [
457 include_str!("runtime/session_manager/mod.rs"),
458 include_str!("plugin/runtime_host.rs"),
459 include_str!("tool_dispatch/context.rs"),
460 include_str!("tool_provider.rs"),
461 ];
462
463 for source in sources {
464 assert!(!source.contains(&removed_manager));
465 assert!(!source.contains(&removed_host));
466 }
467 }
468}