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 session;
24pub mod session_graph;
25pub mod session_model;
26mod stable_hash;
27pub mod store;
28#[cfg(any(test, feature = "testing"))]
29pub mod testing;
30pub mod tool_dispatch;
31mod tool_provider;
32pub mod tool_registry;
33mod tool_result;
34mod trace;
35pub mod triggers;
36
37pub use lash_sansio::sansio;
38
39pub const VERSION: &str = env!("CARGO_PKG_VERSION");
40pub const SANSIO_VERSION: &str = lash_sansio::VERSION;
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43pub enum DurabilityTier {
44 Inline,
45 Durable,
46}
47
48pub use attachments::{
50 AttachmentReclamationReport, AttachmentRootSet, AttachmentStore, AttachmentStoreError,
51 AttachmentStorePersistence, FileAttachmentStore, InMemoryAttachmentStore,
52 NoopAttachmentManifest, SessionAttachmentStore, StoredAttachment, StoredBlobRef,
53 reclaim_unreferenced_attachments,
54};
55pub use chronological::{
56 BorrowedChronologicalEntry, BorrowedChronologicalMessage, BorrowedChronologicalPayload,
57 ChronologicalEntry, ChronologicalPayload, ChronologicalProjection, visit_turn_view,
58};
59pub use direct::{
60 DirectJsonSchema, DirectLlmClient, DirectLlmError, DirectMessage, DirectOutputSpec, DirectPart,
61 DirectRequest, DirectRole,
62};
63pub use lash_sansio::llm::types::{
64 GenerationOptions, LlmOutputPart, LlmRequest, LlmRequestScope, LlmResponse, LlmTerminalReason,
65};
66pub use lash_sansio::{
67 AcceptedInjectedTurnInput, AttachmentCreateMeta, AttachmentId, AttachmentMeta, AttachmentRef,
68 BaseRenderCache, CheckpointDelivery, CheckpointKind, CompactToolContract, EffectId,
69 ErrorEnvelope, ExecImage, ExecResponse, ImageMediaType, LashSchema, LlmCallError, MediaType,
70 Message, MessageOrigin, MessageRole, MessageSequence, ModelToolReturn, ModelToolReturnPart,
71 Part, PartKind, PluginMessage, PluginRuntimeEvent, PreparedPrompt, ProjectionMode,
72 PromptBuildInput, PromptBuiltin, PromptContext, PromptContribution, PromptContributionGate,
73 PromptContributionSet, PromptFingerprint, PromptLayer, PromptSlot, PromptSlotLayer,
74 PromptTemplate, PromptTemplateEntry, PromptTemplateSection, ProviderSchemaCapabilities,
75 PruneState, RenderedPrompt, ResolvedPromptLayer, ResolvedSchema, Response, SchemaContract,
76 SchemaDialect, SchemaProjectionOverride, SchemaProjectionPolicy, SchemaPurpose,
77 SchemaResolutionError, SchemaResolutionRequest, SessionEvent, TextProjectionMetadata,
78 TokenUsage, ToolActivation, ToolArgumentProjectionPolicy, ToolCallOutcome, ToolCallOutput,
79 ToolCallRecord, ToolCallStatus, ToolCancellation, ToolCatalog, ToolCatalogBuildInput,
80 ToolCatalogEntry, ToolContract, ToolControl, ToolDefinition, ToolFailure, ToolFailureClass,
81 ToolFailureSource, ToolId, ToolManifest, ToolOutputContract, ToolRetryDisposition,
82 ToolRetryPolicy, ToolScheduling, ToolValue, TurnCause, TurnFinish, TurnLimitFinalMessage,
83 TurnOutcome, TurnStop, append_assistant_text_part, build_prompt, build_tool_catalog,
84 build_turn, default_prompt_template, head_tail_truncate, messages_are_prompt_resume_safe,
85 normalized_response_parts, project_anthropic_bedrock_schema, project_for_dialect,
86 prompt_template_fingerprint, prompt_text_fingerprint, prompt_tool_names_fingerprint,
87 reasoning_part, render_turn_causes_prompt, resolve_prompt_layers, resolve_schema, shared_parts,
88 validate_tool_input, visible_response_parts, visible_response_text_from_parts,
89};
90pub use protocol_build::ProtocolBuildInput;
91pub use tool_registry::{
92 PLUGIN_TOOL_SOURCE_ID, ReconfigureError, ToolRegistry, ToolRestoreReport, ToolSourceHandle,
93 ToolState, ToolStateEntry,
94};
95pub use tool_result::{CancelHint, PendingCompletion, TimeoutBehavior, ToolResult};
96pub use triggers::{
97 InMemoryTriggerStore, TriggerDeliveryEmitOutcome, TriggerDeliveryEmitReport,
98 TriggerDeliveryReservation, TriggerDeliveryReservationStatus, TriggerEmitReport, TriggerEvent,
99 TriggerEventCatalog, TriggerEventKey, TriggerEventType, TriggerInputBinding,
100 TriggerOccurrenceFilter, TriggerOccurrenceRecord, TriggerOccurrenceRequest,
101 TriggerRegistration, TriggerRouter, TriggerStore, TriggerSubscriptionDraft,
102 TriggerSubscriptionFilter, TriggerSubscriptionRecord, TriggerTargetSummary,
103 default_trigger_source_key, deterministic_delivery_process_id, deterministic_occurrence_id,
104 empty_trigger_source_key, trigger_event_type, trigger_occurrence_request_hash,
105 validate_trigger_occurrence_request,
106};
107pub const PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION: u32 = 1;
108
109#[derive(Clone, Debug, serde::Serialize)]
110pub struct ProtocolTurnOptions {
111 pub schema_version: u32,
112 pub payload: serde_json::Value,
113}
114
115fn empty_protocol_turn_payload() -> serde_json::Value {
116 serde_json::Value::Object(serde_json::Map::new())
117}
118
119#[derive(Debug, thiserror::Error)]
120pub enum ProtocolTurnOptionsError {
121 #[error(
122 "protocol turn options are missing schema_version and were written by unsupported pre-versioned state (expected {expected})"
123 )]
124 MissingSchemaVersion { expected: u32 },
125 #[error(
126 "protocol turn options schema_version {actual} is not supported by this binary (expected {expected})"
127 )]
128 UnsupportedSchemaVersion { actual: u32, expected: u32 },
129 #[error(
130 "protocol turn options schema_version {actual} is invalid (expected integer {expected})"
131 )]
132 InvalidSchemaVersion { actual: String, expected: u32 },
133 #[error("failed to decode protocol turn options payload: {0}")]
134 Decode(#[source] serde_json::Error),
135}
136
137fn parse_protocol_turn_options_schema_version(
138 value: Option<serde_json::Value>,
139) -> Result<u32, ProtocolTurnOptionsError> {
140 let expected = PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION;
141 let Some(value) = value else {
142 return Err(ProtocolTurnOptionsError::MissingSchemaVersion { expected });
143 };
144 let Some(actual) = value
145 .as_u64()
146 .and_then(|version| u32::try_from(version).ok())
147 else {
148 return Err(ProtocolTurnOptionsError::InvalidSchemaVersion {
149 actual: value.to_string(),
150 expected,
151 });
152 };
153 ensure_protocol_turn_options_schema_version(actual)?;
154 Ok(actual)
155}
156
157fn ensure_protocol_turn_options_schema_version(
158 actual: u32,
159) -> Result<(), ProtocolTurnOptionsError> {
160 let expected = PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION;
161 if actual == expected {
162 Ok(())
163 } else {
164 Err(ProtocolTurnOptionsError::UnsupportedSchemaVersion { actual, expected })
165 }
166}
167
168impl<'de> serde::Deserialize<'de> for ProtocolTurnOptions {
169 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
170 where
171 D: serde::Deserializer<'de>,
172 {
173 #[derive(serde::Deserialize)]
174 struct ProtocolTurnOptionsWire {
175 schema_version: Option<serde_json::Value>,
176 #[serde(default = "empty_protocol_turn_payload")]
177 payload: serde_json::Value,
178 }
179
180 let wire = ProtocolTurnOptionsWire::deserialize(deserializer)?;
181 let schema_version = parse_protocol_turn_options_schema_version(wire.schema_version)
182 .map_err(serde::de::Error::custom)?;
183 Ok(Self {
184 schema_version,
185 payload: wire.payload,
186 })
187 }
188}
189
190impl Default for ProtocolTurnOptions {
191 fn default() -> Self {
192 Self::empty()
193 }
194}
195
196impl ProtocolTurnOptions {
197 pub fn empty() -> Self {
198 Self {
199 schema_version: PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION,
200 payload: serde_json::Value::Object(serde_json::Map::new()),
201 }
202 }
203
204 pub fn from_payload(payload: serde_json::Value) -> Self {
205 Self {
206 schema_version: PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION,
207 payload,
208 }
209 }
210
211 pub fn is_empty(&self) -> bool {
212 match &self.payload {
213 serde_json::Value::Object(map) => map.is_empty(),
214 _ => false,
215 }
216 }
217
218 pub fn merged_with_override(&self, override_options: &Self) -> Self {
219 match (&self.payload, &override_options.payload) {
220 (serde_json::Value::Object(base), serde_json::Value::Object(overrides)) => {
221 let mut payload = base.clone();
222 payload.extend(overrides.clone());
223 Self {
224 schema_version: PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION,
225 payload: serde_json::Value::Object(payload),
226 }
227 }
228 _ => override_options.clone(),
229 }
230 }
231
232 pub fn typed<T>(value: T) -> Result<Self, serde_json::Error>
233 where
234 T: serde::Serialize,
235 {
236 Ok(Self {
237 schema_version: PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION,
238 payload: serde_json::to_value(value)?,
239 })
240 }
241
242 pub fn decode<T>(&self) -> Result<T, ProtocolTurnOptionsError>
243 where
244 T: serde::de::DeserializeOwned,
245 {
246 ensure_protocol_turn_options_schema_version(self.schema_version)?;
247 serde_json::from_value(self.payload.clone()).map_err(ProtocolTurnOptionsError::Decode)
248 }
249}
250
251#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
252pub struct ProtocolDriverState {
253 pub plugin_id: String,
254 pub payload: serde_json::Value,
255}
256
257impl ProtocolDriverState {
258 pub fn new(plugin_id: impl Into<String>, payload: serde_json::Value) -> Self {
259 Self {
260 plugin_id: plugin_id.into(),
261 payload,
262 }
263 }
264}
265
266#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
267pub struct HostTurnProtocol;
268
269impl lash_sansio::TurnProtocol for HostTurnProtocol {
270 type Event = crate::session_model::ProtocolEvent;
271 type Termination = ProtocolTurnOptions;
272 type DriverState = ProtocolDriverState;
273}
274
275pub type Effect = lash_sansio::Effect<HostTurnProtocol>;
276pub type DriverAction = lash_sansio::DriverAction<HostTurnProtocol>;
277pub type DriverContextView<'a> = lash_sansio::DriverContextView<'a, HostTurnProtocol>;
278pub type TurnDriverConfig = lash_sansio::TurnDriverConfig<HostTurnProtocol>;
279pub type TurnDriverPreamble = lash_sansio::TurnDriverPreamble<HostTurnProtocol>;
280pub type ProjectorContext<'a> = lash_sansio::ProjectorContext<'a, HostTurnProtocol>;
281pub type PreparedTurnMachine = lash_sansio::PreparedTurnMachine<HostTurnProtocol>;
282pub type SansIoTurnInput = lash_sansio::SansIoTurnInput<HostTurnProtocol>;
283pub type TurnMachine = lash_sansio::TurnMachine<HostTurnProtocol>;
284pub type TurnMachineConfig = lash_sansio::TurnMachineConfig<HostTurnProtocol>;
285#[cfg(feature = "otel-trace")]
286pub use lash_trace::otel::{OtelTraceOptions, OtelTraceSink};
287pub use lash_trace::{
288 JsonlTraceSink, TraceAttachment, TraceBranchSelection, TraceContentBlock, TraceContext,
289 TraceError, TraceEvent, TraceLabelMetadata, TraceLevel, TraceLlmMessage, TraceLlmRequest,
290 TraceLlmResponse, TracePromptComponent, TraceProviderStreamEvent, TraceRecord,
291 TraceRuntimeScope, TraceRuntimeStreamEvent, TraceRuntimeSubject, TraceSink, TraceSinkError,
292 TraceTokenUsage, TraceToolSpec,
293};
294pub use llm::transport::{LlmTransportError, ProviderFailure, ProviderFailureKind};
295pub use model::{ModelLimits, ModelSpec};
296pub use plugin::{
297 AgentFrameAssignment, AgentFrameId, AgentFrameReason, AgentFrameRecord, AgentFrameStatus,
298 AppendSessionNodesRequest, AppendSessionNodesResult, AssistantResponseHookContext,
299 AssistantResponseTransform, AssistantStreamHookContext, AssistantStreamTransform,
300 CheckpointHookContext, CompactionContext, ContextCompaction, ContextCompactor, ContextError,
301 ContextRegistrations, DirectCompletion, DirectLlmCompletion, OpenAgentFrameRequest,
302 OpenAgentFrameResult, PersistentRuntimeServices, PluginCommand, PluginCommandContext,
303 PluginCommandOutcome, PluginCommandReceipt, PluginDirective, PluginError,
304 PluginExtensionContribution, PluginExtensions, PluginFactory, PluginHost, PluginLifecycleEvent,
305 PluginLifecycleEventHook, PluginOperation, PluginOperationDef, PluginOperationFailure,
306 PluginOperationInvokeError, PluginOperationKind, PluginOptions, PluginOwned, PluginQuery,
307 PluginQueryContext, PluginRegistrar, PluginRuntimeDirective, PluginSession,
308 PluginSessionContext, PluginSessionSnapshot, PluginSnapshotArtifact, PluginSnapshotEntry,
309 PluginSnapshotMeta, PluginSpec, PluginSpecFactory, PluginTask, PluginTaskContext,
310 PluginTaskOutcome, PluginTaskReceipt, ProcessEngineContributionContext, PromptHookContext,
311 ProtocolBeforeLlmCallContext, ProtocolLlmCallAction, RuntimeServices, SessionAppendNode,
312 SessionConfigChangedContext, SessionContextOverlay, SessionCreateRequest, SessionGraphService,
313 SessionHandle, SessionLifecycleService, SessionParam, SessionPlugin, SessionPluginSource,
314 SessionReadView, SessionRelation, SessionSnapshot, SessionStartPoint,
315 SessionStateChangedContext, SessionStateService, SessionToolAccess, SessionTurnInput,
316 SessionTurnRequest, SnapshotReader, SnapshotWriter, SubagentSessionContext,
317 ToolCatalogContribution, ToolResultProjectionContext, ToolResultProjector,
318 TriggerEventRegistrations, TurnContextTransform, TurnHookContext, TurnResultHookContext,
319 TurnResultSummary, TurnTransformContext, plugin_operation_def,
320};
321pub use plugin_stack::PluginStack;
322pub use provider::{
323 CacheRetention, EmptyProviderResolver, LlmTimeouts, MapProviderResolver, ModelCapability,
324 ModelEffortValidationCategory, ModelEffortValidationError, Provider, ProviderBinding,
325 ProviderComponents, ProviderFactory, ProviderHandle, ProviderOptions, ProviderResolutionError,
326 ProviderSpec, ReasoningCapability, ReasoningEncoding, RequestTimeout, RuntimeProviderResolver,
327 SingleProviderResolver,
328};
329#[cfg(any(test, feature = "testing"))]
330pub use runtime::TestLocalProcessRegistry;
331pub use runtime::{
332 AbandonEvidence, AbandonRequest, AbandonWriter, AgentFrameRun, AssembledTurn, AssistantOutput,
333 AwaitEventKey, AwaitEventResolver, AwaitEventWaitIdentity, CausalRef, Clock, CodeOutputRecord,
334 DefaultProcessCancelAbility, DeliveryPolicy, DirectCompletionClient, DurableProcessWorker,
335 DurableProcessWorkerConfig, DurableStoreFacet, EffectHost, EmbeddedRuntimeBuilder,
336 EmbeddedRuntimeHost, EventSink, ExecutionScope, ExecutionSummary, ExternalCompletionError,
337 InMemoryLiveReplayStore, InMemoryLiveReplayStoreConfig, InMemoryProcessExecutionEnvStore,
338 InMemorySessionStore, InMemorySessionStoreFactory, InlineEffectHost, InlineProcessRunHandle,
339 InlineRuntimeEffectController, InputItem, LashRuntime, LiveReplayGap, LiveReplayGapReason,
340 LiveReplayResult, LiveReplayStore, LiveReplayStoreError, LiveReplaySubscribeResult,
341 LiveReplaySubscription, MergeKey, NoopEventSink, NoopTurnActivitySink, ObservedProcess,
342 ObservedProcessEvent, ObservedWorkItem, OutputState, PROCESS_LEASE_SCHEMA_VERSION,
343 ParkedSession, PendingTurnInput, PendingTurnInputCancelOutcome, PendingTurnInputCancelResult,
344 PendingTurnInputCancelTarget, PendingTurnInputClaimDiagnostics, PendingTurnInputDraft,
345 PendingTurnInputSuffixCancelOutcome, ProcessAttach, ProcessAwaitOutput, ProcessAwaiter,
346 ProcessCancelAbility, ProcessCancelAllRequest, ProcessCancelRequest, ProcessCancelSource,
347 ProcessCancelSummary, ProcessChangeCursor, ProcessChangeHub, ProcessCompletionAuthority,
348 ProcessDrainReport, ProcessEngine, ProcessEngineRegistry, ProcessEngineRunContext,
349 ProcessEngineRunGuard, ProcessEngineRuntimeContext, ProcessEngineValidationContext,
350 ProcessEvent, ProcessEventAppendPlan, ProcessEventAppendRequest, ProcessEventAppendResult,
351 ProcessEventSink, ProcessEventType, ProcessExecutionContext, ProcessExecutionEnvRef,
352 ProcessExecutionEnvSpec, ProcessExecutionEnvStore, ProcessExternalRef, ProcessHandleDescriptor,
353 ProcessHandleGrant, ProcessHandleSummary, ProcessId, ProcessIdentity, ProcessInput,
354 ProcessLease, ProcessLeaseClaimOutcome, ProcessLeaseCompletion, ProcessLifecycleStatus,
355 ProcessListFilter, ProcessListMode, ProcessLiveReferenceSummary, ProcessOpScope,
356 ProcessOriginator, ProcessProvenance, ProcessPruneReport, ProcessRecord, ProcessRegistration,
357 ProcessRegistry, ProcessRunHandle, ProcessRuntimeHost, ProcessService,
358 ProcessSessionDeleteReport, ProcessSpawnProvenance, ProcessStartGrant, ProcessStartOptions,
359 ProcessStartRequest, ProcessStarted, ProcessStatus, ProcessStatusFilter,
360 ProcessTerminalSemantics, ProcessTerminalSpec, ProcessTerminalState, ProcessValueSelector,
361 ProcessWake, ProcessWakeDedupeKey, ProcessWakeDelivery, ProcessWakeDeliveryRequest,
362 ProcessWakeSpec, ProcessWorkDriver, ProcessWorkObserver, ProcessWorkSnapshot, PromptUsage,
363 ProtocolSessionExtension, ProtocolSessionExtensionHandle, ProtocolTurnExtension,
364 ProtocolTurnExtensionHandle, QueuedWorkDriver, QueuedWorkRunHandle, QueuedWorkRunRequest,
365 RecoveryDisposition, Residency, Resolution, ResolveOutcome, RuntimeEnvironment,
366 RuntimeEnvironmentBuilder, RuntimeError, RuntimeErrorCode, RuntimeHandle, RuntimeHostConfig,
367 RuntimeObservation, ScopedEffectController, SessionCommand, SessionCommandReceipt,
368 SessionCursor, SessionCursorError, SessionObservation, SessionObservationEvent,
369 SessionObservationEventPayload, SessionObservationSubscription, SessionProcessEventKind,
370 SessionQueueEventKind, SessionResume, SessionRevision, SessionScope, SessionScopeId,
371 SessionStoreCreateRequest, SessionStoreFactory, SessionUsageReport, SlotPolicy, SystemClock,
372 TerminationPolicy, TokenLedgerEntry, ToolCallLaunch, TurnActivity, TurnActivityId,
373 TurnActivitySink, TurnContext, TurnEvent, TurnInput, TurnInputCheckpointBoundary,
374 TurnInputClaim, TurnInputClaimMode, TurnInputCompletion, TurnInputIngress, TurnInputState,
375 TurnIssue, TurnOptions, UnavailableProcessService, UsageReportRow, UsageTotals, WaitKind,
376 WaitState, apply_process_status_projection, current_epoch_ms, diff_token_ledger,
377 diff_usage_reports, ensure_durable_effect_input, epoch_ms_from_system_time,
378 process_signal_event_type, process_signal_name_from_event_type, process_signal_wait_key,
379 process_wake_delivery, system_time_from_epoch_ms, terminal_append_request,
380 terminal_event_type_name, validate_process_signal_name, watch_process_registry,
381 watch_process_registry_with_sink,
382};
383#[allow(unused_imports)]
384pub(crate) use runtime::{
385 LlmAttachmentSpec, ProcessEventSemantics, QueuedCheckpointTurnInput, QueuedCheckpointWork,
386 QueuedTurnWork, QueuedWorkBatch, QueuedWorkBatchDraft, QueuedWorkClaim,
387 QueuedWorkClaimBoundary, QueuedWorkCompletion, QueuedWorkItem, QueuedWorkPayload,
388 RuntimeReplay, RuntimeScope, RuntimeSubject, load_process_execution_env,
389 materialize_process_event_semantics, persist_process_execution_env,
390 prepare_process_event_append, prepare_process_registration, process_event_invocation,
391 process_event_payload_hash, process_wake_batch_draft, process_wake_input_from_event_payload,
392 process_wake_turn_cause, process_wake_turn_text, require_event_replay,
393};
394pub use session_model::{
395 PLUGIN_RUNTIME_PROTOCOL_PLUGIN_ID, PersistedPluginRuntimeEvent,
396 plugin_runtime_event_from_protocol, plugin_runtime_protocol_event,
397};
398pub use runtime::{
402 LlmRequestSpec, ProcessCommand, ProcessEffectOutcome, ProcessEventSemanticsSpec,
403 RuntimeAwaitEventOptions, RuntimeEffectCommand, RuntimeEffectController,
404 RuntimeEffectControllerError, RuntimeEffectEnvelope, RuntimeEffectKind,
405 RuntimeEffectLocalExecutor, RuntimeEffectOutcome, RuntimeInvocation, RuntimeSessionState,
406 ToolAttemptEffectOutcome, ToolAttemptLaunch, ToolBatchEffectOutcome,
407};
408pub use schemars::JsonSchema;
409pub(crate) use session::RuntimeExecutionTracing;
410pub use session::{
411 ExecRequest, InjectedTurnInput, RuntimeExecutionContext, Session, SessionError, ToolInvocation,
412 ToolInvocationReply,
413};
414pub use session_graph::{
415 PersistedSessionConfig, PersistedTurnState, SessionGraph, SessionMessageTreeNode,
416 SessionNodePayload, SessionNodeRecord,
417};
418pub use session_model::context::PreparedContext;
419pub use session_model::{ConversationRecord, ProtocolEvent, SessionEventRecord};
420pub use session_model::{RuntimeSessionPolicy, SessionPolicy, SessionSpec};
421pub use store::{
422 AttachmentIntent, AttachmentManifest, AttachmentManifestEntry, BlobRef, GcReport,
423 LeaseOwnerIdentity, LeaseOwnerLiveness, LeaseTimings, LeaseTimingsError, QueuedWorkStore,
424 RuntimePersistence, SessionCommitStore, SessionExecutionLease,
425 SessionExecutionLeaseClaimOutcome, SessionExecutionLeaseCompletion, SessionExecutionLeaseFence,
426 SessionExecutionLeaseStore, SessionMeta, SessionPickerInfo, SessionReadScope, StoreError,
427 StoreMaintenance, TurnInputStore, VacuumReport,
428};
429#[allow(unused_imports)]
430pub(crate) use store::{
431 GraphCommitDelta, PersistedSessionRead, RuntimeCommitResult, SessionCheckpoint,
432 SessionHeadMeta, ensure_supported_schema_version, load_persisted_session_state,
433 load_persisted_session_state_active_path,
434};
435pub use store::{
436 HydratedSessionCheckpoint, RuntimeCommit, RuntimeTurnCommitStamp, SessionHead,
437 refresh_persisted_session_state,
438};
439pub use tool_provider::{
440 PreparedToolBatch, PreparedToolBatchCall, PreparedToolCall, ProgressSender, SandboxMessage,
441 ToolCall, ToolChildExecutionTraceHook, ToolChildProcessStarted, ToolContext,
442 ToolDurableEffects, ToolExecutionGrant, ToolPrepareCall, ToolPrepareContext, ToolProvider,
443 ToolSessionAdmin, ToolSessionModel, ToolSessionProcessAdmin, ToolTriggerClient,
444};
445
446#[cfg(test)]
447mod tests {
448 use super::*;
449
450 #[test]
451 fn protocol_turn_options_missing_payload_deserializes_to_empty_object() {
452 let options: ProtocolTurnOptions = serde_json::from_value(serde_json::json!({
453 "schema_version": PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION
454 }))
455 .expect("deserialize options");
456
457 assert!(options.is_empty());
458 assert_eq!(options.schema_version, PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION);
459 assert_eq!(options.payload, serde_json::json!({}));
460 }
461
462 #[test]
463 fn protocol_turn_options_explicit_null_is_not_empty() {
464 let options: ProtocolTurnOptions = serde_json::from_value(serde_json::json!({
465 "schema_version": PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION,
466 "payload": null
467 }))
468 .expect("deserialize options");
469
470 assert!(!options.is_empty());
471 assert_eq!(options.payload, serde_json::Value::Null);
472 }
473
474 #[test]
475 fn protocol_turn_options_missing_schema_version_rejects_preversioned_state() {
476 let err =
477 serde_json::from_value::<ProtocolTurnOptions>(serde_json::json!({ "payload": {} }))
478 .expect_err("pre-versioned options should fail");
479
480 assert!(
481 err.to_string().contains(
482 "missing schema_version and were written by unsupported pre-versioned state"
483 ),
484 "{err}"
485 );
486 }
487
488 #[test]
489 fn protocol_turn_options_unsupported_schema_version_rejects_state() {
490 let err = serde_json::from_value::<ProtocolTurnOptions>(serde_json::json!({
491 "schema_version": PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION + 1,
492 "payload": {}
493 }))
494 .expect_err("unsupported options version should fail");
495
496 assert!(
497 err.to_string().contains("is not supported by this binary"),
498 "{err}"
499 );
500 }
501
502 #[test]
503 fn root_exports_do_not_reintroduce_removed_session_state_shapes() {
504 let source = include_str!("lib.rs");
505 let removed_envelope = ["SessionState", "Envelope"].concat();
506 let removed_persisted = ["PersistedSession", "Snapshot"].concat();
507 let removed_history_rewriter = ["History", "Rewriter"].concat();
508 let removed_rewrite_trigger = ["Rewrite", "Trigger"].concat();
509 let removed_rewrite_context = ["Rewrite", "Context"].concat();
510 let removed_history_state = ["History", "State"].concat();
511 let removed_history_metadata = ["History", "Rewrite", "Metadata"].concat();
512
513 assert!(!source.contains(&removed_envelope));
514 assert!(!source.contains(&removed_persisted));
515 assert!(!source.contains(&removed_history_rewriter));
516 assert!(!source.contains(&removed_rewrite_trigger));
517 assert!(!source.contains(&removed_rewrite_context));
518 assert!(!source.contains(&removed_history_state));
519 assert!(!source.contains(&removed_history_metadata));
520 }
521
522 fn public_reexport_block(source: &str, module: &str) -> String {
523 let start = format!("pub use {module}::{{");
524 let mut block = String::new();
525 let mut collecting = false;
526 for line in source.lines() {
527 if line.trim_start().starts_with(&start) {
528 collecting = true;
529 }
530 if collecting {
531 block.push_str(line);
532 block.push('\n');
533 if line.trim_end() == "};" {
534 break;
535 }
536 }
537 }
538 assert!(!block.is_empty(), "missing public {module} re-export block");
539 block
540 }
541
542 #[test]
543 fn root_runtime_exports_exclude_internal_runtime_records() {
544 let runtime_exports = public_reexport_block(include_str!("lib.rs"), "runtime");
545 for removed in [
546 "RuntimeEffectCommand",
547 "RuntimeEffectEnvelope",
548 "RuntimeEffectKind",
549 "RuntimeEffectOutcome",
550 "RuntimeInvocation",
551 "RuntimeScope",
552 "RuntimeSessionState",
553 "QueuedWorkBatch",
554 "QueuedWorkBatchDraft",
555 "QueuedWorkPayload",
556 "prepare_process_registration",
557 "process_wake_batch_draft",
558 "require_event_replay",
559 ] {
560 assert!(
561 !runtime_exports.contains(removed),
562 "runtime root export leaked {removed}"
563 );
564 }
565 }
566
567 #[test]
568 fn root_store_exports_exclude_wire_records() {
569 let store_exports = public_reexport_block(include_str!("lib.rs"), "store");
570 for removed in [
571 "SessionHead",
572 "SessionCheckpoint",
573 "RuntimeCommit",
574 "HydratedSessionCheckpoint",
575 "PersistedSessionRead",
576 "GraphCommitDelta",
577 ] {
578 assert!(
579 !store_exports.contains(removed),
580 "store root export leaked {removed}"
581 );
582 }
583 }
584
585 #[test]
586 fn removed_manager_and_host_trait_names_stay_removed() {
587 let removed_manager = ["Runtime", "Session", "Manager"].concat();
588 let removed_host = ["Runtime", "Session", "Host"].concat();
589 let sources = [
590 include_str!("runtime/session_manager/mod.rs"),
591 include_str!("plugin/runtime_host.rs"),
592 include_str!("tool_dispatch/context.rs"),
593 include_str!("tool_provider.rs"),
594 ];
595
596 for source in sources {
597 assert!(!source.contains(&removed_manager));
598 assert!(!source.contains(&removed_host));
599 }
600 }
601}