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, ProcessEngineContributionContext, PromptHookContext,
308 ProtocolBeforeLlmCallContext, ProtocolLlmCallAction, RuntimeServices, SessionAppendNode,
309 SessionConfigChangedContext, SessionContextOverlay, SessionCreateRequest, SessionGraphService,
310 SessionHandle, SessionLifecycleService, SessionParam, SessionPlugin, SessionPluginSource,
311 SessionReadView, SessionRelation, SessionSnapshot, SessionStartPoint,
312 SessionStateChangedContext, SessionStateService, SessionToolAccess, SessionTurnInput,
313 SessionTurnRequest, SnapshotReader, SnapshotWriter, SubagentSessionContext,
314 ToolCatalogContribution, ToolResultProjectionContext, ToolResultProjector,
315 TriggerEventRegistrations, TurnContextTransform, TurnHookContext, TurnResultHookContext,
316 TurnResultSummary, TurnTransformContext, plugin_operation_def,
317};
318pub use plugin_stack::PluginStack;
319pub use provider::{
320 CacheRetention, EmptyProviderResolver, LlmTimeouts, MapProviderResolver, Provider,
321 ProviderBinding, ProviderComponents, ProviderFactory, ProviderHandle, ProviderModelPolicy,
322 ProviderOptions, ProviderResolutionError, ProviderSpec, RequestTimeout,
323 RuntimeProviderResolver, SingleProviderResolver, StaticModelPolicy,
324};
325#[cfg(any(test, feature = "testing"))]
326pub use runtime::TestLocalProcessRegistry;
327pub use runtime::{
328 AgentFrameRun, AssembledTurn, AssistantOutput, AwaitEventKey, AwaitEventResolver,
329 AwaitEventWaitIdentity, CausalRef, Clock, CodeOutputRecord, DefaultProcessCancelAbility,
330 DeliveryPolicy, DirectCompletionClient, DurableProcessWorker, DurableProcessWorkerConfig,
331 DurableStoreFacet, EffectHost, EmbeddedRuntimeBuilder, EmbeddedRuntimeHost, EventSink,
332 ExecutionScope, ExecutionSummary, ExternalCompletionError, InMemoryLiveReplayStore,
333 InMemoryLiveReplayStoreConfig, InMemoryProcessExecutionEnvStore, InMemorySessionStore,
334 InMemorySessionStoreFactory, InlineEffectHost, InlineProcessRunHandle,
335 InlineRuntimeEffectController, InputItem, LashRuntime, LiveReplayGap, LiveReplayGapReason,
336 LiveReplayResult, LiveReplayStore, LiveReplayStoreError, LiveReplaySubscribeResult,
337 LiveReplaySubscription, MergeKey, NoopEventSink, NoopTurnActivitySink, ObservedProcess,
338 ObservedProcessEvent, ObservedWorkItem, OutputState, PROCESS_LEASE_SCHEMA_VERSION,
339 ParkedSession, PendingTurnInput, PendingTurnInputCancelOutcome, PendingTurnInputCancelResult,
340 PendingTurnInputCancelTarget, PendingTurnInputClaimDiagnostics, PendingTurnInputDraft,
341 PendingTurnInputSuffixCancelOutcome, ProcessAwaitOutput, ProcessCancelAbility,
342 ProcessCancelAllRequest, ProcessCancelRequest, ProcessCancelSource, ProcessCancelSummary,
343 ProcessEngine, ProcessEngineRegistry, ProcessEngineRunContext, ProcessEngineRunGuard,
344 ProcessEngineRuntimeContext, ProcessEngineValidationContext, ProcessEvent,
345 ProcessEventAppendPlan, ProcessEventAppendRequest, ProcessEventAppendResult, ProcessEventType,
346 ProcessExecutionContext, ProcessExecutionEnvRef, ProcessExecutionEnvSpec,
347 ProcessExecutionEnvStore, ProcessExternalRef, ProcessHandleDescriptor, ProcessHandleGrant,
348 ProcessHandleSummary, ProcessId, ProcessIdentity, ProcessInput, ProcessLease,
349 ProcessLeaseCompletion, ProcessLifecycleStatus, ProcessListFilter, ProcessListMode,
350 ProcessOpScope, ProcessOriginator, ProcessProvenance, ProcessRecord, ProcessRegistration,
351 ProcessRegistry, ProcessRunHandle, ProcessRuntimeHost, ProcessService,
352 ProcessSessionDeleteReport, ProcessSpawnProvenance, ProcessStartGrant, ProcessStartOptions,
353 ProcessStartRequest, ProcessStatus, ProcessStatusFilter, ProcessTerminalSemantics,
354 ProcessTerminalSpec, ProcessTerminalState, ProcessValueSelector, ProcessWake,
355 ProcessWakeDedupeKey, ProcessWakeDelivery, ProcessWakeDeliveryRequest, ProcessWakeSpec,
356 ProcessWorkDriver, ProcessWorkObserver, ProcessWorkSnapshot, PromptUsage,
357 ProtocolSessionExtension, ProtocolSessionExtensionHandle, ProtocolTurnExtension,
358 ProtocolTurnExtensionHandle, QueuedWorkDriver, QueuedWorkRunHandle, QueuedWorkRunRequest,
359 Residency, Resolution, ResolveOutcome, RuntimeEnvironment, RuntimeEnvironmentBuilder,
360 RuntimeError, RuntimeErrorCode, RuntimeHandle, RuntimeHostConfig, RuntimeObservation,
361 ScopedEffectController, SessionCommand, SessionCommandReceipt, SessionCursor,
362 SessionCursorError, SessionObservation, SessionObservationEvent,
363 SessionObservationEventPayload, SessionObservationSubscription, SessionProcessEventKind,
364 SessionQueueEventKind, SessionResume, SessionRevision, SessionScope, SessionScopeId,
365 SessionStoreCreateRequest, SessionStoreFactory, SessionUsageReport, SlotPolicy, SystemClock,
366 TerminationPolicy, TokenLedgerEntry, ToolCallLaunch, TurnActivity, TurnActivityId,
367 TurnActivitySink, TurnContext, TurnEvent, TurnInput, TurnInputCheckpointBoundary,
368 TurnInputClaim, TurnInputClaimMode, TurnInputCompletion, TurnInputIngress, TurnInputState,
369 TurnIssue, TurnOptions, UnavailableProcessService, UsageReportRow, UsageTotals, WaitKind,
370 WaitState, apply_process_status_projection, current_epoch_ms, diff_token_ledger,
371 diff_usage_reports, ensure_durable_effect_input, epoch_ms_from_system_time,
372 process_signal_event_type, process_signal_name_from_event_type, process_signal_wait_key,
373 process_wake_delivery, system_time_from_epoch_ms, validate_process_signal_name,
374};
375#[allow(unused_imports)]
376pub(crate) use runtime::{
377 LlmAttachmentSpec, ProcessEventSemantics, QUEUED_WORK_CLAIM_TTL_MS, QueuedCheckpointTurnInput,
378 QueuedCheckpointWork, QueuedTurnWork, QueuedWorkBatch, QueuedWorkBatchDraft, QueuedWorkClaim,
379 QueuedWorkClaimBoundary, QueuedWorkCompletion, QueuedWorkItem, QueuedWorkPayload,
380 RuntimeReplay, RuntimeScope, RuntimeSubject, TURN_INPUT_CLAIM_TTL_MS,
381 load_process_execution_env, materialize_process_event_semantics, persist_process_execution_env,
382 prepare_process_event_append, prepare_process_registration, process_event_invocation,
383 process_event_payload_hash, process_wake_batch_draft, process_wake_input_from_event_payload,
384 process_wake_turn_cause, process_wake_turn_text, require_event_replay,
385};
386pub use session_model::{
387 PLUGIN_RUNTIME_PROTOCOL_PLUGIN_ID, PersistedPluginRuntimeEvent,
388 plugin_runtime_event_from_protocol, plugin_runtime_protocol_event,
389};
390pub use runtime::{
394 LlmRequestSpec, ProcessCommand, ProcessEffectOutcome, ProcessEventSemanticsSpec,
395 RuntimeEffectCommand, RuntimeEffectController, RuntimeEffectControllerError,
396 RuntimeEffectEnvelope, RuntimeEffectKind, RuntimeEffectLocalExecutor, RuntimeEffectOutcome,
397 RuntimeInvocation, RuntimeSessionState, ToolAttemptEffectOutcome, ToolAttemptLaunch,
398 ToolBatchEffectOutcome,
399};
400pub use schemars::JsonSchema;
401pub use session::{
402 ExecRequest, InjectedTurnInput, RuntimeExecutionContext, Session, SessionError, ToolInvocation,
403 ToolInvocationReply,
404};
405pub use session_graph::{
406 PersistedSessionConfig, PersistedTurnState, SessionGraph, SessionMessageTreeNode,
407 SessionNodePayload, SessionNodeRecord,
408};
409pub use session_model::context::PreparedContext;
410pub use session_model::{ConversationRecord, ProtocolEvent, SessionEventRecord};
411pub use session_model::{RuntimeSessionPolicy, SessionPolicy, SessionSpec};
412pub use store::{
413 AttachmentIntent, AttachmentManifest, AttachmentManifestEntry, BlobRef, GcReport,
414 LeaseOwnerIdentity, LeaseOwnerLiveness, RuntimePersistence, SessionExecutionLease,
415 SessionExecutionLeaseClaimOutcome, SessionExecutionLeaseCompletion, SessionExecutionLeaseFence,
416 SessionMeta, SessionPickerInfo, SessionReadScope, StoreError, VacuumReport,
417};
418#[allow(unused_imports)]
419pub(crate) use store::{
420 GraphCommitDelta, PersistedSessionRead, RuntimeCommitResult, SessionCheckpoint,
421 SessionHeadMeta, ensure_supported_schema_version, load_persisted_session_state,
422 load_persisted_session_state_active_path,
423};
424pub use store::{
425 HydratedSessionCheckpoint, RuntimeCommit, RuntimeTurnCommitStamp, SessionHead,
426 refresh_persisted_session_state,
427};
428pub use tool_provider::{
429 PreparedToolBatch, PreparedToolBatchCall, PreparedToolCall, ProgressSender, SandboxMessage,
430 ToolCall, ToolChildExecutionTraceHook, ToolChildProcessStarted, ToolContext,
431 ToolDurableEffects, ToolExecutionGrant, ToolPrepareCall, ToolPrepareContext, ToolProvider,
432 ToolSessionAdmin, ToolSessionModel, ToolTriggerClient,
433};
434
435#[cfg(test)]
436mod tests {
437 use super::*;
438
439 #[test]
440 fn protocol_turn_options_missing_payload_deserializes_to_empty_object() {
441 let options: ProtocolTurnOptions = serde_json::from_value(serde_json::json!({
442 "schema_version": PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION
443 }))
444 .expect("deserialize options");
445
446 assert!(options.is_empty());
447 assert_eq!(options.schema_version, PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION);
448 assert_eq!(options.payload, serde_json::json!({}));
449 }
450
451 #[test]
452 fn protocol_turn_options_explicit_null_is_not_empty() {
453 let options: ProtocolTurnOptions = serde_json::from_value(serde_json::json!({
454 "schema_version": PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION,
455 "payload": null
456 }))
457 .expect("deserialize options");
458
459 assert!(!options.is_empty());
460 assert_eq!(options.payload, serde_json::Value::Null);
461 }
462
463 #[test]
464 fn protocol_turn_options_missing_schema_version_rejects_preversioned_state() {
465 let err =
466 serde_json::from_value::<ProtocolTurnOptions>(serde_json::json!({ "payload": {} }))
467 .expect_err("pre-versioned options should fail");
468
469 assert!(
470 err.to_string().contains(
471 "missing schema_version and were written by unsupported pre-versioned state"
472 ),
473 "{err}"
474 );
475 }
476
477 #[test]
478 fn protocol_turn_options_unsupported_schema_version_rejects_state() {
479 let err = serde_json::from_value::<ProtocolTurnOptions>(serde_json::json!({
480 "schema_version": PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION + 1,
481 "payload": {}
482 }))
483 .expect_err("unsupported options version should fail");
484
485 assert!(
486 err.to_string().contains("is not supported by this binary"),
487 "{err}"
488 );
489 }
490
491 #[test]
492 fn root_exports_do_not_reintroduce_removed_session_state_shapes() {
493 let source = include_str!("lib.rs");
494 let removed_envelope = ["SessionState", "Envelope"].concat();
495 let removed_persisted = ["PersistedSession", "Snapshot"].concat();
496 let removed_history_rewriter = ["History", "Rewriter"].concat();
497 let removed_rewrite_trigger = ["Rewrite", "Trigger"].concat();
498 let removed_rewrite_context = ["Rewrite", "Context"].concat();
499 let removed_history_state = ["History", "State"].concat();
500 let removed_history_metadata = ["History", "Rewrite", "Metadata"].concat();
501
502 assert!(!source.contains(&removed_envelope));
503 assert!(!source.contains(&removed_persisted));
504 assert!(!source.contains(&removed_history_rewriter));
505 assert!(!source.contains(&removed_rewrite_trigger));
506 assert!(!source.contains(&removed_rewrite_context));
507 assert!(!source.contains(&removed_history_state));
508 assert!(!source.contains(&removed_history_metadata));
509 }
510
511 fn public_reexport_block(source: &str, module: &str) -> String {
512 let start = format!("pub use {module}::{{");
513 let mut block = String::new();
514 let mut collecting = false;
515 for line in source.lines() {
516 if line.trim_start().starts_with(&start) {
517 collecting = true;
518 }
519 if collecting {
520 block.push_str(line);
521 block.push('\n');
522 if line.trim_end() == "};" {
523 break;
524 }
525 }
526 }
527 assert!(!block.is_empty(), "missing public {module} re-export block");
528 block
529 }
530
531 #[test]
532 fn root_runtime_exports_exclude_internal_runtime_records() {
533 let runtime_exports = public_reexport_block(include_str!("lib.rs"), "runtime");
534 for removed in [
535 "RuntimeEffectCommand",
536 "RuntimeEffectEnvelope",
537 "RuntimeEffectKind",
538 "RuntimeEffectOutcome",
539 "RuntimeInvocation",
540 "RuntimeScope",
541 "RuntimeSessionState",
542 "QueuedWorkBatch",
543 "QueuedWorkBatchDraft",
544 "QueuedWorkPayload",
545 "prepare_process_registration",
546 "process_wake_batch_draft",
547 "require_event_replay",
548 ] {
549 assert!(
550 !runtime_exports.contains(removed),
551 "runtime root export leaked {removed}"
552 );
553 }
554 }
555
556 #[test]
557 fn root_store_exports_exclude_wire_records() {
558 let store_exports = public_reexport_block(include_str!("lib.rs"), "store");
559 for removed in [
560 "SessionHead",
561 "SessionCheckpoint",
562 "RuntimeCommit",
563 "HydratedSessionCheckpoint",
564 "PersistedSessionRead",
565 "GraphCommitDelta",
566 ] {
567 assert!(
568 !store_exports.contains(removed),
569 "store root export leaked {removed}"
570 );
571 }
572 }
573
574 #[test]
575 fn removed_manager_and_host_trait_names_stay_removed() {
576 let removed_manager = ["Runtime", "Session", "Manager"].concat();
577 let removed_host = ["Runtime", "Session", "Host"].concat();
578 let sources = [
579 include_str!("runtime/session_manager/mod.rs"),
580 include_str!("plugin/runtime_host.rs"),
581 include_str!("tool_dispatch/context.rs"),
582 include_str!("tool_provider.rs"),
583 ];
584
585 for source in sources {
586 assert!(!source.contains(&removed_manager));
587 assert!(!source.contains(&removed_host));
588 }
589 }
590}