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