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, ToolBatchEffectOutcome,
309};
310pub use schemars::JsonSchema;
311pub use session::{
312    ExecRequest, InjectedTurnInput, RuntimeExecutionContext, Session, SessionError, ToolInvocation,
313    ToolInvocationReply,
314};
315pub use session_graph::{
316    PersistedSessionConfig, PersistedTurnState, SessionGraph, SessionMessageTreeNode,
317    SessionNodePayload, SessionNodeRecord,
318};
319pub use session_model::context::PreparedContext;
320pub use session_model::{ConversationRecord, ProtocolEvent, SessionEventRecord};
321pub use session_model::{RuntimeSessionPolicy, SessionPolicy, SessionSpec};
322pub use store::{
323    AttachmentIntent, AttachmentManifest, AttachmentManifestEntry, BlobRef, GcReport,
324    RuntimePersistence, SessionMeta, SessionPickerInfo, SessionReadScope, StoreError, VacuumReport,
325};
326#[allow(unused_imports)]
327pub(crate) use store::{
328    GraphCommitDelta, PersistedSessionRead, RuntimeCommitResult, SessionCheckpoint,
329    SessionHeadMeta, ensure_supported_schema_version, load_persisted_session_state,
330    load_persisted_session_state_active_path,
331};
332pub use store::{
333    HydratedSessionCheckpoint, RuntimeCommit, RuntimeTurnCommitStamp, SessionHead,
334    refresh_persisted_session_state,
335};
336pub use tool_provider::{
337    PreparedToolBatch, PreparedToolBatchCall, PreparedToolCall, ProgressSender, SandboxMessage,
338    ToolCall, ToolChildExecutionTraceHook, ToolChildProcessStarted, ToolContext,
339    ToolDurableEffects, ToolPrepareCall, ToolPrepareContext, ToolProvider, ToolSessionAdmin,
340    ToolSessionModel, ToolTriggerClient,
341};
342
343#[cfg(test)]
344mod tests {
345    use super::*;
346
347    #[test]
348    fn protocol_turn_options_missing_payload_deserializes_to_empty_object() {
349        let options: ProtocolTurnOptions =
350            serde_json::from_value(serde_json::json!({})).expect("deserialize options");
351
352        assert!(options.is_empty());
353        assert_eq!(options.payload, serde_json::json!({}));
354    }
355
356    #[test]
357    fn protocol_turn_options_explicit_null_is_not_empty() {
358        let options: ProtocolTurnOptions =
359            serde_json::from_value(serde_json::json!({ "payload": null }))
360                .expect("deserialize options");
361
362        assert!(!options.is_empty());
363        assert_eq!(options.payload, serde_json::Value::Null);
364    }
365
366    #[test]
367    fn root_exports_do_not_reintroduce_removed_session_state_shapes() {
368        let source = include_str!("lib.rs");
369        let removed_envelope = ["SessionState", "Envelope"].concat();
370        let removed_persisted = ["PersistedSession", "Snapshot"].concat();
371        let removed_history_rewriter = ["History", "Rewriter"].concat();
372        let removed_rewrite_trigger = ["Rewrite", "Trigger"].concat();
373        let removed_rewrite_context = ["Rewrite", "Context"].concat();
374        let removed_history_state = ["History", "State"].concat();
375        let removed_history_metadata = ["History", "Rewrite", "Metadata"].concat();
376
377        assert!(!source.contains(&removed_envelope));
378        assert!(!source.contains(&removed_persisted));
379        assert!(!source.contains(&removed_history_rewriter));
380        assert!(!source.contains(&removed_rewrite_trigger));
381        assert!(!source.contains(&removed_rewrite_context));
382        assert!(!source.contains(&removed_history_state));
383        assert!(!source.contains(&removed_history_metadata));
384    }
385
386    fn public_reexport_block(source: &str, module: &str) -> String {
387        let start = format!("pub use {module}::{{");
388        let mut block = String::new();
389        let mut collecting = false;
390        for line in source.lines() {
391            if line.trim_start().starts_with(&start) {
392                collecting = true;
393            }
394            if collecting {
395                block.push_str(line);
396                block.push('\n');
397                if line.trim_end() == "};" {
398                    break;
399                }
400            }
401        }
402        assert!(!block.is_empty(), "missing public {module} re-export block");
403        block
404    }
405
406    #[test]
407    fn root_runtime_exports_exclude_internal_runtime_records() {
408        let runtime_exports = public_reexport_block(include_str!("lib.rs"), "runtime");
409        for removed in [
410            "RuntimeEffectCommand",
411            "RuntimeEffectEnvelope",
412            "RuntimeEffectKind",
413            "RuntimeEffectOutcome",
414            "RuntimeInvocation",
415            "RuntimeScope",
416            "RuntimeSessionState",
417            "QueuedWorkBatch",
418            "QueuedWorkBatchDraft",
419            "QueuedWorkPayload",
420            "prepare_process_registration",
421            "process_wake_batch_draft",
422            "require_event_replay",
423        ] {
424            assert!(
425                !runtime_exports.contains(removed),
426                "runtime root export leaked {removed}"
427            );
428        }
429    }
430
431    #[test]
432    fn root_store_exports_exclude_wire_records() {
433        let store_exports = public_reexport_block(include_str!("lib.rs"), "store");
434        for removed in [
435            "SessionHead",
436            "SessionCheckpoint",
437            "RuntimeCommit",
438            "HydratedSessionCheckpoint",
439            "PersistedSessionRead",
440            "GraphCommitDelta",
441        ] {
442            assert!(
443                !store_exports.contains(removed),
444                "store root export leaked {removed}"
445            );
446        }
447    }
448
449    #[test]
450    fn removed_manager_and_host_trait_names_stay_removed() {
451        let removed_manager = ["Runtime", "Session", "Manager"].concat();
452        let removed_host = ["Runtime", "Session", "Host"].concat();
453        let sources = [
454            include_str!("runtime/session_manager/mod.rs"),
455            include_str!("plugin/runtime_host.rs"),
456            include_str!("tool_dispatch/context.rs"),
457            include_str!("tool_provider.rs"),
458        ];
459
460        for source in sources {
461            assert!(!source.contains(&removed_manager));
462            assert!(!source.contains(&removed_host));
463        }
464    }
465}