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