1use async_trait::async_trait;
14use everruns_core::agent::Agent;
15use everruns_core::app::{App, AppChannel, ChannelType};
16use everruns_core::capability_dto::CapabilityInfo;
17use everruns_core::error::{AgentLoopError, Result};
18use everruns_core::harness::Harness;
19use everruns_core::platform_store::{PlatformMessage, PlatformStore};
20use everruns_core::session::Session;
21use everruns_core::typed_id::{
22 AgentId, AgentIdentityId, AppChannelId, AppId, HarnessId, SessionId,
23};
24use std::sync::Arc;
25
26#[async_trait]
31pub trait LocalSessionRunner: Send + Sync {
32 async fn create_session(
34 &self,
35 harness_id: HarnessId,
36 agent_id: Option<AgentId>,
37 title: Option<&str>,
38 locale: Option<&str>,
39 parent_session_id: Option<SessionId>,
40 ) -> Result<Session>;
41
42 async fn send_message(&self, session_id: SessionId, content: &str) -> Result<()>;
44
45 async fn list_sessions(
47 &self,
48 limit: Option<usize>,
49 agent_id: Option<AgentId>,
50 ) -> Result<Vec<Session>>;
51
52 async fn get_session(&self, session_id: SessionId) -> Result<Option<Session>>;
54
55 async fn get_messages(
57 &self,
58 session_id: SessionId,
59 limit: Option<usize>,
60 ) -> Result<Vec<PlatformMessage>>;
61
62 async fn get_session_status(&self, session_id: SessionId) -> Result<Option<String>>;
64}
65
66fn unsupported(op: &str) -> AgentLoopError {
67 AgentLoopError::tool(format!(
68 "operation '{op}' is not supported by the local platform store; \
69 manage this entity in embedder code"
70 ))
71}
72
73#[derive(Clone)]
76pub struct LocalPlatformStore {
77 runner: Arc<dyn LocalSessionRunner>,
78 base_url: String,
79}
80
81impl LocalPlatformStore {
82 pub fn new(runner: Arc<dyn LocalSessionRunner>, base_url: impl Into<String>) -> Self {
83 Self {
84 runner,
85 base_url: base_url.into(),
86 }
87 }
88}
89
90#[async_trait]
91impl PlatformStore for LocalPlatformStore {
92 async fn create_session(
95 &self,
96 harness_id: HarnessId,
97 agent_id: Option<AgentId>,
98 title: Option<&str>,
99 locale: Option<&str>,
100 blueprint_id: Option<&str>,
101 _blueprint_config: Option<&serde_json::Value>,
102 parent_session_id: Option<SessionId>,
103 ) -> Result<Session> {
104 if blueprint_id.is_some() {
105 return Err(unsupported("create_session(blueprint)"));
106 }
107 self.runner
108 .create_session(harness_id, agent_id, title, locale, parent_session_id)
109 .await
110 }
111
112 async fn get_session_by_id(&self, id: SessionId) -> Result<Option<Session>> {
113 self.runner.get_session(id).await
114 }
115
116 async fn list_sessions(
117 &self,
118 limit: Option<usize>,
119 agent_id: Option<AgentId>,
120 ) -> Result<Vec<Session>> {
121 self.runner.list_sessions(limit, agent_id).await
122 }
123
124 async fn send_message(&self, session_id: SessionId, content: &str) -> Result<()> {
125 self.runner.send_message(session_id, content).await
126 }
127
128 async fn get_messages(
129 &self,
130 session_id: SessionId,
131 limit: Option<usize>,
132 ) -> Result<Vec<PlatformMessage>> {
133 self.runner.get_messages(session_id, limit).await
134 }
135
136 async fn wait_for_idle(
137 &self,
138 session_id: SessionId,
139 _timeout_secs: Option<u64>,
140 ) -> Result<String> {
141 self.runner
144 .get_session_status(session_id)
145 .await?
146 .ok_or_else(|| AgentLoopError::session_not_found(session_id))
147 }
148
149 fn base_url(&self) -> &str {
150 &self.base_url
151 }
152
153 async fn list_harnesses(&self) -> Result<Vec<Harness>> {
156 Err(unsupported("list_harnesses"))
157 }
158 async fn get_harness(&self, _id: HarnessId) -> Result<Option<Harness>> {
159 Err(unsupported("get_harness"))
160 }
161 async fn create_harness(
162 &self,
163 _name: &str,
164 _display_name: Option<&str>,
165 _description: Option<&str>,
166 _system_prompt: Option<&str>,
167 _parent_harness_id: Option<HarnessId>,
168 _capabilities: &[String],
169 ) -> Result<Harness> {
170 Err(unsupported("create_harness"))
171 }
172 async fn update_harness(
173 &self,
174 _id: HarnessId,
175 _name: Option<&str>,
176 _display_name: Option<&str>,
177 _description: Option<&str>,
178 _system_prompt: Option<&str>,
179 _parent_harness_id: Option<Option<HarnessId>>,
180 ) -> Result<Harness> {
181 Err(unsupported("update_harness"))
182 }
183 async fn delete_harness(&self, _id: HarnessId) -> Result<()> {
184 Err(unsupported("delete_harness"))
185 }
186 async fn copy_harness(&self, _id: HarnessId, _new_name: Option<&str>) -> Result<Harness> {
187 Err(unsupported("copy_harness"))
188 }
189 async fn list_agents(&self) -> Result<Vec<Agent>> {
190 Err(unsupported("list_agents"))
191 }
192 async fn get_agent_by_id(&self, _id: AgentId) -> Result<Option<Agent>> {
193 Err(unsupported("get_agent_by_id"))
194 }
195 async fn create_agent(
196 &self,
197 _name: &str,
198 _display_name: Option<&str>,
199 _description: Option<&str>,
200 _system_prompt: &str,
201 _capabilities: &[String],
202 ) -> Result<Agent> {
203 Err(unsupported("create_agent"))
204 }
205 async fn update_agent(
206 &self,
207 _id: AgentId,
208 _name: Option<&str>,
209 _display_name: Option<&str>,
210 _description: Option<&str>,
211 _system_prompt: Option<&str>,
212 ) -> Result<Agent> {
213 Err(unsupported("update_agent"))
214 }
215 async fn delete_agent(&self, _id: AgentId) -> Result<()> {
216 Err(unsupported("delete_agent"))
217 }
218 async fn list_apps(&self, _search: Option<&str>, _include_archived: bool) -> Result<Vec<App>> {
219 Err(unsupported("list_apps"))
220 }
221 async fn get_app(&self, _id: AppId) -> Result<Option<App>> {
222 Err(unsupported("get_app"))
223 }
224 async fn create_app(
225 &self,
226 _name: &str,
227 _description: Option<&str>,
228 _harness_id: HarnessId,
229 _agent_id: Option<AgentId>,
230 _agent_identity_id: Option<AgentIdentityId>,
231 _channel_type: Option<ChannelType>,
232 _channel_config: Option<&serde_json::Value>,
233 ) -> Result<App> {
234 Err(unsupported("create_app"))
235 }
236 async fn update_app(
237 &self,
238 _id: AppId,
239 _name: Option<&str>,
240 _description: Option<&str>,
241 _harness_id: Option<HarnessId>,
242 _agent_id: Option<AgentId>,
243 _agent_identity_id: Option<Option<AgentIdentityId>>,
244 ) -> Result<App> {
245 Err(unsupported("update_app"))
246 }
247 async fn delete_app(&self, _id: AppId) -> Result<()> {
248 Err(unsupported("delete_app"))
249 }
250 async fn destroy_app(&self, _id: AppId) -> Result<()> {
251 Err(unsupported("destroy_app"))
252 }
253 async fn publish_app(&self, _id: AppId) -> Result<App> {
254 Err(unsupported("publish_app"))
255 }
256 async fn unpublish_app(&self, _id: AppId) -> Result<App> {
257 Err(unsupported("unpublish_app"))
258 }
259 async fn add_app_channel(
260 &self,
261 _app_id: AppId,
262 _channel_type: ChannelType,
263 _channel_config: Option<&serde_json::Value>,
264 _enabled: Option<bool>,
265 ) -> Result<AppChannel> {
266 Err(unsupported("add_app_channel"))
267 }
268 async fn update_app_channel(
269 &self,
270 _app_id: AppId,
271 _channel_id: AppChannelId,
272 _channel_type: Option<ChannelType>,
273 _channel_config: Option<&serde_json::Value>,
274 _enabled: Option<bool>,
275 ) -> Result<AppChannel> {
276 Err(unsupported("update_app_channel"))
277 }
278 async fn delete_app_channel(&self, _app_id: AppId, _channel_id: AppChannelId) -> Result<()> {
279 Err(unsupported("delete_app_channel"))
280 }
281 async fn get_session_context_report(
282 &self,
283 _id: SessionId,
284 ) -> Result<everruns_core::SessionContextReport> {
285 Err(unsupported("get_session_context_report"))
286 }
287 async fn delete_session(&self, _id: SessionId) -> Result<()> {
288 Err(unsupported("delete_session"))
289 }
290 async fn list_capabilities(&self, _search: Option<&str>) -> Result<Vec<CapabilityInfo>> {
291 Err(unsupported("list_capabilities"))
292 }
293}