use async_trait::async_trait;
use everruns_core::agent::Agent;
use everruns_core::app::{App, AppChannel, ChannelType};
use everruns_core::capability_dto::CapabilityInfo;
use everruns_core::error::{AgentLoopError, Result};
use everruns_core::harness::Harness;
use everruns_core::platform_store::{PlatformMessage, PlatformStore};
use everruns_core::session::Session;
use everruns_core::typed_id::{
AgentId, AgentIdentityId, AppChannelId, AppId, HarnessId, SessionId,
};
use std::sync::Arc;
#[async_trait]
pub trait LocalSessionRunner: Send + Sync {
async fn create_session(
&self,
harness_id: HarnessId,
agent_id: Option<AgentId>,
title: Option<&str>,
locale: Option<&str>,
parent_session_id: Option<SessionId>,
) -> Result<Session>;
async fn send_message(&self, session_id: SessionId, content: &str) -> Result<()>;
async fn list_sessions(
&self,
limit: Option<usize>,
agent_id: Option<AgentId>,
) -> Result<Vec<Session>>;
async fn get_session(&self, session_id: SessionId) -> Result<Option<Session>>;
async fn get_messages(
&self,
session_id: SessionId,
limit: Option<usize>,
) -> Result<Vec<PlatformMessage>>;
async fn get_session_status(&self, session_id: SessionId) -> Result<Option<String>>;
}
fn unsupported(op: &str) -> AgentLoopError {
AgentLoopError::tool(format!(
"operation '{op}' is not supported by the local platform store; \
manage this entity in embedder code"
))
}
#[derive(Clone)]
pub struct LocalPlatformStore {
runner: Arc<dyn LocalSessionRunner>,
base_url: String,
}
impl LocalPlatformStore {
pub fn new(runner: Arc<dyn LocalSessionRunner>, base_url: impl Into<String>) -> Self {
Self {
runner,
base_url: base_url.into(),
}
}
}
#[async_trait]
impl PlatformStore for LocalPlatformStore {
async fn create_session(
&self,
harness_id: HarnessId,
agent_id: Option<AgentId>,
title: Option<&str>,
locale: Option<&str>,
blueprint_id: Option<&str>,
_blueprint_config: Option<&serde_json::Value>,
parent_session_id: Option<SessionId>,
) -> Result<Session> {
if blueprint_id.is_some() {
return Err(unsupported("create_session(blueprint)"));
}
self.runner
.create_session(harness_id, agent_id, title, locale, parent_session_id)
.await
}
async fn get_session_by_id(&self, id: SessionId) -> Result<Option<Session>> {
self.runner.get_session(id).await
}
async fn list_sessions(
&self,
limit: Option<usize>,
agent_id: Option<AgentId>,
) -> Result<Vec<Session>> {
self.runner.list_sessions(limit, agent_id).await
}
async fn send_message(&self, session_id: SessionId, content: &str) -> Result<()> {
self.runner.send_message(session_id, content).await
}
async fn get_messages(
&self,
session_id: SessionId,
limit: Option<usize>,
) -> Result<Vec<PlatformMessage>> {
self.runner.get_messages(session_id, limit).await
}
async fn wait_for_idle(
&self,
session_id: SessionId,
_timeout_secs: Option<u64>,
) -> Result<String> {
self.runner
.get_session_status(session_id)
.await?
.ok_or_else(|| AgentLoopError::session_not_found(session_id))
}
fn base_url(&self) -> &str {
&self.base_url
}
async fn list_harnesses(&self) -> Result<Vec<Harness>> {
Err(unsupported("list_harnesses"))
}
async fn get_harness(&self, _id: HarnessId) -> Result<Option<Harness>> {
Err(unsupported("get_harness"))
}
async fn create_harness(
&self,
_name: &str,
_display_name: Option<&str>,
_description: Option<&str>,
_system_prompt: Option<&str>,
_parent_harness_id: Option<HarnessId>,
_capabilities: &[String],
) -> Result<Harness> {
Err(unsupported("create_harness"))
}
async fn update_harness(
&self,
_id: HarnessId,
_name: Option<&str>,
_display_name: Option<&str>,
_description: Option<&str>,
_system_prompt: Option<&str>,
_parent_harness_id: Option<Option<HarnessId>>,
) -> Result<Harness> {
Err(unsupported("update_harness"))
}
async fn delete_harness(&self, _id: HarnessId) -> Result<()> {
Err(unsupported("delete_harness"))
}
async fn copy_harness(&self, _id: HarnessId, _new_name: Option<&str>) -> Result<Harness> {
Err(unsupported("copy_harness"))
}
async fn list_agents(&self) -> Result<Vec<Agent>> {
Err(unsupported("list_agents"))
}
async fn get_agent_by_id(&self, _id: AgentId) -> Result<Option<Agent>> {
Err(unsupported("get_agent_by_id"))
}
async fn create_agent(
&self,
_name: &str,
_display_name: Option<&str>,
_description: Option<&str>,
_system_prompt: &str,
_capabilities: &[String],
) -> Result<Agent> {
Err(unsupported("create_agent"))
}
async fn update_agent(
&self,
_id: AgentId,
_name: Option<&str>,
_display_name: Option<&str>,
_description: Option<&str>,
_system_prompt: Option<&str>,
) -> Result<Agent> {
Err(unsupported("update_agent"))
}
async fn delete_agent(&self, _id: AgentId) -> Result<()> {
Err(unsupported("delete_agent"))
}
async fn list_apps(&self, _search: Option<&str>, _include_archived: bool) -> Result<Vec<App>> {
Err(unsupported("list_apps"))
}
async fn get_app(&self, _id: AppId) -> Result<Option<App>> {
Err(unsupported("get_app"))
}
async fn create_app(
&self,
_name: &str,
_description: Option<&str>,
_harness_id: HarnessId,
_agent_id: Option<AgentId>,
_agent_identity_id: Option<AgentIdentityId>,
_channel_type: Option<ChannelType>,
_channel_config: Option<&serde_json::Value>,
) -> Result<App> {
Err(unsupported("create_app"))
}
async fn update_app(
&self,
_id: AppId,
_name: Option<&str>,
_description: Option<&str>,
_harness_id: Option<HarnessId>,
_agent_id: Option<AgentId>,
_agent_identity_id: Option<Option<AgentIdentityId>>,
) -> Result<App> {
Err(unsupported("update_app"))
}
async fn delete_app(&self, _id: AppId) -> Result<()> {
Err(unsupported("delete_app"))
}
async fn destroy_app(&self, _id: AppId) -> Result<()> {
Err(unsupported("destroy_app"))
}
async fn publish_app(&self, _id: AppId) -> Result<App> {
Err(unsupported("publish_app"))
}
async fn unpublish_app(&self, _id: AppId) -> Result<App> {
Err(unsupported("unpublish_app"))
}
async fn add_app_channel(
&self,
_app_id: AppId,
_channel_type: ChannelType,
_channel_config: Option<&serde_json::Value>,
_enabled: Option<bool>,
) -> Result<AppChannel> {
Err(unsupported("add_app_channel"))
}
async fn update_app_channel(
&self,
_app_id: AppId,
_channel_id: AppChannelId,
_channel_type: Option<ChannelType>,
_channel_config: Option<&serde_json::Value>,
_enabled: Option<bool>,
) -> Result<AppChannel> {
Err(unsupported("update_app_channel"))
}
async fn delete_app_channel(&self, _app_id: AppId, _channel_id: AppChannelId) -> Result<()> {
Err(unsupported("delete_app_channel"))
}
async fn get_session_context_report(
&self,
_id: SessionId,
) -> Result<everruns_core::SessionContextReport> {
Err(unsupported("get_session_context_report"))
}
async fn delete_session(&self, _id: SessionId) -> Result<()> {
Err(unsupported("delete_session"))
}
async fn list_capabilities(&self, _search: Option<&str>) -> Result<Vec<CapabilityInfo>> {
Err(unsupported("list_capabilities"))
}
}