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