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, ToolAvailability,
76    ToolAvailabilityConfig, ToolCallOutcome, ToolCallOutput, ToolCallRecord, ToolCallStatus,
77    ToolCancellation, ToolCatalog, ToolCatalogBuildInput, ToolCatalogEntry, ToolCatalogOverride,
78    ToolContract, ToolControl, ToolDefinition, ToolFailure, ToolFailureClass, ToolFailureSource,
79    ToolId, ToolManifest, ToolOutputContract, ToolRetryDisposition, ToolRetryPolicy,
80    ToolScheduling, ToolValue, TurnCause, TurnFinish, TurnLimitFinalMessage, TurnOutcome, TurnStop,
81    append_assistant_text_part, build_prompt, build_tool_catalog, build_turn,
82    default_prompt_template, head_tail_truncate, messages_are_prompt_resume_safe,
83    normalized_response_parts, prompt_template_fingerprint, prompt_text_fingerprint,
84    prompt_tool_names_fingerprint, reasoning_part, render_turn_causes_prompt,
85    resolve_prompt_layers, shared_parts, validate_tool_input,
86};
87pub use protocol_build::ProtocolBuildInput;
88pub use tool_registry::{
89    ReconfigureError, ToolRegistry, ToolRestoreReport, ToolSourceHandle, 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, ToolDiscoveryContext,
227    ToolDiscoveryContribution, ToolDiscoveryContributor, ToolDiscoveryToolContribution,
228    ToolResultProjectionContext, ToolResultProjector, TriggerEventRegistrations,
229    TurnContextTransform, TurnHookContext, TurnResultHookContext, TurnResultSummary,
230    TurnTransformContext, plugin_operation_def,
231};
232pub use plugin_stack::PluginStack;
233pub use provider::{
234    CacheRetention, EmptyProviderResolver, LlmTimeouts, MapProviderResolver, Provider,
235    ProviderBinding, ProviderComponents, ProviderFactory, ProviderHandle, ProviderModelPolicy,
236    ProviderOptions, ProviderResolutionError, ProviderSpec, RequestTimeout,
237    RuntimeProviderResolver, SingleProviderResolver, StaticModelPolicy,
238};
239#[cfg(any(test, feature = "testing"))]
240pub use runtime::TestLocalProcessRegistry;
241pub use runtime::{
242    AgentFrameRun, AssembledTurn, AssistantOutput, AwaitEventKey, AwaitEventWaitIdentity,
243    CausalRef, Clock, CodeOutputRecord, DefaultProcessCancelAbility, DeliveryPolicy,
244    DirectCompletionClient, DurableProcessWorker, DurableProcessWorkerConfig, DurableStoreFacet,
245    EffectHost, EmbeddedRuntimeBuilder, EmbeddedRuntimeHost, EventSink, ExecutionScope,
246    ExecutionSummary, ExternalCompletionError, InMemoryLiveReplayStore,
247    InMemoryLiveReplayStoreConfig, InMemoryProcessExecutionEnvStore, InMemorySessionStore,
248    InMemorySessionStoreFactory, InlineEffectHost, InlineProcessRunHandle,
249    InlineRuntimeEffectController, InputItem, LashRuntime, LiveReplayGap, LiveReplayGapReason,
250    LiveReplayResult, LiveReplayStore, LiveReplayStoreError, LiveReplaySubscribeResult,
251    LiveReplaySubscription, MergeKey, NoopEventSink, NoopTurnActivitySink, ObservedProcess,
252    ObservedProcessEvent, ObservedWorkItem, OutputState, PROCESS_LEASE_SCHEMA_VERSION,
253    ParkedSession, ProcessAwaitOutput, ProcessCancelAbility, ProcessCancelAllRequest,
254    ProcessCancelRequest, ProcessCancelSource, ProcessCancelSummary, ProcessEngine,
255    ProcessEngineRegistry, ProcessEngineRunContext, ProcessEngineRunGuard,
256    ProcessEngineRuntimeContext, ProcessEngineValidationContext, ProcessEvent,
257    ProcessEventAppendPlan, ProcessEventAppendRequest, ProcessEventAppendResult, ProcessEventType,
258    ProcessExecutionContext, ProcessExecutionEnvRef, ProcessExecutionEnvSpec,
259    ProcessExecutionEnvStore, ProcessExternalRef, ProcessHandleDescriptor, ProcessHandleGrant,
260    ProcessHandleSummary, ProcessId, ProcessIdentity, ProcessInput, ProcessLease,
261    ProcessLeaseCompletion, ProcessLifecycleStatus, ProcessListFilter, ProcessListMode,
262    ProcessOpScope, ProcessOriginator, ProcessProvenance, ProcessRecord, ProcessRegistration,
263    ProcessRegistry, ProcessRunHandle, ProcessRuntimeHost, ProcessService,
264    ProcessSessionDeleteReport, ProcessSpawnProvenance, ProcessStartGrant, ProcessStartOptions,
265    ProcessStartRequest, ProcessStatus, ProcessStatusFilter, ProcessTerminalSemantics,
266    ProcessTerminalSpec, ProcessTerminalState, ProcessValueSelector, ProcessWake,
267    ProcessWakeDedupeKey, ProcessWakeDelivery, ProcessWakeSpec, ProcessWorkDriver,
268    ProcessWorkObserver, ProcessWorkSnapshot, PromptUsage, ProtocolSessionExtension,
269    ProtocolSessionExtensionHandle, ProtocolTurnExtension, ProtocolTurnExtensionHandle,
270    QueuedWorkDriver, QueuedWorkRunHandle, QueuedWorkRunRequest, Residency, Resolution,
271    ResolveOutcome, RuntimeEnvironment, RuntimeEnvironmentBuilder, RuntimeError, RuntimeErrorCode,
272    RuntimeHandle, RuntimeHostConfig, RuntimeObservation, ScopedEffectController, SessionCommand,
273    SessionCommandReceipt, SessionCursor, SessionCursorError, SessionObservation,
274    SessionObservationEvent, SessionObservationEventPayload, SessionObservationSubscription,
275    SessionProcessEventKind, SessionQueueEventKind, SessionResume, SessionRevision, SessionScope,
276    SessionScopeId, SessionStoreCreateRequest, SessionStoreFactory, SessionUsageReport, SlotPolicy,
277    SystemClock, TerminationPolicy, TokenLedgerEntry, ToolCallLaunch, TurnActivity, TurnActivityId,
278    TurnActivitySink, TurnContext, TurnEvent, TurnInput, TurnIssue, TurnOptions,
279    UnavailableProcessService, UsageReportRow, UsageTotals, WaitKind, WaitState,
280    apply_process_status_projection, current_epoch_ms, diff_token_ledger, diff_usage_reports,
281    ensure_durable_effect_input, epoch_ms_from_system_time, process_signal_event_type,
282    process_signal_name_from_event_type, process_signal_wait_key, system_time_from_epoch_ms,
283    validate_process_signal_name,
284};
285#[allow(unused_imports)]
286pub(crate) use runtime::{
287    LlmAttachmentSpec, ProcessEventSemantics, QUEUED_WORK_CLAIM_TTL_MS, QueuedCheckpointWork,
288    QueuedTurnWork, QueuedWorkBatch, QueuedWorkBatchDraft, QueuedWorkClaim,
289    QueuedWorkClaimBoundary, QueuedWorkCompletion, QueuedWorkItem, QueuedWorkPayload,
290    RuntimeReplay, RuntimeScope, RuntimeSubject, load_process_execution_env,
291    materialize_process_event_semantics, persist_process_execution_env,
292    prepare_process_event_append, prepare_process_registration, process_event_invocation,
293    process_event_payload_hash, process_wake_batch_draft, process_wake_delivery,
294    process_wake_input_from_event_payload, process_wake_turn_cause, process_wake_turn_text,
295    require_event_replay,
296};
297pub use session_model::{
298    PLUGIN_RUNTIME_PROTOCOL_PLUGIN_ID, PersistedPluginRuntimeEvent,
299    plugin_runtime_event_from_protocol, plugin_runtime_protocol_event,
300};
301// Effect / process-control types consumed by external effect hosts (e.g.
302// lash-restate's workflows) and their integration tests. Kept on the public
303// surface; the rest of the runtime block above stays crate-internal.
304pub use runtime::{
305    LlmRequestSpec, ProcessCommand, ProcessEffectOutcome, ProcessEventSemanticsSpec,
306    RuntimeEffectCommand, RuntimeEffectController, RuntimeEffectControllerError,
307    RuntimeEffectEnvelope, RuntimeEffectKind, RuntimeEffectLocalExecutor, RuntimeEffectOutcome,
308    RuntimeInvocation, RuntimeSessionState, ToolAttemptEffectOutcome, ToolAttemptLaunch,
309    ToolBatchEffectOutcome,
310};
311pub use schemars::JsonSchema;
312pub use session::{
313    ExecRequest, InjectedTurnInput, RuntimeExecutionContext, Session, SessionError, ToolInvocation,
314    ToolInvocationReply,
315};
316pub use session_graph::{
317    PersistedSessionConfig, PersistedTurnState, SessionGraph, SessionMessageTreeNode,
318    SessionNodePayload, SessionNodeRecord,
319};
320pub use session_model::context::PreparedContext;
321pub use session_model::{ConversationRecord, ProtocolEvent, SessionEventRecord};
322pub use session_model::{RuntimeSessionPolicy, SessionPolicy, SessionSpec};
323pub use store::{
324    AttachmentIntent, AttachmentManifest, AttachmentManifestEntry, BlobRef, GcReport,
325    RuntimePersistence, SessionExecutionLease, SessionExecutionLeaseCompletion,
326    SessionExecutionLeaseFence, SessionMeta, SessionPickerInfo, SessionReadScope, StoreError,
327    VacuumReport,
328};
329#[allow(unused_imports)]
330pub(crate) use store::{
331    GraphCommitDelta, PersistedSessionRead, RuntimeCommitResult, SessionCheckpoint,
332    SessionHeadMeta, ensure_supported_schema_version, load_persisted_session_state,
333    load_persisted_session_state_active_path,
334};
335pub use store::{
336    HydratedSessionCheckpoint, RuntimeCommit, RuntimeTurnCommitStamp, SessionHead,
337    refresh_persisted_session_state,
338};
339pub use tool_provider::{
340    PreparedToolBatch, PreparedToolBatchCall, PreparedToolCall, ProgressSender, SandboxMessage,
341    ToolCall, ToolChildExecutionTraceHook, ToolChildProcessStarted, ToolContext,
342    ToolDurableEffects, ToolPrepareCall, ToolPrepareContext, ToolProvider, ToolSessionAdmin,
343    ToolSessionModel, ToolTriggerClient,
344};
345
346#[cfg(test)]
347mod tests {
348    use super::*;
349
350    #[test]
351    fn protocol_turn_options_missing_payload_deserializes_to_empty_object() {
352        let options: ProtocolTurnOptions =
353            serde_json::from_value(serde_json::json!({})).expect("deserialize options");
354
355        assert!(options.is_empty());
356        assert_eq!(options.payload, serde_json::json!({}));
357    }
358
359    #[test]
360    fn protocol_turn_options_explicit_null_is_not_empty() {
361        let options: ProtocolTurnOptions =
362            serde_json::from_value(serde_json::json!({ "payload": null }))
363                .expect("deserialize options");
364
365        assert!(!options.is_empty());
366        assert_eq!(options.payload, serde_json::Value::Null);
367    }
368
369    #[test]
370    fn root_exports_do_not_reintroduce_removed_session_state_shapes() {
371        let source = include_str!("lib.rs");
372        let removed_envelope = ["SessionState", "Envelope"].concat();
373        let removed_persisted = ["PersistedSession", "Snapshot"].concat();
374        let removed_history_rewriter = ["History", "Rewriter"].concat();
375        let removed_rewrite_trigger = ["Rewrite", "Trigger"].concat();
376        let removed_rewrite_context = ["Rewrite", "Context"].concat();
377        let removed_history_state = ["History", "State"].concat();
378        let removed_history_metadata = ["History", "Rewrite", "Metadata"].concat();
379
380        assert!(!source.contains(&removed_envelope));
381        assert!(!source.contains(&removed_persisted));
382        assert!(!source.contains(&removed_history_rewriter));
383        assert!(!source.contains(&removed_rewrite_trigger));
384        assert!(!source.contains(&removed_rewrite_context));
385        assert!(!source.contains(&removed_history_state));
386        assert!(!source.contains(&removed_history_metadata));
387    }
388
389    fn public_reexport_block(source: &str, module: &str) -> String {
390        let start = format!("pub use {module}::{{");
391        let mut block = String::new();
392        let mut collecting = false;
393        for line in source.lines() {
394            if line.trim_start().starts_with(&start) {
395                collecting = true;
396            }
397            if collecting {
398                block.push_str(line);
399                block.push('\n');
400                if line.trim_end() == "};" {
401                    break;
402                }
403            }
404        }
405        assert!(!block.is_empty(), "missing public {module} re-export block");
406        block
407    }
408
409    #[test]
410    fn root_runtime_exports_exclude_internal_runtime_records() {
411        let runtime_exports = public_reexport_block(include_str!("lib.rs"), "runtime");
412        for removed in [
413            "RuntimeEffectCommand",
414            "RuntimeEffectEnvelope",
415            "RuntimeEffectKind",
416            "RuntimeEffectOutcome",
417            "RuntimeInvocation",
418            "RuntimeScope",
419            "RuntimeSessionState",
420            "QueuedWorkBatch",
421            "QueuedWorkBatchDraft",
422            "QueuedWorkPayload",
423            "prepare_process_registration",
424            "process_wake_batch_draft",
425            "require_event_replay",
426        ] {
427            assert!(
428                !runtime_exports.contains(removed),
429                "runtime root export leaked {removed}"
430            );
431        }
432    }
433
434    #[test]
435    fn root_store_exports_exclude_wire_records() {
436        let store_exports = public_reexport_block(include_str!("lib.rs"), "store");
437        for removed in [
438            "SessionHead",
439            "SessionCheckpoint",
440            "RuntimeCommit",
441            "HydratedSessionCheckpoint",
442            "PersistedSessionRead",
443            "GraphCommitDelta",
444        ] {
445            assert!(
446                !store_exports.contains(removed),
447                "store root export leaked {removed}"
448            );
449        }
450    }
451
452    #[test]
453    fn removed_manager_and_host_trait_names_stay_removed() {
454        let removed_manager = ["Runtime", "Session", "Manager"].concat();
455        let removed_host = ["Runtime", "Session", "Host"].concat();
456        let sources = [
457            include_str!("runtime/session_manager/mod.rs"),
458            include_str!("plugin/runtime_host.rs"),
459            include_str!("tool_dispatch/context.rs"),
460            include_str!("tool_provider.rs"),
461        ];
462
463        for source in sources {
464            assert!(!source.contains(&removed_manager));
465            assert!(!source.contains(&removed_host));
466        }
467    }
468}