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, QueuedWorkRunError, QueuedWorkRunErrorClass,
423 QueuedWorkRunHandle, QueuedWorkRunRequest, QueuedWorkWakeDisposition, QueuedWorkWakeFailure,
424 RecoveryDisposition, Residency, Resolution, ResolveOutcome, RuntimeEnvironment,
425 RuntimeEnvironmentBuilder, RuntimeError, RuntimeErrorCode, RuntimeHandle, RuntimeHostConfig,
426 RuntimeObservation, ScopedEffectController, SegmentHandover, SegmentProgress, SessionCommand,
427 SessionCommandReceipt, SessionCursor, SessionCursorError, SessionObservation,
428 SessionObservationEvent, SessionObservationEventPayload, SessionObservationSubscription,
429 SessionProcessEventKind, SessionQueueEventKind, SessionResume, SessionRevision, SessionScope,
430 SessionScopeId, SessionStoreCreateRequest, SessionStoreFactory, SessionUsageReport, SlotPolicy,
431 SystemClock, TerminationPolicy, TokenLedgerEntry, ToolCallLaunch, TurnActivity, TurnActivityId,
432 TurnActivitySink, TurnAddress, TurnAttach, TurnCancelOriginHint, TurnCancelOutcome,
433 TurnCancelReceipt, TurnCancelRequest, TurnCancellationEvidence, TurnContext, TurnEvent,
434 TurnInput, TurnInputAcceptanceReceipt, TurnInputApplication, TurnInputCheckpointBoundary,
435 TurnInputClaim, TurnInputClaimMode, TurnInputCompletion, TurnInputIngress, TurnInputState,
436 TurnIssue, TurnOptions, TurnTerminal, TurnWorkDriver, UnavailableProcessService,
437 UsageReportRow, UsageTotals, WaitKind, WaitState, apply_process_status_projection,
438 current_epoch_ms, diff_token_ledger, diff_usage_reports, ensure_durable_effect_input,
439 epoch_ms_from_system_time, process_runtime_session_ids, process_signal_event_type,
440 process_signal_name_from_event_type, process_signal_wait_key, process_wake_delivery,
441 system_time_from_epoch_ms, terminal_append_request, terminal_event_type_name,
442 validate_process_signal_name, watch_process_registry, watch_process_registry_with_sink,
443};
444pub use runtime::{DEFAULT_PROCESS_EXECUTION_CONCURRENCY, ProcessExecutionConcurrencyError};
445#[allow(unused_imports)]
446pub(crate) use runtime::{
447 LlmAttachmentSpec, ProcessEventSemantics, QueuedCheckpointTurnInput, QueuedCheckpointWork,
448 QueuedTurnWork, QueuedWorkBatch, QueuedWorkBatchDraft, QueuedWorkClaim,
449 QueuedWorkClaimBoundary, QueuedWorkCompletion, QueuedWorkItem, QueuedWorkPayload,
450 RuntimeReplay, RuntimeSubject, load_process_execution_env, materialize_process_event_semantics,
451 persist_process_execution_env, prepare_process_event_append, prepare_process_registration,
452 process_event_invocation, process_event_payload_hash, process_wake_batch_draft,
453 process_wake_input_from_event_payload, process_wake_turn_cause, process_wake_turn_text,
454 require_event_replay,
455};
456pub use session_model::{
457 PLUGIN_RUNTIME_PROTOCOL_PLUGIN_ID, PersistedPluginRuntimeEvent,
458 plugin_runtime_event_from_protocol, plugin_runtime_protocol_event,
459};
460pub use runtime::{
464 CanonicalRuntimeEffectEnvelope, LlmRequestSpec, ProcessCommand, ProcessEffectOutcome,
465 ProcessEventSemanticsSpec, RuntimeAwaitEventOptions, RuntimeEffectCommand,
466 RuntimeEffectController, RuntimeEffectControllerError, RuntimeEffectEnvelope,
467 RuntimeEffectKind, RuntimeEffectLocalExecutor, RuntimeEffectOutcome,
468 RuntimeEffectReplayMismatchSummary, RuntimeEffectReplayTrace, RuntimeInvocation, RuntimeScope,
469 RuntimeSessionState, RuntimeSleepOptions, ToolAttemptEffectOutcome, ToolAttemptLaunch,
470 ToolBatchEffectOutcome, validate_replayed_effect_envelope,
471};
472pub use schemars::JsonSchema;
473pub(crate) use session::RuntimeExecutionTracing;
474pub use session::{
475 ExecRequest, InjectedTurnInput, RuntimeExecutionContext, Session, SessionError, ToolInvocation,
476 ToolInvocationReply,
477};
478pub use session_graph::{
479 PersistedSessionConfig, PersistedTurnState, SessionGraph, SessionMessageTreeNode,
480 SessionNodePayload, SessionNodeRecord,
481};
482pub use session_model::context::PreparedContext;
483pub use session_model::{ConversationRecord, ProtocolEvent, SessionHistoryRecord};
484pub use session_model::{RuntimeSessionPolicy, SessionPolicy, SessionSpec};
485pub use store::{
486 AttachmentIntent, AttachmentManifest, AttachmentManifestEntry, BlobRef, GcReport,
487 LeaseOwnerIdentity, LeaseOwnerLiveness, LeaseTimings, LeaseTimingsError, QueuedWorkStore,
488 RuntimePersistence, SessionCommitStore, SessionExecutionLease,
489 SessionExecutionLeaseClaimOutcome, SessionExecutionLeaseCompletion, SessionExecutionLeaseFence,
490 SessionExecutionLeaseStore, SessionMeta, SessionPickerInfo, SessionReadScope, StoreError,
491 StoreMaintenance, TurnInputStore, VacuumReport,
492};
493#[allow(unused_imports)]
494pub(crate) use store::{
495 GraphCommitDelta, PersistedSessionRead, RuntimeCommitResult, SessionCheckpoint,
496 SessionHeadMeta, ensure_supported_schema_version, load_persisted_session_state,
497 load_persisted_session_state_active_path,
498};
499pub use store::{
500 HydratedSessionCheckpoint, RuntimeCommit, RuntimeTurnCommitStamp, SessionHead,
501 refresh_persisted_session_state,
502};
503pub use tool_provider::{
504 PreparedToolBatch, PreparedToolBatchCall, PreparedToolCall, ProgressSender, SandboxMessage,
505 ToolCall, ToolChildExecutionTraceHook, ToolChildProcessStarted, ToolContext,
506 ToolExecutionGrant, ToolPrepareCall, ToolPrepareContext, ToolProvider, ToolSessionAdmin,
507 ToolSessionModel, ToolSessionProcessAdmin, ToolTriggerClient,
508};
509
510#[cfg(test)]
511mod tests {
512 use super::*;
513
514 #[test]
515 fn invalid_agent_frame_seed_is_rejected_at_the_serde_boundary() {
516 let err = serde_json::from_value::<ToolControl>(serde_json::json!({
517 "type": "switch_agent_frame",
518 "frame_id": "delegate",
519 "initial_nodes": [{ "not": "a session append node" }],
520 "task": "continue the work"
521 }))
522 .expect_err("invalid seed cannot construct a tool control");
523
524 assert!(err.to_string().contains("kind"), "unexpected error: {err}");
525 }
526
527 #[test]
528 fn protocol_turn_options_missing_payload_deserializes_to_empty_object() {
529 let options: ProtocolTurnOptions = serde_json::from_value(serde_json::json!({
530 "schema_version": PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION
531 }))
532 .expect("deserialize options");
533
534 assert!(options.is_empty());
535 assert_eq!(options.schema_version, PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION);
536 assert_eq!(options.payload, serde_json::json!({}));
537 }
538
539 #[test]
540 fn protocol_turn_options_explicit_null_is_not_empty() {
541 let options: ProtocolTurnOptions = serde_json::from_value(serde_json::json!({
542 "schema_version": PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION,
543 "payload": null
544 }))
545 .expect("deserialize options");
546
547 assert!(!options.is_empty());
548 assert_eq!(options.payload, serde_json::Value::Null);
549 }
550
551 #[test]
552 fn protocol_turn_options_missing_schema_version_rejects_preversioned_state() {
553 let err =
554 serde_json::from_value::<ProtocolTurnOptions>(serde_json::json!({ "payload": {} }))
555 .expect_err("pre-versioned options should fail");
556
557 assert!(
558 err.to_string().contains(
559 "missing schema_version and were written by unsupported pre-versioned state"
560 ),
561 "{err}"
562 );
563 }
564
565 #[test]
566 fn protocol_turn_options_unsupported_schema_version_rejects_state() {
567 let err = serde_json::from_value::<ProtocolTurnOptions>(serde_json::json!({
568 "schema_version": PROTOCOL_TURN_OPTIONS_SCHEMA_VERSION + 1,
569 "payload": {}
570 }))
571 .expect_err("unsupported options version should fail");
572
573 assert!(
574 err.to_string().contains("is not supported by this binary"),
575 "{err}"
576 );
577 }
578
579 #[test]
580 fn root_exports_do_not_reintroduce_removed_session_state_shapes() {
581 let source = include_str!("lib.rs");
582 let removed_envelope = ["SessionState", "Envelope"].concat();
583 let removed_persisted = ["PersistedSession", "Snapshot"].concat();
584 let removed_history_rewriter = ["History", "Rewriter"].concat();
585 let removed_rewrite_trigger = ["Rewrite", "Trigger"].concat();
586 let removed_rewrite_context = ["Rewrite", "Context"].concat();
587 let removed_history_state = ["History", "State"].concat();
588 let removed_history_metadata = ["History", "Rewrite", "Metadata"].concat();
589
590 assert!(!source.contains(&removed_envelope));
591 assert!(!source.contains(&removed_persisted));
592 assert!(!source.contains(&removed_history_rewriter));
593 assert!(!source.contains(&removed_rewrite_trigger));
594 assert!(!source.contains(&removed_rewrite_context));
595 assert!(!source.contains(&removed_history_state));
596 assert!(!source.contains(&removed_history_metadata));
597 }
598
599 fn public_reexport_block(source: &str, module: &str) -> String {
600 let start = format!("pub use {module}::{{");
601 let mut block = String::new();
602 let mut collecting = false;
603 for line in source.lines() {
604 if line.trim_start().starts_with(&start) {
605 collecting = true;
606 }
607 if collecting {
608 block.push_str(line);
609 block.push('\n');
610 if line.trim_end() == "};" {
611 break;
612 }
613 }
614 }
615 assert!(!block.is_empty(), "missing public {module} re-export block");
616 block
617 }
618
619 #[test]
620 fn root_runtime_exports_exclude_internal_runtime_records() {
621 let runtime_exports = public_reexport_block(include_str!("lib.rs"), "runtime");
622 for removed in [
623 "RuntimeEffectCommand",
624 "RuntimeEffectEnvelope",
625 "RuntimeEffectKind",
626 "RuntimeEffectOutcome",
627 "RuntimeInvocation",
628 "RuntimeScope",
629 "RuntimeSessionState",
630 "QueuedWorkBatch",
631 "QueuedWorkBatchDraft",
632 "QueuedWorkPayload",
633 "prepare_process_registration",
634 "process_wake_batch_draft",
635 "require_event_replay",
636 ] {
637 assert!(
638 !runtime_exports.contains(removed),
639 "runtime root export leaked {removed}"
640 );
641 }
642 }
643
644 #[test]
645 fn root_store_exports_exclude_wire_records() {
646 let store_exports = public_reexport_block(include_str!("lib.rs"), "store");
647 for removed in [
648 "SessionHead",
649 "SessionCheckpoint",
650 "RuntimeCommit",
651 "HydratedSessionCheckpoint",
652 "PersistedSessionRead",
653 "GraphCommitDelta",
654 ] {
655 assert!(
656 !store_exports.contains(removed),
657 "store root export leaked {removed}"
658 );
659 }
660 }
661
662 #[test]
663 fn removed_manager_and_host_trait_names_stay_removed() {
664 let removed_manager = ["Runtime", "Session", "Manager"].concat();
665 let removed_host = ["Runtime", "Session", "Host"].concat();
666 let sources = [
667 include_str!("runtime/session_manager/mod.rs"),
668 include_str!("plugin/runtime_host.rs"),
669 include_str!("tool_dispatch/context.rs"),
670 include_str!("tool_provider.rs"),
671 ];
672
673 for source in sources {
674 assert!(!source.contains(&removed_manager));
675 assert!(!source.contains(&removed_host));
676 }
677 }
678}