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