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