Skip to main content

everruns_host/
backends.rs

1// Public backend contract for the embedded host.
2// Seeding stores remain writable host configuration. Conversation history is
3// different: canonical events are the only write path and EventHistory is the
4// rebuildable read projection.
5
6use crate::events::{EventLog, EventSink, InMemoryEventLog, NoopEventSink};
7use crate::in_memory::{InMemorySessionStorageStore, InMemorySessionStore};
8use async_trait::async_trait;
9use everruns_core::agent::Agent;
10use everruns_core::error::Result;
11use everruns_core::harness::Harness;
12use everruns_core::in_memory::{InMemoryAgentStore, InMemoryHarnessStore, InMemoryProviderStore};
13use everruns_core::session::Session;
14use everruns_core::session_task::SessionTaskRegistry;
15use everruns_core::traits::{
16    AgentStore, HarnessStore, ProviderStore, ResolvedModel, SessionMutator, SessionScheduleStore,
17    SessionStorageStore, SessionStore, UserConnectionResolver,
18};
19use everruns_core::typed_id::SessionId;
20use everruns_platform::PlatformStore;
21use std::sync::Arc;
22
23/// Factory producing a per-org [`SessionScheduleStore`]. Embedders that have a
24/// single global store can ignore the `org_id` argument and return the same
25/// `Arc` every time.
26pub type ScheduleStoreFactory = Arc<dyn Fn(i64) -> Arc<dyn SessionScheduleStore> + Send + Sync>;
27
28/// Factory producing a per-(org, session) [`PlatformStore`]. The session id is
29/// supplied because platform stores are scoped to the calling session for
30/// subagent spawning and platform-management tools.
31pub type PlatformStoreFactory = Arc<dyn Fn(i64, SessionId) -> Arc<dyn PlatformStore> + Send + Sync>;
32
33/// Agent store contract for runtime seeding and lookup.
34#[async_trait]
35pub trait RuntimeAgentStore: AgentStore + Send + Sync {
36    /// Insert or replace an agent definition.
37    async fn add_agent(&self, agent: Agent) -> Result<()>;
38}
39
40/// Harness store contract for runtime seeding and lookup.
41#[async_trait]
42pub trait RuntimeHarnessStore: HarnessStore + Send + Sync {
43    /// Insert or replace a harness definition.
44    async fn add_harness(&self, harness: Harness) -> Result<()>;
45}
46
47/// Session store contract for runtime seeding, lookup, and mutation.
48#[async_trait]
49pub trait RuntimeSessionStore: SessionStore + SessionMutator + Send + Sync {
50    /// Insert or replace a session definition.
51    async fn add_session(&self, session: Session) -> Result<()>;
52}
53
54/// Provider store contract for runtime lookup and default-model configuration.
55#[async_trait]
56pub trait RuntimeProviderStore: ProviderStore + Send + Sync {
57    /// Set the runtime default model.
58    async fn set_default_model(&self, model: ResolvedModel) -> Result<()>;
59}
60
61/// Non-filesystem backend bundle supplied to an execution host.
62///
63/// Use this when you want the public runtime orchestration but your own store
64/// implementations instead of the built-in in-memory ones. Session filesystem
65/// selection is always resolved from `PlatformDefinition`.
66#[derive(Clone)]
67pub struct HostBackends {
68    /// Harness definitions available to the runtime.
69    pub harness_store: Arc<dyn RuntimeHarnessStore>,
70    /// Agent definitions available to the runtime.
71    pub agent_store: Arc<dyn RuntimeAgentStore>,
72    /// Session records and mutable session metadata.
73    pub session_store: Arc<dyn RuntimeSessionStore>,
74    /// Coherent canonical event log. This is the sole conversation write path.
75    pub event_log: Arc<dyn EventLog>,
76    /// Durable replacement context used to reconstruct compacted model input.
77    pub compaction_checkpoint_store: Arc<dyn everruns_core::CompactionCheckpointStore>,
78    /// Model/provider resolution and default-model configuration.
79    pub provider_store: Arc<dyn RuntimeProviderStore>,
80    /// Optional, non-blocking live observation sink notified after commit.
81    pub event_sink: Arc<dyn EventSink>,
82    /// Session key/value + secret storage backend.
83    pub storage_store: Arc<dyn SessionStorageStore>,
84    /// Optional resolver for user connection tokens (e.g. GitHub, Daytona).
85    ///
86    /// When set, the runtime exposes it through `ToolContext.connection_resolver`
87    /// so connection-aware capabilities can resolve tokens lazily at tool time.
88    /// `None` (the default) leaves the resolver unset, matching prior behavior.
89    /// There is no in-memory default because a connection resolver implies a
90    /// real credential source the embedder must supply.
91    pub connection_resolver: Option<Arc<dyn UserConnectionResolver>>,
92    /// Optional session-task registry injected into the act path so background
93    /// tools, subagents, and monitors persist their lifecycle. `None` (the
94    /// default) leaves `RuntimeHostAdapter::session_task_registry` returning
95    /// `None`, matching prior in-memory behavior.
96    pub session_task_registry: Option<Arc<dyn SessionTaskRegistry>>,
97    /// Optional per-org schedule store factory. `None` (the default) leaves
98    /// `RuntimeHostAdapter::schedule_store` returning `None`.
99    pub schedule_store_factory: Option<ScheduleStoreFactory>,
100    /// Optional per-(org, session) platform store factory. `None` (the
101    /// default) leaves `RuntimeHostAdapter::platform_store` returning `None`.
102    pub platform_store_factory: Option<PlatformStoreFactory>,
103}
104
105impl HostBackends {
106    /// Backend bundle with in-memory implementations for every store.
107    ///
108    /// Suitable for tests, examples, and the default runtime configuration.
109    /// Use the chainable `with_*` setters to override individual stores.
110    pub fn in_memory() -> Self {
111        Self {
112            harness_store: Arc::new(InMemoryHarnessStore::new()),
113            agent_store: Arc::new(InMemoryAgentStore::new()),
114            session_store: Arc::new(InMemorySessionStore::new()),
115            event_log: Arc::new(InMemoryEventLog::new()),
116            compaction_checkpoint_store: Arc::new(
117                everruns_core::InMemoryCompactionCheckpointStore::default(),
118            ),
119            provider_store: Arc::new(InMemoryProviderStore::new()),
120            event_sink: Arc::new(NoopEventSink),
121            storage_store: Arc::new(InMemorySessionStorageStore::new()),
122            connection_resolver: None,
123            session_task_registry: None,
124            schedule_store_factory: None,
125            platform_store_factory: None,
126        }
127    }
128
129    pub fn with_harness_store(mut self, store: Arc<dyn RuntimeHarnessStore>) -> Self {
130        self.harness_store = store;
131        self
132    }
133
134    pub fn with_agent_store(mut self, store: Arc<dyn RuntimeAgentStore>) -> Self {
135        self.agent_store = store;
136        self
137    }
138
139    pub fn with_session_store(mut self, store: Arc<dyn RuntimeSessionStore>) -> Self {
140        self.session_store = store;
141        self
142    }
143
144    /// Replace the coherent canonical event log.
145    pub fn with_event_log(mut self, log: Arc<dyn EventLog>) -> Self {
146        self.event_log = log;
147        self
148    }
149
150    pub fn with_compaction_checkpoint_store(
151        mut self,
152        store: Arc<dyn everruns_core::CompactionCheckpointStore>,
153    ) -> Self {
154        self.compaction_checkpoint_store = store;
155        self
156    }
157
158    pub fn with_provider_store(mut self, store: Arc<dyn RuntimeProviderStore>) -> Self {
159        self.provider_store = store;
160        self
161    }
162
163    /// Install a non-blocking post-commit observation sink.
164    pub fn with_event_sink(mut self, sink: Arc<dyn EventSink>) -> Self {
165        self.event_sink = sink;
166        self
167    }
168
169    pub fn with_storage_store(mut self, store: Arc<dyn SessionStorageStore>) -> Self {
170        self.storage_store = store;
171        self
172    }
173
174    /// Supply a resolver for user connection tokens (e.g. GitHub, Daytona).
175    ///
176    /// The runtime forwards it into `ToolContext` so connection-aware
177    /// capabilities resolve tokens lazily at tool execution time.
178    pub fn with_connection_resolver(mut self, resolver: Arc<dyn UserConnectionResolver>) -> Self {
179        self.connection_resolver = Some(resolver);
180        self
181    }
182
183    /// Inject a session-task registry so background tools / subagents / monitors
184    /// persist their lifecycle through the act path. Additive: leaving this unset
185    /// keeps the prior behavior (the adapter returns `None`).
186    pub fn with_session_task_registry(mut self, registry: Arc<dyn SessionTaskRegistry>) -> Self {
187        self.session_task_registry = Some(registry);
188        self
189    }
190
191    /// Inject a per-org schedule store factory. The closure is invoked with the
192    /// internal org id each time the act path needs a schedule store.
193    pub fn with_schedule_store_factory(mut self, factory: ScheduleStoreFactory) -> Self {
194        self.schedule_store_factory = Some(factory);
195        self
196    }
197
198    /// Inject a per-(org, session) platform store factory. The closure is
199    /// invoked with the internal org id and the calling session id.
200    pub fn with_platform_store_factory(mut self, factory: PlatformStoreFactory) -> Self {
201        self.platform_store_factory = Some(factory);
202        self
203    }
204}
205
206#[async_trait]
207impl RuntimeAgentStore for InMemoryAgentStore {
208    async fn add_agent(&self, agent: Agent) -> Result<()> {
209        InMemoryAgentStore::add_agent(self, agent).await;
210        Ok(())
211    }
212}
213
214#[async_trait]
215impl RuntimeHarnessStore for InMemoryHarnessStore {
216    async fn add_harness(&self, harness: Harness) -> Result<()> {
217        InMemoryHarnessStore::add_harness(self, harness).await;
218        Ok(())
219    }
220}
221
222#[async_trait]
223impl RuntimeSessionStore for InMemorySessionStore {
224    async fn add_session(&self, session: Session) -> Result<()> {
225        InMemorySessionStore::add_session(self, session).await;
226        Ok(())
227    }
228}
229
230#[async_trait]
231impl RuntimeProviderStore for InMemoryProviderStore {
232    async fn set_default_model(&self, model: ResolvedModel) -> Result<()> {
233        InMemoryProviderStore::set_default_model(self, model).await;
234        Ok(())
235    }
236}