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