Skip to main content

everruns_runtime/
backends.rs

1// Public backend contract for the embedded runtime.
2// Decision: runtime extends core's read-only traits with the minimal write
3// operations needed for seeding and message persistence. Trait upcasting +
4// blanket `Arc<T>: T` impls in core let the runtime forward to atoms without
5// shim wrappers.
6
7use crate::in_memory::{InMemorySessionStorageStore, InMemorySessionStore};
8use async_trait::async_trait;
9use everruns_core::agent::Agent;
10use everruns_core::error::Result;
11use everruns_core::events::Event;
12use everruns_core::harness::Harness;
13use everruns_core::in_memory::{
14    InMemoryAgentStore, InMemoryEventEmitter, InMemoryHarnessStore, InMemoryMessageRetriever,
15    InMemoryProviderStore,
16};
17use everruns_core::message::Message;
18use everruns_core::message_retriever::{InputMessage, MessageRetriever};
19use everruns_core::platform_store::PlatformStore;
20use everruns_core::session::Session;
21use everruns_core::session_task::SessionTaskRegistry;
22use everruns_core::traits::{
23    AgentStore, EventEmitter, HarnessStore, ProviderStore, ResolvedModel, SessionMutator,
24    SessionScheduleStore, SessionStorageStore, SessionStore, UserConnectionResolver,
25};
26use everruns_core::typed_id::SessionId;
27use std::sync::Arc;
28
29/// Factory producing a per-org [`SessionScheduleStore`]. Embedders that have a
30/// single global store can ignore the `org_id` argument and return the same
31/// `Arc` every time.
32pub type ScheduleStoreFactory = Arc<dyn Fn(i64) -> Arc<dyn SessionScheduleStore> + Send + Sync>;
33
34/// Factory producing a per-(org, session) [`PlatformStore`]. The session id is
35/// supplied because platform stores are scoped to the calling session for
36/// subagent spawning and platform-management tools.
37pub type PlatformStoreFactory = Arc<dyn Fn(i64, SessionId) -> Arc<dyn PlatformStore> + Send + Sync>;
38
39/// Agent store contract for runtime seeding and lookup.
40#[async_trait]
41pub trait RuntimeAgentStore: AgentStore + Send + Sync {
42    /// Insert or replace an agent definition.
43    async fn add_agent(&self, agent: Agent) -> Result<()>;
44}
45
46/// Harness store contract for runtime seeding and lookup.
47#[async_trait]
48pub trait RuntimeHarnessStore: HarnessStore + Send + Sync {
49    /// Insert or replace a harness definition.
50    async fn add_harness(&self, harness: Harness) -> Result<()>;
51}
52
53/// Session store contract for runtime seeding, lookup, and mutation.
54#[async_trait]
55pub trait RuntimeSessionStore: SessionStore + SessionMutator + Send + Sync {
56    /// Insert or replace a session definition.
57    async fn add_session(&self, session: Session) -> Result<()>;
58}
59
60/// Message store contract for runtime persistence and lookup.
61#[async_trait]
62pub trait RuntimeMessageStore: MessageRetriever + Send + Sync {
63    /// Store a new input message and return the generated message record.
64    async fn add_input_message(
65        &self,
66        session_id: SessionId,
67        input: InputMessage,
68    ) -> Result<Message>;
69
70    /// Persist an existing message record.
71    async fn store_message(&self, session_id: SessionId, message: Message) -> Result<()>;
72}
73
74/// Provider store contract for runtime lookup and default-model configuration.
75#[async_trait]
76pub trait RuntimeProviderStore: ProviderStore + Send + Sync {
77    /// Set the runtime default model.
78    async fn set_default_model(&self, model: ResolvedModel) -> Result<()>;
79}
80
81/// Event sink that supports emission and optional collection.
82///
83/// Every `EventBus` is an `EventEmitter`. Embedders that want to inspect
84/// emitted events override `collected_events`; production hosts inherit the
85/// no-op default.
86#[async_trait]
87pub trait EventBus: EventEmitter {
88    /// Return all collected events. Defaults to an empty `Vec` for buses
89    /// that do not retain events.
90    async fn collected_events(&self) -> Vec<Event> {
91        Vec::new()
92    }
93}
94
95#[async_trait]
96impl<T: EventBus + ?Sized> EventBus for Arc<T> {
97    async fn collected_events(&self) -> Vec<Event> {
98        (**self).collected_events().await
99    }
100}
101
102/// Non-filesystem backend bundle supplied to the embedded runtime.
103///
104/// Use this when you want the public runtime orchestration but your own store
105/// implementations instead of the built-in in-memory ones. Session filesystem
106/// selection is always resolved from `PlatformDefinition`.
107#[derive(Clone)]
108pub struct RuntimeBackends {
109    /// Harness definitions available to the runtime.
110    pub harness_store: Arc<dyn RuntimeHarnessStore>,
111    /// Agent definitions available to the runtime.
112    pub agent_store: Arc<dyn RuntimeAgentStore>,
113    /// Session records and mutable session metadata.
114    pub session_store: Arc<dyn RuntimeSessionStore>,
115    /// Conversation message persistence and retrieval.
116    pub message_store: Arc<dyn RuntimeMessageStore>,
117    /// Durable replacement context used to reconstruct compacted model input.
118    pub compaction_checkpoint_store: Arc<dyn everruns_core::CompactionCheckpointStore>,
119    /// Model/provider resolution and default-model configuration.
120    pub provider_store: Arc<dyn RuntimeProviderStore>,
121    /// Event sink (emit + optional collection).
122    pub event_bus: Arc<dyn EventBus>,
123    /// Session key/value + secret storage backend.
124    pub storage_store: Arc<dyn SessionStorageStore>,
125    /// Optional resolver for user connection tokens (e.g. GitHub, Daytona).
126    ///
127    /// When set, the runtime exposes it through `ToolContext.connection_resolver`
128    /// so connection-aware capabilities can resolve tokens lazily at tool time.
129    /// `None` (the default) leaves the resolver unset, matching prior behavior.
130    /// There is no in-memory default because a connection resolver implies a
131    /// real credential source the embedder must supply.
132    pub connection_resolver: Option<Arc<dyn UserConnectionResolver>>,
133    /// Optional session-task registry injected into the act path so background
134    /// tools, subagents, and monitors persist their lifecycle. `None` (the
135    /// default) leaves `RuntimeHostAdapter::session_task_registry` returning
136    /// `None`, matching prior in-memory behavior.
137    pub session_task_registry: Option<Arc<dyn SessionTaskRegistry>>,
138    /// Optional per-org schedule store factory. `None` (the default) leaves
139    /// `RuntimeHostAdapter::schedule_store` returning `None`.
140    pub schedule_store_factory: Option<ScheduleStoreFactory>,
141    /// Optional per-(org, session) platform store factory. `None` (the
142    /// default) leaves `RuntimeHostAdapter::platform_store` returning `None`.
143    pub platform_store_factory: Option<PlatformStoreFactory>,
144}
145
146impl RuntimeBackends {
147    /// Backend bundle with in-memory implementations for every store.
148    ///
149    /// Suitable for tests, examples, and the default runtime configuration.
150    /// Use the chainable `with_*` setters to override individual stores.
151    pub fn in_memory() -> Self {
152        let event_bus = Arc::new(InMemoryEventEmitter::new());
153        Self {
154            harness_store: Arc::new(InMemoryHarnessStore::new()),
155            agent_store: Arc::new(InMemoryAgentStore::new()),
156            session_store: Arc::new(InMemorySessionStore::new()),
157            message_store: Arc::new(InMemoryMessageRetriever::new()),
158            compaction_checkpoint_store: Arc::new(
159                everruns_core::InMemoryCompactionCheckpointStore::default(),
160            ),
161            provider_store: Arc::new(InMemoryProviderStore::new()),
162            event_bus,
163            storage_store: Arc::new(InMemorySessionStorageStore::new()),
164            connection_resolver: None,
165            session_task_registry: None,
166            schedule_store_factory: None,
167            platform_store_factory: None,
168        }
169    }
170
171    pub fn with_harness_store(mut self, store: Arc<dyn RuntimeHarnessStore>) -> Self {
172        self.harness_store = store;
173        self
174    }
175
176    pub fn with_agent_store(mut self, store: Arc<dyn RuntimeAgentStore>) -> Self {
177        self.agent_store = store;
178        self
179    }
180
181    pub fn with_session_store(mut self, store: Arc<dyn RuntimeSessionStore>) -> Self {
182        self.session_store = store;
183        self
184    }
185
186    pub fn with_message_store(mut self, store: Arc<dyn RuntimeMessageStore>) -> Self {
187        self.message_store = store;
188        self
189    }
190
191    pub fn with_compaction_checkpoint_store(
192        mut self,
193        store: Arc<dyn everruns_core::CompactionCheckpointStore>,
194    ) -> Self {
195        self.compaction_checkpoint_store = store;
196        self
197    }
198
199    pub fn with_provider_store(mut self, store: Arc<dyn RuntimeProviderStore>) -> Self {
200        self.provider_store = store;
201        self
202    }
203
204    pub fn with_event_bus(mut self, bus: Arc<dyn EventBus>) -> Self {
205        self.event_bus = bus;
206        self
207    }
208
209    pub fn with_storage_store(mut self, store: Arc<dyn SessionStorageStore>) -> Self {
210        self.storage_store = store;
211        self
212    }
213
214    /// Supply a resolver for user connection tokens (e.g. GitHub, Daytona).
215    ///
216    /// The runtime forwards it into `ToolContext` so connection-aware
217    /// capabilities resolve tokens lazily at tool execution time.
218    pub fn with_connection_resolver(mut self, resolver: Arc<dyn UserConnectionResolver>) -> Self {
219        self.connection_resolver = Some(resolver);
220        self
221    }
222
223    /// Inject a session-task registry so background tools / subagents / monitors
224    /// persist their lifecycle through the act path. Additive: leaving this unset
225    /// keeps the prior behavior (the adapter returns `None`).
226    pub fn with_session_task_registry(mut self, registry: Arc<dyn SessionTaskRegistry>) -> Self {
227        self.session_task_registry = Some(registry);
228        self
229    }
230
231    /// Inject a per-org schedule store factory. The closure is invoked with the
232    /// internal org id each time the act path needs a schedule store.
233    pub fn with_schedule_store_factory(mut self, factory: ScheduleStoreFactory) -> Self {
234        self.schedule_store_factory = Some(factory);
235        self
236    }
237
238    /// Inject a per-(org, session) platform store factory. The closure is
239    /// invoked with the internal org id and the calling session id.
240    pub fn with_platform_store_factory(mut self, factory: PlatformStoreFactory) -> Self {
241        self.platform_store_factory = Some(factory);
242        self
243    }
244}
245
246#[async_trait]
247impl RuntimeAgentStore for InMemoryAgentStore {
248    async fn add_agent(&self, agent: Agent) -> Result<()> {
249        InMemoryAgentStore::add_agent(self, agent).await;
250        Ok(())
251    }
252}
253
254#[async_trait]
255impl RuntimeHarnessStore for InMemoryHarnessStore {
256    async fn add_harness(&self, harness: Harness) -> Result<()> {
257        InMemoryHarnessStore::add_harness(self, harness).await;
258        Ok(())
259    }
260}
261
262#[async_trait]
263impl RuntimeSessionStore for InMemorySessionStore {
264    async fn add_session(&self, session: Session) -> Result<()> {
265        InMemorySessionStore::add_session(self, session).await;
266        Ok(())
267    }
268}
269
270#[async_trait]
271impl RuntimeMessageStore for InMemoryMessageRetriever {
272    async fn add_input_message(
273        &self,
274        session_id: SessionId,
275        input: InputMessage,
276    ) -> Result<Message> {
277        self.add(session_id, input).await
278    }
279
280    async fn store_message(&self, session_id: SessionId, message: Message) -> Result<()> {
281        self.store(session_id, message).await
282    }
283}
284
285#[async_trait]
286impl RuntimeProviderStore for InMemoryProviderStore {
287    async fn set_default_model(&self, model: ResolvedModel) -> Result<()> {
288        InMemoryProviderStore::set_default_model(self, model).await;
289        Ok(())
290    }
291}
292
293#[async_trait]
294impl EventBus for InMemoryEventEmitter {
295    async fn collected_events(&self) -> Vec<Event> {
296        InMemoryEventEmitter::events(self).await
297    }
298}