Skip to main content

lash_core/
lib.rs

1//! Runtime kernel for Lash.
2//!
3//! The process kernel intentionally understands `ToolCall`, `SessionTurn`, and
4//! `External` because those inputs carry runtime mechanisms core must enforce:
5//! tool orchestration, child-session turns, and externally completed work. New
6//! process runtimes should use `ProcessInput::Engine { kind, payload }` unless
7//! core must understand their semantics to enforce a kernel mechanism.
8//!
9//! Protocols follow the same boundary: core owns the `HostTurnProtocol` state
10//! shape and the `ProtocolDriverPlugin` slot, while external protocol crates
11//! provide the driver implementation.
12
13pub mod attachments;
14pub mod chronological;
15pub mod direct;
16pub mod llm;
17mod model;
18pub mod plugin;
19mod plugin_stack;
20mod protocol_build;
21pub mod provider;
22pub mod runtime;
23pub mod search;
24pub mod session;
25pub mod session_graph;
26pub mod session_model;
27mod stable_hash;
28pub mod store;
29#[cfg(any(test, feature = "testing"))]
30pub mod testing;
31pub mod tool_dispatch;
32mod tool_provider;
33pub mod tool_registry;
34mod tool_result;
35mod trace;
36pub mod triggers;
37
38pub use lash_sansio::sansio;
39
40pub const VERSION: &str = env!("CARGO_PKG_VERSION");
41pub const SANSIO_VERSION: &str = lash_sansio::VERSION;
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub enum DurabilityTier {
45    Inline,
46    Durable,
47}
48
49// Re-exports
50pub use attachments::{
51    AttachmentStore, AttachmentStoreError, AttachmentStorePersistence, FileAttachmentStore,
52    InMemoryAttachmentStore, SessionScopedAttachmentStore, StoredAttachment,
53};
54pub use chronological::{
55    BorrowedChronologicalEntry, BorrowedChronologicalMessage, BorrowedChronologicalPayload,
56    ChronologicalEntry, ChronologicalPayload, ChronologicalProjection, visit_turn_view,
57};
58pub use direct::{
59    DirectJsonSchema, DirectLlmClient, DirectLlmError, DirectMessage, DirectOutputSpec, DirectPart,
60    DirectRequest, DirectRole,
61};
62pub use lash_sansio::llm::types::{
63    GenerationOptions, LlmOutputPart, LlmRequest, LlmResponse, LlmTerminalReason,
64};
65pub use lash_sansio::{
66    AcceptedInjectedTurnInput, AttachmentCreateMeta, AttachmentId, AttachmentMeta, AttachmentRef,
67    BaseRenderCache, CheckpointDelivery, CheckpointKind, CompactToolContract, EffectId,
68    ErrorEnvelope, ExecImage, ExecResponse, ImageMediaType, LashSchema, LlmCallError, MediaType,
69    Message, MessageOrigin, MessageRole, MessageSequence, ModelToolReturn, ModelToolReturnPart,
70    Part, PartKind, PluginMessage, PluginRuntimeEvent, PreparedPrompt, PromptBuildInput,
71    PromptBuiltin, PromptContext, PromptContribution, PromptContributionGate,
72    PromptContributionSet, PromptFingerprint, PromptLayer, PromptSlot, PromptSlotLayer,
73    PromptTemplate, PromptTemplateEntry, PromptTemplateSection, PruneState, RenderedPrompt,
74    ResolvedPromptLayer, Response, SchemaProjectionOverride, SessionEvent, TextProjectionMetadata,
75    TokenUsage, ToolActivation, ToolArgumentProjectionPolicy, ToolCallOutcome, ToolCallOutput,
76    ToolCallRecord, ToolCallStatus, ToolCancellation, ToolCatalog, ToolCatalogBuildInput,
77    ToolCatalogEntry, ToolContract, ToolControl, ToolDefinition, ToolFailure, ToolFailureClass,
78    ToolFailureSource, ToolId, ToolManifest, ToolOutputContract, ToolRetryDisposition,
79    ToolRetryPolicy, ToolScheduling, ToolValue, TurnCause, TurnFinish, TurnLimitFinalMessage,
80    TurnOutcome, TurnStop, append_assistant_text_part, build_prompt, build_tool_catalog,
81    build_turn, default_prompt_template, head_tail_truncate, messages_are_prompt_resume_safe,
82    normalized_response_parts, prompt_template_fingerprint, prompt_text_fingerprint,
83    prompt_tool_names_fingerprint, reasoning_part, render_turn_causes_prompt,
84    resolve_prompt_layers, shared_parts, validate_tool_input,
85};
86pub use protocol_build::ProtocolBuildInput;
87pub use tool_registry::{
88    PLUGIN_TOOL_SOURCE_ID, ReconfigureError, ToolRegistry, ToolRestoreReport, ToolSourceHandle,
89    ToolState, ToolStateEntry,
90};
91pub use tool_result::{CancelHint, PendingCompletion, TimeoutBehavior, ToolResult};
92pub use triggers::{
93    InMemoryTriggerStore, TriggerDeliveryReservation, TriggerEmitReport, TriggerEvent,
94    TriggerEventCatalog, TriggerEventKey, TriggerEventType, TriggerInputBinding,
95    TriggerOccurrenceRecord, TriggerOccurrenceRequest, TriggerRegistration, TriggerRouter,
96    TriggerStore, TriggerSubscriptionDraft, TriggerSubscriptionFilter, TriggerSubscriptionRecord,
97    TriggerTargetSummary, default_trigger_source_key, deterministic_delivery_process_id,
98    deterministic_occurrence_id, empty_trigger_source_key, trigger_event_type,
99    trigger_occurrence_request_hash, validate_trigger_occurrence_request,
100};
101#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
102pub struct ProtocolTurnOptions {
103    #[serde(default = "empty_protocol_turn_payload")]
104    pub payload: serde_json::Value,
105}
106
107fn empty_protocol_turn_payload() -> serde_json::Value {
108    serde_json::Value::Object(serde_json::Map::new())
109}
110
111impl Default for ProtocolTurnOptions {
112    fn default() -> Self {
113        Self::empty()
114    }
115}
116
117impl ProtocolTurnOptions {
118    pub fn empty() -> Self {
119        Self {
120            payload: serde_json::Value::Object(serde_json::Map::new()),
121        }
122    }
123
124    pub fn is_empty(&self) -> bool {
125        match &self.payload {
126            serde_json::Value::Object(map) => map.is_empty(),
127            _ => false,
128        }
129    }
130
131    pub fn merged_with_override(&self, override_options: &Self) -> Self {
132        match (&self.payload, &override_options.payload) {
133            (serde_json::Value::Object(base), serde_json::Value::Object(overrides)) => {
134                let mut payload = base.clone();
135                payload.extend(overrides.clone());
136                Self {
137                    payload: serde_json::Value::Object(payload),
138                }
139            }
140            _ => override_options.clone(),
141        }
142    }
143
144    pub fn typed<T>(value: T) -> Result<Self, serde_json::Error>
145    where
146        T: serde::Serialize,
147    {
148        Ok(Self {
149            payload: serde_json::to_value(value)?,
150        })
151    }
152
153    pub fn decode<T>(&self) -> Result<T, serde_json::Error>
154    where
155        T: serde::de::DeserializeOwned,
156    {
157        serde_json::from_value(self.payload.clone())
158    }
159}
160
161#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
162pub struct ProtocolDriverState {
163    pub plugin_id: String,
164    pub payload: serde_json::Value,
165}
166
167impl ProtocolDriverState {
168    pub fn new(plugin_id: impl Into<String>, payload: serde_json::Value) -> Self {
169        Self {
170            plugin_id: plugin_id.into(),
171            payload,
172        }
173    }
174}
175
176#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
177pub struct HostTurnProtocol;
178
179impl lash_sansio::TurnProtocol for HostTurnProtocol {
180    type Event = crate::session_model::ProtocolEvent;
181    type Termination = ProtocolTurnOptions;
182    type DriverState = ProtocolDriverState;
183}
184
185pub type Effect = lash_sansio::Effect<HostTurnProtocol>;
186pub type DriverAction = lash_sansio::DriverAction<HostTurnProtocol>;
187pub type DriverContextView<'a> = lash_sansio::DriverContextView<'a, HostTurnProtocol>;
188pub type TurnDriverConfig = lash_sansio::TurnDriverConfig<HostTurnProtocol>;
189pub type TurnDriverPreamble = lash_sansio::TurnDriverPreamble<HostTurnProtocol>;
190pub type ProjectorContext<'a> = lash_sansio::ProjectorContext<'a, HostTurnProtocol>;
191pub type PreparedTurnMachine = lash_sansio::PreparedTurnMachine<HostTurnProtocol>;
192pub type SansIoTurnInput = lash_sansio::SansIoTurnInput<HostTurnProtocol>;
193pub type TurnMachine = lash_sansio::TurnMachine<HostTurnProtocol>;
194pub type TurnMachineConfig = lash_sansio::TurnMachineConfig<HostTurnProtocol>;
195#[cfg(feature = "otel-trace")]
196pub use lash_trace::otel::{OtelTraceOptions, OtelTraceSink};
197pub use lash_trace::{
198    JsonlTraceSink, TraceAttachment, TraceBranchSelection, TraceContentBlock, TraceContext,
199    TraceError, TraceEvent, TraceLabelMetadata, TraceLevel, TraceLlmMessage, TraceLlmRequest,
200    TraceLlmResponse, TracePromptComponent, TraceProviderStreamEvent, TraceRecord,
201    TraceRuntimeScope, TraceRuntimeStreamEvent, TraceRuntimeSubject, TraceSink, TraceSinkError,
202    TraceTokenUsage, TraceToolSpec,
203};
204pub use llm::transport::{LlmTransportError, ProviderFailure, ProviderFailureKind};
205pub use model::{ModelLimits, ModelSpec};
206pub use plugin::{
207    AgentFrameAssignment, AgentFrameId, AgentFrameReason, AgentFrameRecord, AgentFrameStatus,
208    AppendSessionNodesRequest, AppendSessionNodesResult, AssistantResponseHookContext,
209    AssistantResponseTransform, AssistantStreamHookContext, AssistantStreamTransform,
210    CheckpointHookContext, CompactionContext, ContextCompaction, ContextCompactor, ContextError,
211    ContextRegistrations, DirectCompletion, DirectLlmCompletion, OpenAgentFrameRequest,
212    OpenAgentFrameResult, PersistentRuntimeServices, PluginCommand, PluginCommandContext,
213    PluginCommandOutcome, PluginCommandReceipt, PluginDirective, PluginError,
214    PluginExtensionContribution, PluginExtensions, PluginFactory, PluginHost, PluginLifecycleEvent,
215    PluginLifecycleEventHook, PluginOperation, PluginOperationDef, PluginOperationFailure,
216    PluginOperationInvokeError, PluginOperationKind, PluginOptions, PluginOwned, PluginQuery,
217    PluginQueryContext, PluginRegistrar, PluginRuntimeDirective, PluginSession,
218    PluginSessionContext, PluginSessionSnapshot, PluginSnapshotArtifact, PluginSnapshotEntry,
219    PluginSnapshotMeta, PluginSpec, PluginSpecFactory, PluginTask, PluginTaskContext,
220    PluginTaskOutcome, PluginTaskReceipt, PromptHookContext, ProtocolBeforeLlmCallContext,
221    ProtocolLlmCallAction, RuntimeServices, SessionAppendNode, SessionConfigChangedContext,
222    SessionContextOverlay, SessionCreateRequest, SessionGraphService, SessionHandle,
223    SessionLifecycleService, SessionParam, SessionPlugin, SessionPluginSource, SessionReadView,
224    SessionRelation, SessionSnapshot, SessionStartPoint, SessionStateChangedContext,
225    SessionStateService, SessionToolAccess, SessionTurnInput, SessionTurnRequest, SnapshotReader,
226    SnapshotWriter, SubagentSessionContext, ToolCatalogContribution, ToolResultProjectionContext,
227    ToolResultProjector, TriggerEventRegistrations, TurnContextTransform, TurnHookContext,
228    TurnResultHookContext, TurnResultSummary, TurnTransformContext, plugin_operation_def,
229};
230pub use plugin_stack::PluginStack;
231pub use provider::{
232    CacheRetention, EmptyProviderResolver, LlmTimeouts, MapProviderResolver, Provider,
233    ProviderBinding, ProviderComponents, ProviderFactory, ProviderHandle, ProviderModelPolicy,
234    ProviderOptions, ProviderResolutionError, ProviderSpec, RequestTimeout,
235    RuntimeProviderResolver, SingleProviderResolver, StaticModelPolicy,
236};
237#[cfg(any(test, feature = "testing"))]
238pub use runtime::TestLocalProcessRegistry;
239pub use runtime::{
240    AgentFrameRun, AssembledTurn, AssistantOutput, AwaitEventKey, AwaitEventWaitIdentity,
241    CausalRef, Clock, CodeOutputRecord, DefaultProcessCancelAbility, DeliveryPolicy,
242    DirectCompletionClient, DurableProcessWorker, DurableProcessWorkerConfig, DurableStoreFacet,
243    EffectHost, EmbeddedRuntimeBuilder, EmbeddedRuntimeHost, EventSink, ExecutionScope,
244    ExecutionSummary, ExternalCompletionError, InMemoryLiveReplayStore,
245    InMemoryLiveReplayStoreConfig, InMemoryProcessExecutionEnvStore, InMemorySessionStore,
246    InMemorySessionStoreFactory, InlineEffectHost, InlineProcessRunHandle,
247    InlineRuntimeEffectController, InputItem, LashRuntime, LiveReplayGap, LiveReplayGapReason,
248    LiveReplayResult, LiveReplayStore, LiveReplayStoreError, LiveReplaySubscribeResult,
249    LiveReplaySubscription, MergeKey, NoopEventSink, NoopTurnActivitySink, ObservedProcess,
250    ObservedProcessEvent, ObservedWorkItem, OutputState, PROCESS_LEASE_SCHEMA_VERSION,
251    ParkedSession, ProcessAwaitOutput, ProcessCancelAbility, ProcessCancelAllRequest,
252    ProcessCancelRequest, ProcessCancelSource, ProcessCancelSummary, ProcessEngine,
253    ProcessEngineRegistry, ProcessEngineRunContext, ProcessEngineRunGuard,
254    ProcessEngineRuntimeContext, ProcessEngineValidationContext, ProcessEvent,
255    ProcessEventAppendPlan, ProcessEventAppendRequest, ProcessEventAppendResult, ProcessEventType,
256    ProcessExecutionContext, ProcessExecutionEnvRef, ProcessExecutionEnvSpec,
257    ProcessExecutionEnvStore, ProcessExternalRef, ProcessHandleDescriptor, ProcessHandleGrant,
258    ProcessHandleSummary, ProcessId, ProcessIdentity, ProcessInput, ProcessLease,
259    ProcessLeaseCompletion, ProcessLifecycleStatus, ProcessListFilter, ProcessListMode,
260    ProcessOpScope, ProcessOriginator, ProcessProvenance, ProcessRecord, ProcessRegistration,
261    ProcessRegistry, ProcessRunHandle, ProcessRuntimeHost, ProcessService,
262    ProcessSessionDeleteReport, ProcessSpawnProvenance, ProcessStartGrant, ProcessStartOptions,
263    ProcessStartRequest, ProcessStatus, ProcessStatusFilter, ProcessTerminalSemantics,
264    ProcessTerminalSpec, ProcessTerminalState, ProcessValueSelector, ProcessWake,
265    ProcessWakeDedupeKey, ProcessWakeDelivery, ProcessWakeSpec, ProcessWorkDriver,
266    ProcessWorkObserver, ProcessWorkSnapshot, PromptUsage, ProtocolSessionExtension,
267    ProtocolSessionExtensionHandle, ProtocolTurnExtension, ProtocolTurnExtensionHandle,
268    QueuedWorkDriver, QueuedWorkRunHandle, QueuedWorkRunRequest, Residency, Resolution,
269    ResolveOutcome, RuntimeEnvironment, RuntimeEnvironmentBuilder, RuntimeError, RuntimeErrorCode,
270    RuntimeHandle, RuntimeHostConfig, RuntimeObservation, ScopedEffectController, SessionCommand,
271    SessionCommandReceipt, SessionCursor, SessionCursorError, SessionObservation,
272    SessionObservationEvent, SessionObservationEventPayload, SessionObservationSubscription,
273    SessionProcessEventKind, SessionQueueEventKind, SessionResume, SessionRevision, SessionScope,
274    SessionScopeId, SessionStoreCreateRequest, SessionStoreFactory, SessionUsageReport, SlotPolicy,
275    SystemClock, TerminationPolicy, TokenLedgerEntry, ToolCallLaunch, TurnActivity, TurnActivityId,
276    TurnActivitySink, TurnContext, TurnEvent, TurnInput, TurnIssue, TurnOptions,
277    UnavailableProcessService, UsageReportRow, UsageTotals, WaitKind, WaitState,
278    apply_process_status_projection, current_epoch_ms, diff_token_ledger, diff_usage_reports,
279    ensure_durable_effect_input, epoch_ms_from_system_time, process_signal_event_type,
280    process_signal_name_from_event_type, process_signal_wait_key, system_time_from_epoch_ms,
281    validate_process_signal_name,
282};
283#[allow(unused_imports)]
284pub(crate) use runtime::{
285    LlmAttachmentSpec, ProcessEventSemantics, QUEUED_WORK_CLAIM_TTL_MS, QueuedCheckpointWork,
286    QueuedTurnWork, QueuedWorkBatch, QueuedWorkBatchDraft, QueuedWorkClaim,
287    QueuedWorkClaimBoundary, QueuedWorkCompletion, QueuedWorkItem, QueuedWorkPayload,
288    RuntimeReplay, RuntimeScope, RuntimeSubject, load_process_execution_env,
289    materialize_process_event_semantics, persist_process_execution_env,
290    prepare_process_event_append, prepare_process_registration, process_event_invocation,
291    process_event_payload_hash, process_wake_batch_draft, process_wake_delivery,
292    process_wake_input_from_event_payload, process_wake_turn_cause, process_wake_turn_text,
293    require_event_replay,
294};
295pub use session_model::{
296    PLUGIN_RUNTIME_PROTOCOL_PLUGIN_ID, PersistedPluginRuntimeEvent,
297    plugin_runtime_event_from_protocol, plugin_runtime_protocol_event,
298};
299// Effect / process-control types consumed by external effect hosts (e.g.
300// lash-restate's workflows) and their integration tests. Kept on the public
301// surface; the rest of the runtime block above stays crate-internal.
302pub use runtime::{
303    LlmRequestSpec, ProcessCommand, ProcessEffectOutcome, ProcessEventSemanticsSpec,
304    RuntimeEffectCommand, RuntimeEffectController, RuntimeEffectControllerError,
305    RuntimeEffectEnvelope, RuntimeEffectKind, RuntimeEffectLocalExecutor, RuntimeEffectOutcome,
306    RuntimeInvocation, RuntimeSessionState, ToolAttemptEffectOutcome, ToolAttemptLaunch,
307    ToolBatchEffectOutcome,
308};
309pub use schemars::JsonSchema;
310pub use session::{
311    ExecRequest, InjectedTurnInput, RuntimeExecutionContext, Session, SessionError, ToolInvocation,
312    ToolInvocationReply,
313};
314pub use session_graph::{
315    PersistedSessionConfig, PersistedTurnState, SessionGraph, SessionMessageTreeNode,
316    SessionNodePayload, SessionNodeRecord,
317};
318pub use session_model::context::PreparedContext;
319pub use session_model::{ConversationRecord, ProtocolEvent, SessionEventRecord};
320pub use session_model::{RuntimeSessionPolicy, SessionPolicy, SessionSpec};
321pub use store::{
322    AttachmentIntent, AttachmentManifest, AttachmentManifestEntry, BlobRef, GcReport,
323    LeaseOwnerIdentity, LeaseOwnerLiveness, RuntimePersistence, SessionExecutionLease,
324    SessionExecutionLeaseClaimOutcome, SessionExecutionLeaseCompletion, SessionExecutionLeaseFence,
325    SessionMeta, SessionPickerInfo, SessionReadScope, StoreError, VacuumReport,
326};
327#[allow(unused_imports)]
328pub(crate) use store::{
329    GraphCommitDelta, PersistedSessionRead, RuntimeCommitResult, SessionCheckpoint,
330    SessionHeadMeta, ensure_supported_schema_version, load_persisted_session_state,
331    load_persisted_session_state_active_path,
332};
333pub use store::{
334    HydratedSessionCheckpoint, RuntimeCommit, RuntimeTurnCommitStamp, SessionHead,
335    refresh_persisted_session_state,
336};
337pub use tool_provider::{
338    PreparedToolBatch, PreparedToolBatchCall, PreparedToolCall, ProgressSender, SandboxMessage,
339    ToolCall, ToolChildExecutionTraceHook, ToolChildProcessStarted, ToolContext,
340    ToolDurableEffects, ToolExecutionGrant, ToolPrepareCall, ToolPrepareContext, ToolProvider,
341    ToolSessionAdmin, ToolSessionModel, ToolTriggerClient,
342};
343
344#[cfg(test)]
345mod tests {
346    use super::*;
347
348    #[test]
349    fn protocol_turn_options_missing_payload_deserializes_to_empty_object() {
350        let options: ProtocolTurnOptions =
351            serde_json::from_value(serde_json::json!({})).expect("deserialize options");
352
353        assert!(options.is_empty());
354        assert_eq!(options.payload, serde_json::json!({}));
355    }
356
357    #[test]
358    fn protocol_turn_options_explicit_null_is_not_empty() {
359        let options: ProtocolTurnOptions =
360            serde_json::from_value(serde_json::json!({ "payload": null }))
361                .expect("deserialize options");
362
363        assert!(!options.is_empty());
364        assert_eq!(options.payload, serde_json::Value::Null);
365    }
366
367    #[test]
368    fn root_exports_do_not_reintroduce_removed_session_state_shapes() {
369        let source = include_str!("lib.rs");
370        let removed_envelope = ["SessionState", "Envelope"].concat();
371        let removed_persisted = ["PersistedSession", "Snapshot"].concat();
372        let removed_history_rewriter = ["History", "Rewriter"].concat();
373        let removed_rewrite_trigger = ["Rewrite", "Trigger"].concat();
374        let removed_rewrite_context = ["Rewrite", "Context"].concat();
375        let removed_history_state = ["History", "State"].concat();
376        let removed_history_metadata = ["History", "Rewrite", "Metadata"].concat();
377
378        assert!(!source.contains(&removed_envelope));
379        assert!(!source.contains(&removed_persisted));
380        assert!(!source.contains(&removed_history_rewriter));
381        assert!(!source.contains(&removed_rewrite_trigger));
382        assert!(!source.contains(&removed_rewrite_context));
383        assert!(!source.contains(&removed_history_state));
384        assert!(!source.contains(&removed_history_metadata));
385    }
386
387    fn public_reexport_block(source: &str, module: &str) -> String {
388        let start = format!("pub use {module}::{{");
389        let mut block = String::new();
390        let mut collecting = false;
391        for line in source.lines() {
392            if line.trim_start().starts_with(&start) {
393                collecting = true;
394            }
395            if collecting {
396                block.push_str(line);
397                block.push('\n');
398                if line.trim_end() == "};" {
399                    break;
400                }
401            }
402        }
403        assert!(!block.is_empty(), "missing public {module} re-export block");
404        block
405    }
406
407    #[test]
408    fn root_runtime_exports_exclude_internal_runtime_records() {
409        let runtime_exports = public_reexport_block(include_str!("lib.rs"), "runtime");
410        for removed in [
411            "RuntimeEffectCommand",
412            "RuntimeEffectEnvelope",
413            "RuntimeEffectKind",
414            "RuntimeEffectOutcome",
415            "RuntimeInvocation",
416            "RuntimeScope",
417            "RuntimeSessionState",
418            "QueuedWorkBatch",
419            "QueuedWorkBatchDraft",
420            "QueuedWorkPayload",
421            "prepare_process_registration",
422            "process_wake_batch_draft",
423            "require_event_replay",
424        ] {
425            assert!(
426                !runtime_exports.contains(removed),
427                "runtime root export leaked {removed}"
428            );
429        }
430    }
431
432    #[test]
433    fn root_store_exports_exclude_wire_records() {
434        let store_exports = public_reexport_block(include_str!("lib.rs"), "store");
435        for removed in [
436            "SessionHead",
437            "SessionCheckpoint",
438            "RuntimeCommit",
439            "HydratedSessionCheckpoint",
440            "PersistedSessionRead",
441            "GraphCommitDelta",
442        ] {
443            assert!(
444                !store_exports.contains(removed),
445                "store root export leaked {removed}"
446            );
447        }
448    }
449
450    #[test]
451    fn removed_manager_and_host_trait_names_stay_removed() {
452        let removed_manager = ["Runtime", "Session", "Manager"].concat();
453        let removed_host = ["Runtime", "Session", "Host"].concat();
454        let sources = [
455            include_str!("runtime/session_manager/mod.rs"),
456            include_str!("plugin/runtime_host.rs"),
457            include_str!("tool_dispatch/context.rs"),
458            include_str!("tool_provider.rs"),
459        ];
460
461        for source in sources {
462            assert!(!source.contains(&removed_manager));
463            assert!(!source.contains(&removed_host));
464        }
465    }
466}